Reworked code for handling of asynchonous i/o requests.
[reactos.git] / reactos / drivers / fs / vfat / dir.c
1 /*
2 * $Id: dir.c,v 1.21 2001/11/02 22:44:34 hbirr Exp $
3 *
4 * COPYRIGHT: See COPYING in the top level directory
5 * PROJECT: ReactOS kernel
6 * FILE: services/fs/vfat/dir.c
7 * PURPOSE: VFAT Filesystem : directory control
8 * UPDATE HISTORY:
9 19-12-1998 : created
10
11 */
12
13 #include <ddk/ntddk.h>
14 #include <wchar.h>
15
16 #define NDEBUG
17 #include <debug.h>
18
19 #include "vfat.h"
20
21
22 // function like DosDateTimeToFileTime
23 BOOL FsdDosDateTimeToFileTime (WORD wDosDate, WORD wDosTime, TIME * FileTime)
24 {
25 PDOSTIME pdtime = (PDOSTIME) & wDosTime;
26 PDOSDATE pddate = (PDOSDATE) & wDosDate;
27 TIME_FIELDS TimeFields;
28
29 if (FileTime == NULL)
30 return FALSE;
31
32 TimeFields.Milliseconds = 0;
33 TimeFields.Second = pdtime->Second * 2;
34 TimeFields.Minute = pdtime->Minute;
35 TimeFields.Hour = pdtime->Hour;
36
37 TimeFields.Day = pddate->Day;
38 TimeFields.Month = pddate->Month;
39 TimeFields.Year = 1980 + pddate->Year;
40
41 RtlTimeFieldsToTime (&TimeFields, (PLARGE_INTEGER) FileTime);
42
43 return TRUE;
44 }
45
46
47 // function like FileTimeToDosDateTime
48 BOOL
49 FsdFileTimeToDosDateTime (TIME * FileTime, WORD * pwDosDate, WORD * pwDosTime)
50 {
51 PDOSTIME pdtime = (PDOSTIME) pwDosTime;
52 PDOSDATE pddate = (PDOSDATE) pwDosDate;
53 TIME_FIELDS TimeFields;
54
55 if (FileTime == NULL)
56 return FALSE;
57
58 RtlTimeToTimeFields ((PLARGE_INTEGER) FileTime, &TimeFields);
59
60 if (pdtime)
61 {
62 pdtime->Second = TimeFields.Second / 2;
63 pdtime->Minute = TimeFields.Minute;
64 pdtime->Hour = TimeFields.Hour;
65 }
66
67 if (pddate)
68 {
69 pddate->Day = TimeFields.Day;
70 pddate->Month = TimeFields.Month;
71 pddate->Year = TimeFields.Year - 1980;
72 }
73
74 return TRUE;
75 }
76
77
78
79 unsigned long
80 vfat_wstrlen (PWSTR s)
81 {
82 WCHAR c = ' ';
83 unsigned int len = 0;
84
85 while (c != 0)
86 {
87 c = *s;
88 s++;
89 len++;
90 };
91 s -= len;
92
93 return len - 1;
94 }
95
96 #define DWORD_ROUND_UP(x) ( (((ULONG)(x))%32) ? ((((ULONG)x)&(~0x1f))+0x20) : ((ULONG)x) )
97
98 NTSTATUS
99 VfatGetFileNameInformation (PVFATFCB pFcb,
100 PFILE_NAMES_INFORMATION pInfo, ULONG BufferLength)
101 {
102 ULONG Length;
103 Length = vfat_wstrlen (pFcb->ObjectName);
104 if ((sizeof (FILE_DIRECTORY_INFORMATION) + Length * sizeof(WCHAR)) > BufferLength)
105 return STATUS_BUFFER_OVERFLOW;
106 pInfo->FileNameLength = Length;
107 pInfo->NextEntryOffset =
108 DWORD_ROUND_UP (sizeof (FILE_DIRECTORY_INFORMATION) + Length * sizeof(WCHAR));
109 memcpy (pInfo->FileName, pFcb->ObjectName,
110 sizeof (WCHAR) * (pInfo->FileNameLength));
111 return STATUS_SUCCESS;
112 }
113
114 NTSTATUS
115 VfatGetFileDirectoryInformation (PVFATFCB pFcb,
116 PDEVICE_EXTENSION DeviceExt,
117 PFILE_DIRECTORY_INFORMATION pInfo,
118 ULONG BufferLength)
119 {
120 unsigned long long AllocSize;
121 ULONG Length;
122 Length = vfat_wstrlen (pFcb->ObjectName);
123 if ((sizeof (FILE_DIRECTORY_INFORMATION) + Length * sizeof(WCHAR)) > BufferLength)
124 return STATUS_BUFFER_OVERFLOW;
125 pInfo->FileNameLength = Length;
126 pInfo->NextEntryOffset =
127 DWORD_ROUND_UP (sizeof (FILE_DIRECTORY_INFORMATION) + Length * sizeof(WCHAR));
128 memcpy (pInfo->FileName, pFcb->ObjectName,
129 sizeof (WCHAR) * (pInfo->FileNameLength));
130 // pInfo->FileIndex=;
131 FsdDosDateTimeToFileTime (pFcb->entry.CreationDate,
132 pFcb->entry.CreationTime, &pInfo->CreationTime);
133 FsdDosDateTimeToFileTime (pFcb->entry.AccessDate, 0,
134 &pInfo->LastAccessTime);
135 FsdDosDateTimeToFileTime (pFcb->entry.UpdateDate, pFcb->entry.UpdateTime,
136 &pInfo->LastWriteTime);
137 FsdDosDateTimeToFileTime (pFcb->entry.UpdateDate, pFcb->entry.UpdateTime,
138 &pInfo->ChangeTime);
139 pInfo->EndOfFile = RtlConvertUlongToLargeInteger (pFcb->entry.FileSize);
140 /* Make allocsize a rounded up multiple of BytesPerCluster */
141 AllocSize = ((pFcb->entry.FileSize + DeviceExt->BytesPerCluster - 1) /
142 DeviceExt->BytesPerCluster) * DeviceExt->BytesPerCluster;
143 pInfo->AllocationSize.QuadPart = AllocSize;
144 pInfo->FileAttributes = pFcb->entry.Attrib;
145
146 return STATUS_SUCCESS;
147 }
148
149 NTSTATUS
150 VfatGetFileFullDirectoryInformation (PVFATFCB pFcb,
151 PDEVICE_EXTENSION DeviceExt,
152 PFILE_FULL_DIRECTORY_INFORMATION pInfo,
153 ULONG BufferLength)
154 {
155 unsigned long long AllocSize;
156 ULONG Length;
157 Length = vfat_wstrlen (pFcb->ObjectName);
158 if ((sizeof (FILE_FULL_DIRECTORY_INFORMATION) + Length * sizeof(WCHAR)) > BufferLength)
159 return STATUS_BUFFER_OVERFLOW;
160 pInfo->FileNameLength = Length;
161 pInfo->NextEntryOffset =
162 DWORD_ROUND_UP (sizeof (FILE_FULL_DIRECTORY_INFORMATION) + Length * sizeof(WCHAR));
163 memcpy (pInfo->FileName, pFcb->ObjectName,
164 sizeof (WCHAR) * (pInfo->FileNameLength));
165 // pInfo->FileIndex=;
166 FsdDosDateTimeToFileTime (pFcb->entry.CreationDate,
167 pFcb->entry.CreationTime, &pInfo->CreationTime);
168 FsdDosDateTimeToFileTime (pFcb->entry.AccessDate, 0,
169 &pInfo->LastAccessTime);
170 FsdDosDateTimeToFileTime (pFcb->entry.UpdateDate, pFcb->entry.UpdateTime,
171 &pInfo->LastWriteTime);
172 FsdDosDateTimeToFileTime (pFcb->entry.UpdateDate, pFcb->entry.UpdateTime,
173 &pInfo->ChangeTime);
174 pInfo->EndOfFile = RtlConvertUlongToLargeInteger (pFcb->entry.FileSize);
175 /* Make allocsize a rounded up multiple of BytesPerCluster */
176 AllocSize = ((pFcb->entry.FileSize + DeviceExt->BytesPerCluster - 1) /
177 DeviceExt->BytesPerCluster) * DeviceExt->BytesPerCluster;
178 pInfo->AllocationSize.QuadPart = AllocSize;
179 pInfo->FileAttributes = pFcb->entry.Attrib;
180 // pInfo->EaSize=;
181 return STATUS_SUCCESS;
182 }
183
184 NTSTATUS
185 VfatGetFileBothInformation (PVFATFCB pFcb,
186 PDEVICE_EXTENSION DeviceExt,
187 PFILE_BOTH_DIRECTORY_INFORMATION pInfo,
188 ULONG BufferLength)
189 {
190 short i;
191 unsigned long long AllocSize;
192 ULONG Length;
193 Length = vfat_wstrlen (pFcb->ObjectName);
194 if ((sizeof (FILE_BOTH_DIRECTORY_INFORMATION) + Length * sizeof(WCHAR)) > BufferLength)
195 return STATUS_BUFFER_OVERFLOW;
196 pInfo->FileNameLength = Length;
197 pInfo->NextEntryOffset =
198 DWORD_ROUND_UP (sizeof (FILE_BOTH_DIRECTORY_INFORMATION) + Length * sizeof(WCHAR));
199 memcpy (pInfo->FileName, pFcb->ObjectName,
200 sizeof (WCHAR) * (pInfo->FileNameLength));
201 // pInfo->FileIndex=;
202 FsdDosDateTimeToFileTime (pFcb->entry.CreationDate,
203 pFcb->entry.CreationTime, &pInfo->CreationTime);
204 FsdDosDateTimeToFileTime (pFcb->entry.AccessDate, 0,
205 &pInfo->LastAccessTime);
206 FsdDosDateTimeToFileTime (pFcb->entry.UpdateDate, pFcb->entry.UpdateTime,
207 &pInfo->LastWriteTime);
208 FsdDosDateTimeToFileTime (pFcb->entry.UpdateDate, pFcb->entry.UpdateTime,
209 &pInfo->ChangeTime);
210 pInfo->EndOfFile = RtlConvertUlongToLargeInteger (pFcb->entry.FileSize);
211 /* Make allocsize a rounded up multiple of BytesPerCluster */
212 AllocSize = ((pFcb->entry.FileSize + DeviceExt->BytesPerCluster - 1) /
213 DeviceExt->BytesPerCluster) * DeviceExt->BytesPerCluster;
214 pInfo->AllocationSize.QuadPart = AllocSize;
215 pInfo->FileAttributes = pFcb->entry.Attrib;
216 // pInfo->EaSize=;
217 for (i = 0; i < 8 && (pFcb->entry.Filename[i] != ' '); i++)
218 pInfo->ShortName[i] = pFcb->entry.Filename[i];
219 pInfo->ShortNameLength = i;
220 pInfo->ShortName[i] = '.';
221 for (i = 0; i < 3 && (pFcb->entry.Ext[i] != ' '); i++)
222 pInfo->ShortName[i + 1 + pInfo->ShortNameLength] = pFcb->entry.Ext[i];
223 if (i)
224 pInfo->ShortNameLength += (i + 1);
225 return STATUS_SUCCESS;
226 }
227
228 NTSTATUS DoQuery (PVFAT_IRP_CONTEXT IrpContext)
229 {
230 NTSTATUS RC = STATUS_SUCCESS;
231 long BufferLength = 0;
232 PUNICODE_STRING pSearchPattern = NULL;
233 FILE_INFORMATION_CLASS FileInformationClass;
234 unsigned long FileIndex = 0;
235 unsigned char *Buffer = NULL;
236 PFILE_NAMES_INFORMATION Buffer0 = NULL;
237 PVFATFCB pFcb;
238 VFATFCB tmpFcb;
239 PVFATCCB pCcb;
240 WCHAR star[5], *pCharPattern;
241 unsigned long OldEntry, OldSector;
242
243 pCcb = (PVFATCCB) IrpContext->FileObject->FsContext2;
244 pFcb = pCcb->pFcb;
245
246 if (!ExAcquireResourceSharedLite(&pFcb->MainResource, IrpContext->Flags & IRPCONTEXT_CANWAIT))
247 {
248 return STATUS_PENDING;
249 }
250
251 // Obtain the callers parameters
252 BufferLength = IrpContext->Stack->Parameters.QueryDirectory.Length;
253 pSearchPattern = IrpContext->Stack->Parameters.QueryDirectory.FileName;
254 FileInformationClass =
255 IrpContext->Stack->Parameters.QueryDirectory.FileInformationClass;
256 FileIndex = IrpContext->Stack->Parameters.QueryDirectory.FileIndex;
257 if (IrpContext->Stack->Flags & SL_RESTART_SCAN)
258 { //FIXME : what is really use of RestartScan ?
259 pCcb->StartEntry = pCcb->StartSector = 0;
260 }
261 // determine Buffer for result :
262 if (IrpContext->Irp->MdlAddress)
263 Buffer = MmGetSystemAddressForMdl (IrpContext->Irp->MdlAddress);
264 else
265 Buffer = IrpContext->Irp->UserBuffer;
266 DPRINT ("Buffer=%x tofind=%S\n", Buffer, pSearchPattern->Buffer);
267 if (pSearchPattern == NULL)
268 {
269 star[0] = '*';
270 star[1] = 0;
271 pCharPattern = star;
272 }
273 else
274 pCharPattern = pSearchPattern->Buffer;
275 tmpFcb.ObjectName = tmpFcb.PathName;
276 while (RC == STATUS_SUCCESS && BufferLength > 0)
277 {
278 OldSector = pCcb->StartSector;
279 OldEntry = pCcb->StartEntry;
280 if (OldSector)
281 pCcb->StartEntry++;
282 RC =
283 FindFile (IrpContext->DeviceExt, &tmpFcb, pFcb, pCharPattern, &pCcb->StartEntry, NULL);
284 pCcb->StartSector = 1;
285 DPRINT ("Found %S,RC=%x, sector %x entry %x\n", tmpFcb.ObjectName, RC,
286 pCcb->StartSector, pCcb->StartEntry);
287 if (NT_SUCCESS (RC))
288 {
289 switch (FileInformationClass)
290 {
291 case FileNameInformation:
292 RC =
293 VfatGetFileNameInformation (&tmpFcb,
294 (PFILE_NAMES_INFORMATION) Buffer,
295 BufferLength);
296 break;
297 case FileDirectoryInformation:
298 RC =
299 VfatGetFileDirectoryInformation (&tmpFcb, IrpContext->DeviceExt,
300 (PFILE_DIRECTORY_INFORMATION)
301 Buffer, BufferLength);
302 break;
303 case FileFullDirectoryInformation:
304 RC =
305 VfatGetFileFullDirectoryInformation (&tmpFcb, IrpContext->DeviceExt,
306 (PFILE_FULL_DIRECTORY_INFORMATION)
307 Buffer, BufferLength);
308 break;
309 case FileBothDirectoryInformation:
310 RC =
311 VfatGetFileBothInformation (&tmpFcb, IrpContext->DeviceExt,
312 (PFILE_BOTH_DIRECTORY_INFORMATION)
313 Buffer, BufferLength);
314 break;
315 default:
316 RC = STATUS_INVALID_INFO_CLASS;
317 }
318 }
319 else
320 {
321 if (Buffer0)
322 Buffer0->NextEntryOffset = 0;
323 break;
324 }
325 if (RC == STATUS_BUFFER_OVERFLOW)
326 {
327 if (Buffer0)
328 Buffer0->NextEntryOffset = 0;
329 pCcb->StartSector = OldSector;
330 pCcb->StartEntry = OldEntry;
331 break;
332 }
333 Buffer0 = (PFILE_NAMES_INFORMATION) Buffer;
334 Buffer0->FileIndex = FileIndex++;
335 if (IrpContext->Stack->Flags & SL_RETURN_SINGLE_ENTRY)
336 break;
337 BufferLength -= Buffer0->NextEntryOffset;
338 Buffer += Buffer0->NextEntryOffset;
339 }
340 if (Buffer0)
341 Buffer0->NextEntryOffset = 0;
342 if (FileIndex > 0)
343 RC = STATUS_SUCCESS;
344
345 if (IrpContext->Flags & IRPCONTEXT_CANWAIT)
346 {
347 ExReleaseResourceLite(&pFcb->MainResource);
348 }
349
350 return RC;
351 }
352
353
354 NTSTATUS VfatDirectoryControl (PVFAT_IRP_CONTEXT IrpContext)
355 /*
356 * FUNCTION: directory control : read/write directory informations
357 */
358 {
359 NTSTATUS RC = STATUS_SUCCESS;
360 CHECKPOINT;
361 switch (IrpContext->MinorFunction)
362 {
363 case IRP_MN_QUERY_DIRECTORY:
364 RC = DoQuery (IrpContext);
365 break;
366 case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
367 DPRINT (" vfat, dir : change\n");
368 RC = STATUS_NOT_IMPLEMENTED;
369 break;
370 default:
371 // error
372 DbgPrint ("unexpected minor function %x in VFAT driver\n",
373 IrpContext->MinorFunction);
374 RC = STATUS_INVALID_DEVICE_REQUEST;
375 break;
376 }
377 if (RC == STATUS_PENDING)
378 {
379 RC = VfatQueueRequest(IrpContext);
380 }
381 else
382 {
383 IrpContext->Irp->IoStatus.Status = RC;
384 IrpContext->Irp->IoStatus.Information = 0;
385 IoCompleteRequest (IrpContext->Irp, IO_NO_INCREMENT);
386 VfatFreeIrpContext(IrpContext);
387 }
388 return RC;
389 }
390
391