xref: /reactos/drivers/filesystems/vfatfs/dir.c (revision 542e9f2b)
1 /*
2  * PROJECT:     VFAT Filesystem
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Directory control
5  * COPYRIGHT:   Copyright 1998 Jason Filby <jasonfilby@yahoo.com>
6  *              Copyright 2004-2005 Hervé Poussineau <hpoussin@reactos.org>
7  *              Copyright 2012-2018 Pierre Schweitzer <pierre@reactos.org>
8  */
9 
10 /* INCLUDES *****************************************************************/
11 
12 #include "vfat.h"
13 
14 #define NDEBUG
15 #include <debug.h>
16 
17 /* FUNCTIONS ****************************************************************/
18 
19 /* Function like DosDateTimeToFileTime */
20 BOOLEAN
FsdDosDateTimeToSystemTime(PDEVICE_EXTENSION DeviceExt,USHORT DosDate,USHORT DosTime,PLARGE_INTEGER SystemTime)21 FsdDosDateTimeToSystemTime(
22     PDEVICE_EXTENSION DeviceExt,
23     USHORT DosDate,
24     USHORT DosTime,
25     PLARGE_INTEGER SystemTime)
26 {
27     PDOSTIME pdtime = (PDOSTIME)&DosTime;
28     PDOSDATE pddate = (PDOSDATE)&DosDate;
29     TIME_FIELDS TimeFields;
30     LARGE_INTEGER LocalTime;
31 
32     if (SystemTime == NULL)
33         return FALSE;
34 
35     TimeFields.Milliseconds = 0;
36     TimeFields.Second = pdtime->Second * 2;
37     TimeFields.Minute = pdtime->Minute;
38     TimeFields.Hour = pdtime->Hour;
39 
40     TimeFields.Day = pddate->Day;
41     TimeFields.Month = pddate->Month;
42     TimeFields.Year = (CSHORT)(DeviceExt->BaseDateYear + pddate->Year);
43 
44     RtlTimeFieldsToTime(&TimeFields, &LocalTime);
45     ExLocalTimeToSystemTime(&LocalTime, SystemTime);
46 
47     return TRUE;
48 }
49 
50 /* Function like FileTimeToDosDateTime */
51 BOOLEAN
FsdSystemTimeToDosDateTime(PDEVICE_EXTENSION DeviceExt,PLARGE_INTEGER SystemTime,PUSHORT pDosDate,PUSHORT pDosTime)52 FsdSystemTimeToDosDateTime(
53     PDEVICE_EXTENSION DeviceExt,
54     PLARGE_INTEGER SystemTime,
55     PUSHORT pDosDate,
56     PUSHORT pDosTime)
57 {
58     PDOSTIME pdtime = (PDOSTIME)pDosTime;
59     PDOSDATE pddate = (PDOSDATE)pDosDate;
60     TIME_FIELDS TimeFields;
61     LARGE_INTEGER LocalTime;
62 
63     if (SystemTime == NULL)
64         return FALSE;
65 
66     ExSystemTimeToLocalTime(SystemTime, &LocalTime);
67     RtlTimeToTimeFields(&LocalTime, &TimeFields);
68 
69     if (pdtime)
70     {
71         pdtime->Second = TimeFields.Second / 2;
72         pdtime->Minute = TimeFields.Minute;
73         pdtime->Hour = TimeFields.Hour;
74     }
75 
76     if (pddate)
77     {
78         pddate->Day = TimeFields.Day;
79         pddate->Month = TimeFields.Month;
80         pddate->Year = (USHORT) (TimeFields.Year - DeviceExt->BaseDateYear);
81     }
82 
83     return TRUE;
84 }
85 
86 #define ULONG_ROUND_UP(x)   ROUND_UP((x), (sizeof(ULONG)))
87 
88 static
89 NTSTATUS
VfatGetFileNamesInformation(PVFAT_DIRENTRY_CONTEXT DirContext,PFILE_NAMES_INFORMATION pInfo,ULONG BufferLength,PULONG Written,BOOLEAN First)90 VfatGetFileNamesInformation(
91     PVFAT_DIRENTRY_CONTEXT DirContext,
92     PFILE_NAMES_INFORMATION pInfo,
93     ULONG BufferLength,
94     PULONG Written,
95     BOOLEAN First)
96 {
97     NTSTATUS Status;
98     ULONG BytesToCopy = 0;
99 
100     *Written = 0;
101     Status = STATUS_BUFFER_OVERFLOW;
102 
103     if (FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName) > BufferLength)
104         return Status;
105 
106     if (First || (BufferLength > FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName) + DirContext->LongNameU.Length))
107     {
108         pInfo->FileNameLength = DirContext->LongNameU.Length;
109 
110         *Written = FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName);
111         pInfo->NextEntryOffset = 0;
112         if (BufferLength > FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName))
113         {
114             BytesToCopy = min(DirContext->LongNameU.Length, BufferLength - FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName));
115             RtlCopyMemory(pInfo->FileName,
116                          DirContext->LongNameU.Buffer,
117                          BytesToCopy);
118             *Written += BytesToCopy;
119 
120             if (BytesToCopy == DirContext->LongNameU.Length)
121             {
122                 pInfo->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_NAMES_INFORMATION) +
123                                                         BytesToCopy);
124                 Status = STATUS_SUCCESS;
125             }
126         }
127     }
128 
129     return Status;
130 }
131 
132 static
133 NTSTATUS
VfatGetFileDirectoryInformation(PVFAT_DIRENTRY_CONTEXT DirContext,PDEVICE_EXTENSION DeviceExt,PFILE_DIRECTORY_INFORMATION pInfo,ULONG BufferLength,PULONG Written,BOOLEAN First)134 VfatGetFileDirectoryInformation(
135     PVFAT_DIRENTRY_CONTEXT DirContext,
136     PDEVICE_EXTENSION DeviceExt,
137     PFILE_DIRECTORY_INFORMATION pInfo,
138     ULONG BufferLength,
139     PULONG Written,
140     BOOLEAN First)
141 {
142     NTSTATUS Status;
143     ULONG BytesToCopy = 0;
144 
145     *Written = 0;
146     Status = STATUS_BUFFER_OVERFLOW;
147 
148     if (FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName) > BufferLength)
149         return Status;
150 
151     if (First || (BufferLength > FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName) + DirContext->LongNameU.Length))
152     {
153         pInfo->FileNameLength = DirContext->LongNameU.Length;
154         /* pInfo->FileIndex = ; */
155 
156         *Written = FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName);
157         pInfo->NextEntryOffset = 0;
158         if (BufferLength > FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName))
159         {
160             BytesToCopy = min(DirContext->LongNameU.Length, BufferLength - FIELD_OFFSET(FILE_DIRECTORY_INFORMATION, FileName));
161             RtlCopyMemory(pInfo->FileName,
162                          DirContext->LongNameU.Buffer,
163                          BytesToCopy);
164             *Written += BytesToCopy;
165 
166             if (BytesToCopy == DirContext->LongNameU.Length)
167             {
168                 pInfo->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_DIRECTORY_INFORMATION) +
169                                                         BytesToCopy);
170                 Status = STATUS_SUCCESS;
171             }
172         }
173 
174 
175 
176         if (vfatVolumeIsFatX(DeviceExt))
177         {
178             FsdDosDateTimeToSystemTime(DeviceExt,
179                                        DirContext->DirEntry.FatX.CreationDate,
180                                        DirContext->DirEntry.FatX.CreationTime,
181                                        &pInfo->CreationTime);
182             FsdDosDateTimeToSystemTime(DeviceExt,
183                                        DirContext->DirEntry.FatX.AccessDate,
184                                        DirContext->DirEntry.FatX.AccessTime,
185                                        &pInfo->LastAccessTime);
186             FsdDosDateTimeToSystemTime(DeviceExt,
187                                        DirContext->DirEntry.FatX.UpdateDate,
188                                        DirContext->DirEntry.FatX.UpdateTime,
189                                        &pInfo->LastWriteTime);
190 
191             pInfo->ChangeTime = pInfo->LastWriteTime;
192 
193             if (BooleanFlagOn(DirContext->DirEntry.FatX.Attrib, FILE_ATTRIBUTE_DIRECTORY))
194             {
195                 pInfo->EndOfFile.QuadPart = 0;
196                 pInfo->AllocationSize.QuadPart = 0;
197             }
198             else
199             {
200                 pInfo->EndOfFile.u.HighPart = 0;
201                 pInfo->EndOfFile.u.LowPart = DirContext->DirEntry.FatX.FileSize;
202                 /* Make allocsize a rounded up multiple of BytesPerCluster */
203                 pInfo->AllocationSize.u.HighPart = 0;
204                 pInfo->AllocationSize.u.LowPart = ROUND_UP(DirContext->DirEntry.FatX.FileSize,
205                                                            DeviceExt->FatInfo.BytesPerCluster);
206             }
207 
208             pInfo->FileAttributes = DirContext->DirEntry.FatX.Attrib & 0x3f;
209         }
210         else
211         {
212             FsdDosDateTimeToSystemTime(DeviceExt,
213                                        DirContext->DirEntry.Fat.CreationDate,
214                                        DirContext->DirEntry.Fat.CreationTime,
215                                        &pInfo->CreationTime);
216             FsdDosDateTimeToSystemTime(DeviceExt,
217                                        DirContext->DirEntry.Fat.AccessDate,
218                                        0,
219                                        &pInfo->LastAccessTime);
220             FsdDosDateTimeToSystemTime(DeviceExt,
221                                        DirContext->DirEntry.Fat.UpdateDate,
222                                        DirContext->DirEntry.Fat.UpdateTime,
223                                        &pInfo->LastWriteTime);
224 
225             pInfo->ChangeTime = pInfo->LastWriteTime;
226 
227             if (BooleanFlagOn(DirContext->DirEntry.Fat.Attrib, FILE_ATTRIBUTE_DIRECTORY))
228             {
229                 pInfo->EndOfFile.QuadPart = 0;
230                 pInfo->AllocationSize.QuadPart = 0;
231             }
232             else
233             {
234                 pInfo->EndOfFile.u.HighPart = 0;
235                 pInfo->EndOfFile.u.LowPart = DirContext->DirEntry.Fat.FileSize;
236                 /* Make allocsize a rounded up multiple of BytesPerCluster */
237                 pInfo->AllocationSize.u.HighPart = 0;
238                 pInfo->AllocationSize.u.LowPart = ROUND_UP(DirContext->DirEntry.Fat.FileSize,
239                                                            DeviceExt->FatInfo.BytesPerCluster);
240             }
241 
242             pInfo->FileAttributes = DirContext->DirEntry.Fat.Attrib & 0x3f;
243         }
244     }
245 
246     return Status;
247 }
248 
249 static
250 NTSTATUS
VfatGetFileFullDirectoryInformation(PVFAT_DIRENTRY_CONTEXT DirContext,PDEVICE_EXTENSION DeviceExt,PFILE_FULL_DIR_INFORMATION pInfo,ULONG BufferLength,PULONG Written,BOOLEAN First)251 VfatGetFileFullDirectoryInformation(
252     PVFAT_DIRENTRY_CONTEXT DirContext,
253     PDEVICE_EXTENSION DeviceExt,
254     PFILE_FULL_DIR_INFORMATION pInfo,
255     ULONG BufferLength,
256     PULONG Written,
257     BOOLEAN First)
258 {
259     NTSTATUS Status;
260     ULONG BytesToCopy = 0;
261 
262     *Written = 0;
263     Status = STATUS_BUFFER_OVERFLOW;
264 
265     if (FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName) > BufferLength)
266         return Status;
267 
268     if (First || (BufferLength > FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName) + DirContext->LongNameU.Length))
269     {
270         pInfo->FileNameLength = DirContext->LongNameU.Length;
271         /* pInfo->FileIndex = ; */
272         pInfo->EaSize = 0;
273 
274         *Written = FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName);
275         pInfo->NextEntryOffset = 0;
276         if (BufferLength > FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName))
277         {
278             BytesToCopy = min(DirContext->LongNameU.Length, BufferLength - FIELD_OFFSET(FILE_FULL_DIR_INFORMATION, FileName));
279             RtlCopyMemory(pInfo->FileName,
280                          DirContext->LongNameU.Buffer,
281                          BytesToCopy);
282             *Written += BytesToCopy;
283 
284             if (BytesToCopy == DirContext->LongNameU.Length)
285             {
286                 pInfo->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_FULL_DIR_INFORMATION) +
287                                                         BytesToCopy);
288                 Status = STATUS_SUCCESS;
289             }
290         }
291 
292         if (vfatVolumeIsFatX(DeviceExt))
293         {
294             FsdDosDateTimeToSystemTime(DeviceExt,
295                                        DirContext->DirEntry.FatX.CreationDate,
296                                        DirContext->DirEntry.FatX.CreationTime,
297                                        &pInfo->CreationTime);
298             FsdDosDateTimeToSystemTime(DeviceExt,
299                                        DirContext->DirEntry.FatX.AccessDate,
300                                        DirContext->DirEntry.FatX.AccessTime,
301                                        &pInfo->LastAccessTime);
302             FsdDosDateTimeToSystemTime(DeviceExt,
303                                        DirContext->DirEntry.FatX.UpdateDate,
304                                        DirContext->DirEntry.FatX.UpdateTime,
305                                        &pInfo->LastWriteTime);
306 
307             pInfo->ChangeTime = pInfo->LastWriteTime;
308             pInfo->EndOfFile.u.HighPart = 0;
309             pInfo->EndOfFile.u.LowPart = DirContext->DirEntry.FatX.FileSize;
310             /* Make allocsize a rounded up multiple of BytesPerCluster */
311             pInfo->AllocationSize.u.HighPart = 0;
312             pInfo->AllocationSize.u.LowPart = ROUND_UP(DirContext->DirEntry.FatX.FileSize,
313                                                        DeviceExt->FatInfo.BytesPerCluster);
314             pInfo->FileAttributes = DirContext->DirEntry.FatX.Attrib & 0x3f;
315         }
316         else
317         {
318             FsdDosDateTimeToSystemTime(DeviceExt,
319                                        DirContext->DirEntry.Fat.CreationDate,
320                                        DirContext->DirEntry.Fat.CreationTime,
321                                        &pInfo->CreationTime);
322             FsdDosDateTimeToSystemTime(DeviceExt,
323                                        DirContext->DirEntry.Fat.AccessDate,
324                                        0,
325                                        &pInfo->LastAccessTime);
326             FsdDosDateTimeToSystemTime(DeviceExt,
327                                        DirContext->DirEntry.Fat.UpdateDate,
328                                        DirContext->DirEntry.Fat.UpdateTime,
329                                        &pInfo->LastWriteTime);
330 
331             pInfo->ChangeTime = pInfo->LastWriteTime;
332             pInfo->EndOfFile.u.HighPart = 0;
333             pInfo->EndOfFile.u.LowPart = DirContext->DirEntry.Fat.FileSize;
334             /* Make allocsize a rounded up multiple of BytesPerCluster */
335             pInfo->AllocationSize.u.HighPart = 0;
336             pInfo->AllocationSize.u.LowPart = ROUND_UP(DirContext->DirEntry.Fat.FileSize,
337                                                        DeviceExt->FatInfo.BytesPerCluster);
338             pInfo->FileAttributes = DirContext->DirEntry.Fat.Attrib & 0x3f;
339         }
340     }
341 
342     return Status;
343 }
344 
345 static
346 NTSTATUS
VfatGetFileBothInformation(PVFAT_DIRENTRY_CONTEXT DirContext,PDEVICE_EXTENSION DeviceExt,PFILE_BOTH_DIR_INFORMATION pInfo,ULONG BufferLength,PULONG Written,BOOLEAN First)347 VfatGetFileBothInformation(
348     PVFAT_DIRENTRY_CONTEXT DirContext,
349     PDEVICE_EXTENSION DeviceExt,
350     PFILE_BOTH_DIR_INFORMATION pInfo,
351     ULONG BufferLength,
352     PULONG Written,
353     BOOLEAN First)
354 {
355     NTSTATUS Status;
356     ULONG BytesToCopy = 0;
357 
358     *Written = 0;
359     Status = STATUS_BUFFER_OVERFLOW;
360 
361     if (FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName) > BufferLength)
362         return Status;
363 
364     if (First || (BufferLength > FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName) + DirContext->LongNameU.Length))
365     {
366         pInfo->FileNameLength = DirContext->LongNameU.Length;
367         pInfo->EaSize = 0;
368 
369         *Written = FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName);
370         pInfo->NextEntryOffset = 0;
371         if (BufferLength > FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName))
372         {
373             BytesToCopy = min(DirContext->LongNameU.Length, BufferLength - FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName));
374             RtlCopyMemory(pInfo->FileName,
375                          DirContext->LongNameU.Buffer,
376                          BytesToCopy);
377             *Written += BytesToCopy;
378 
379             if (BytesToCopy == DirContext->LongNameU.Length)
380             {
381                 pInfo->NextEntryOffset = ULONG_ROUND_UP(sizeof(FILE_BOTH_DIR_INFORMATION) +
382                                                         BytesToCopy);
383                 Status = STATUS_SUCCESS;
384             }
385         }
386 
387         if (vfatVolumeIsFatX(DeviceExt))
388         {
389             pInfo->ShortName[0] = 0;
390             pInfo->ShortNameLength = 0;
391             /* pInfo->FileIndex = ; */
392 
393             FsdDosDateTimeToSystemTime(DeviceExt,
394                                        DirContext->DirEntry.FatX.CreationDate,
395                                        DirContext->DirEntry.FatX.CreationTime,
396                                        &pInfo->CreationTime);
397             FsdDosDateTimeToSystemTime(DeviceExt,
398                                        DirContext->DirEntry.FatX.AccessDate,
399                                        DirContext->DirEntry.FatX.AccessTime,
400                                        &pInfo->LastAccessTime);
401             FsdDosDateTimeToSystemTime(DeviceExt,
402                                        DirContext->DirEntry.FatX.UpdateDate,
403                                        DirContext->DirEntry.FatX.UpdateTime,
404                                        &pInfo->LastWriteTime);
405 
406             pInfo->ChangeTime = pInfo->LastWriteTime;
407 
408             if (BooleanFlagOn(DirContext->DirEntry.FatX.Attrib, FILE_ATTRIBUTE_DIRECTORY))
409             {
410                 pInfo->EndOfFile.QuadPart = 0;
411                 pInfo->AllocationSize.QuadPart = 0;
412             }
413             else
414             {
415                 pInfo->EndOfFile.u.HighPart = 0;
416                 pInfo->EndOfFile.u.LowPart = DirContext->DirEntry.FatX.FileSize;
417                 /* Make allocsize a rounded up multiple of BytesPerCluster */
418                 pInfo->AllocationSize.u.HighPart = 0;
419                 pInfo->AllocationSize.u.LowPart = ROUND_UP(DirContext->DirEntry.FatX.FileSize,
420                                                            DeviceExt->FatInfo.BytesPerCluster);
421             }
422 
423             pInfo->FileAttributes = DirContext->DirEntry.FatX.Attrib & 0x3f;
424         }
425         else
426         {
427             pInfo->ShortNameLength = (CCHAR)DirContext->ShortNameU.Length;
428 
429             ASSERT(pInfo->ShortNameLength / sizeof(WCHAR) <= 12);
430             RtlCopyMemory(pInfo->ShortName,
431                           DirContext->ShortNameU.Buffer,
432                           DirContext->ShortNameU.Length);
433 
434             /* pInfo->FileIndex = ; */
435 
436             FsdDosDateTimeToSystemTime(DeviceExt,
437                                        DirContext->DirEntry.Fat.CreationDate,
438                                        DirContext->DirEntry.Fat.CreationTime,
439                                        &pInfo->CreationTime);
440             FsdDosDateTimeToSystemTime(DeviceExt,
441                                        DirContext->DirEntry.Fat.AccessDate,
442                                        0,
443                                        &pInfo->LastAccessTime);
444             FsdDosDateTimeToSystemTime(DeviceExt,
445                                        DirContext->DirEntry.Fat.UpdateDate,
446                                        DirContext->DirEntry.Fat.UpdateTime,
447                                        &pInfo->LastWriteTime);
448 
449             pInfo->ChangeTime = pInfo->LastWriteTime;
450 
451             if (BooleanFlagOn(DirContext->DirEntry.Fat.Attrib, FILE_ATTRIBUTE_DIRECTORY))
452             {
453                 pInfo->EndOfFile.QuadPart = 0;
454                 pInfo->AllocationSize.QuadPart = 0;
455             }
456             else
457             {
458                 pInfo->EndOfFile.u.HighPart = 0;
459                 pInfo->EndOfFile.u.LowPart = DirContext->DirEntry.Fat.FileSize;
460                 /* Make allocsize a rounded up multiple of BytesPerCluster */
461                 pInfo->AllocationSize.u.HighPart = 0;
462                 pInfo->AllocationSize.u.LowPart = ROUND_UP(DirContext->DirEntry.Fat.FileSize, DeviceExt->FatInfo.BytesPerCluster);
463             }
464 
465             pInfo->FileAttributes = DirContext->DirEntry.Fat.Attrib & 0x3f;
466         }
467     }
468 
469     return Status;
470 }
471 
472 static
473 NTSTATUS
DoQuery(PVFAT_IRP_CONTEXT IrpContext)474 DoQuery(
475     PVFAT_IRP_CONTEXT IrpContext)
476 {
477     NTSTATUS Status = STATUS_SUCCESS;
478     LONG BufferLength = 0;
479     PUNICODE_STRING pSearchPattern = NULL;
480     FILE_INFORMATION_CLASS FileInformationClass;
481     PUCHAR Buffer = NULL;
482     PFILE_NAMES_INFORMATION Buffer0 = NULL;
483     PVFATFCB pFcb;
484     PVFATCCB pCcb;
485     BOOLEAN FirstQuery = FALSE;
486     BOOLEAN FirstCall = TRUE;
487     VFAT_DIRENTRY_CONTEXT DirContext;
488     WCHAR LongNameBuffer[LONGNAME_MAX_LENGTH + 1];
489     WCHAR ShortNameBuffer[13];
490     ULONG Written;
491 
492     PIO_STACK_LOCATION Stack = IrpContext->Stack;
493 
494     pCcb = (PVFATCCB)IrpContext->FileObject->FsContext2;
495     pFcb = (PVFATFCB)IrpContext->FileObject->FsContext;
496 
497     /* Determine Buffer for result : */
498     BufferLength = Stack->Parameters.QueryDirectory.Length;
499 #if 0
500     /* Do not probe the user buffer until SEH is available */
501     if (IrpContext->Irp->RequestorMode != KernelMode &&
502         IrpContext->Irp->MdlAddress == NULL &&
503         IrpContext->Irp->UserBuffer != NULL)
504     {
505         ProbeForWrite(IrpContext->Irp->UserBuffer, BufferLength, 1);
506     }
507 #endif
508     Buffer = VfatGetUserBuffer(IrpContext->Irp, FALSE);
509 
510     if (!ExAcquireResourceExclusiveLite(&IrpContext->DeviceExt->DirResource,
511                                         BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
512     {
513         Status = VfatLockUserBuffer(IrpContext->Irp, BufferLength, IoWriteAccess);
514         if (NT_SUCCESS(Status))
515             Status = STATUS_PENDING;
516 
517         return Status;
518     }
519 
520     if (!ExAcquireResourceSharedLite(&pFcb->MainResource,
521                                      BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
522     {
523         ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
524         Status = VfatLockUserBuffer(IrpContext->Irp, BufferLength, IoWriteAccess);
525         if (NT_SUCCESS(Status))
526             Status = STATUS_PENDING;
527 
528         return Status;
529     }
530 
531     /* Obtain the callers parameters */
532     pSearchPattern = (PUNICODE_STRING)Stack->Parameters.QueryDirectory.FileName;
533     FileInformationClass = Stack->Parameters.QueryDirectory.FileInformationClass;
534 
535     /* Allocate search pattern in case:
536      * -> We don't have one already in context
537      * -> We have been given an input pattern
538      * -> The pattern length is not null
539      * -> The pattern buffer is not null
540      * Otherwise, we'll fall later and allocate a match all (*) pattern
541      */
542     if (pSearchPattern &&
543         pSearchPattern->Length != 0 && pSearchPattern->Buffer != NULL)
544     {
545         if (!pCcb->SearchPattern.Buffer)
546         {
547             FirstQuery = TRUE;
548             pCcb->SearchPattern.MaximumLength = pSearchPattern->Length + sizeof(WCHAR);
549             pCcb->SearchPattern.Buffer = ExAllocatePoolWithTag(NonPagedPool,
550                                                                pCcb->SearchPattern.MaximumLength,
551                                                                TAG_SEARCH);
552             if (!pCcb->SearchPattern.Buffer)
553             {
554                 ExReleaseResourceLite(&pFcb->MainResource);
555                 ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
556                 return STATUS_INSUFFICIENT_RESOURCES;
557             }
558             RtlCopyUnicodeString(&pCcb->SearchPattern, pSearchPattern);
559             pCcb->SearchPattern.Buffer[pCcb->SearchPattern.Length / sizeof(WCHAR)] = 0;
560         }
561     }
562     else if (!pCcb->SearchPattern.Buffer)
563     {
564         FirstQuery = TRUE;
565         pCcb->SearchPattern.MaximumLength = 2 * sizeof(WCHAR);
566         pCcb->SearchPattern.Buffer = ExAllocatePoolWithTag(NonPagedPool,
567                                                            2 * sizeof(WCHAR),
568                                                            TAG_SEARCH);
569         if (!pCcb->SearchPattern.Buffer)
570         {
571             ExReleaseResourceLite(&pFcb->MainResource);
572             ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
573             return STATUS_INSUFFICIENT_RESOURCES;
574         }
575         pCcb->SearchPattern.Buffer[0] = L'*';
576         pCcb->SearchPattern.Buffer[1] = 0;
577         pCcb->SearchPattern.Length = sizeof(WCHAR);
578     }
579 
580     if (BooleanFlagOn(IrpContext->Stack->Flags, SL_INDEX_SPECIFIED))
581     {
582         DirContext.DirIndex = pCcb->Entry = Stack->Parameters.QueryDirectory.FileIndex;
583     }
584     else if (FirstQuery || BooleanFlagOn(IrpContext->Stack->Flags, SL_RESTART_SCAN))
585     {
586         DirContext.DirIndex = pCcb->Entry = 0;
587     }
588     else
589     {
590         DirContext.DirIndex = pCcb->Entry;
591     }
592 
593     DPRINT("Buffer=%p tofind=%wZ\n", Buffer, &pCcb->SearchPattern);
594 
595     DirContext.DeviceExt = IrpContext->DeviceExt;
596     DirContext.LongNameU.Buffer = LongNameBuffer;
597     DirContext.LongNameU.MaximumLength = sizeof(LongNameBuffer);
598     DirContext.ShortNameU.Buffer = ShortNameBuffer;
599     DirContext.ShortNameU.MaximumLength = sizeof(ShortNameBuffer);
600 
601     Written = 0;
602     while ((Status == STATUS_SUCCESS) && (BufferLength > 0))
603     {
604         Status = FindFile(IrpContext->DeviceExt,
605                           pFcb,
606                           &pCcb->SearchPattern,
607                           &DirContext,
608                           FirstCall);
609         pCcb->Entry = DirContext.DirIndex;
610 
611         DPRINT("Found %wZ, Status=%x, entry %x\n", &DirContext.LongNameU, Status, pCcb->Entry);
612 
613         FirstCall = FALSE;
614         if (NT_SUCCESS(Status))
615         {
616             switch (FileInformationClass)
617             {
618                 case FileDirectoryInformation:
619                     Status = VfatGetFileDirectoryInformation(&DirContext,
620                                                              IrpContext->DeviceExt,
621                                                              (PFILE_DIRECTORY_INFORMATION)Buffer,
622                                                              BufferLength,
623                                                              &Written,
624                                                              Buffer0 == NULL);
625                     break;
626 
627                 case FileFullDirectoryInformation:
628                     Status = VfatGetFileFullDirectoryInformation(&DirContext,
629                                                                  IrpContext->DeviceExt,
630                                                                  (PFILE_FULL_DIR_INFORMATION)Buffer,
631                                                                  BufferLength,
632                                                                  &Written,
633                                                                  Buffer0 == NULL);
634                     break;
635 
636                 case FileBothDirectoryInformation:
637                     Status = VfatGetFileBothInformation(&DirContext,
638                                                         IrpContext->DeviceExt,
639                                                         (PFILE_BOTH_DIR_INFORMATION)Buffer,
640                                                         BufferLength,
641                                                         &Written,
642                                                         Buffer0 == NULL);
643                     break;
644 
645                 case FileNamesInformation:
646                     Status = VfatGetFileNamesInformation(&DirContext,
647                                                          (PFILE_NAMES_INFORMATION)Buffer,
648                                                          BufferLength,
649                                                          &Written,
650                                                          Buffer0 == NULL);
651                      break;
652 
653                 default:
654                     Status = STATUS_INVALID_INFO_CLASS;
655                     break;
656             }
657 
658             if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_INVALID_INFO_CLASS)
659                 break;
660         }
661         else
662         {
663             Status = (FirstQuery ? STATUS_NO_SUCH_FILE : STATUS_NO_MORE_FILES);
664             break;
665         }
666 
667         Buffer0 = (PFILE_NAMES_INFORMATION) Buffer;
668         Buffer0->FileIndex = DirContext.DirIndex;
669         pCcb->Entry = ++DirContext.DirIndex;
670         BufferLength -= Buffer0->NextEntryOffset;
671 
672         if (BooleanFlagOn(IrpContext->Stack->Flags, SL_RETURN_SINGLE_ENTRY))
673             break;
674 
675         Buffer += Buffer0->NextEntryOffset;
676     }
677 
678     if (Buffer0)
679     {
680         Buffer0->NextEntryOffset = 0;
681         Status = STATUS_SUCCESS;
682         IrpContext->Irp->IoStatus.Information = Stack->Parameters.QueryDirectory.Length - BufferLength;
683     }
684     else
685     {
686         ASSERT(Status != STATUS_SUCCESS || BufferLength == 0);
687         ASSERT(Written <= Stack->Parameters.QueryDirectory.Length);
688         IrpContext->Irp->IoStatus.Information = Written;
689     }
690 
691     ExReleaseResourceLite(&pFcb->MainResource);
692     ExReleaseResourceLite(&IrpContext->DeviceExt->DirResource);
693 
694     return Status;
695 }
696 
VfatNotifyChangeDirectory(PVFAT_IRP_CONTEXT IrpContext)697 NTSTATUS VfatNotifyChangeDirectory(PVFAT_IRP_CONTEXT IrpContext)
698 {
699     PVCB pVcb;
700     PVFATFCB pFcb;
701     PIO_STACK_LOCATION Stack;
702     Stack = IrpContext->Stack;
703     pVcb = IrpContext->DeviceExt;
704     pFcb = (PVFATFCB) IrpContext->FileObject->FsContext;
705 
706     FsRtlNotifyFullChangeDirectory(pVcb->NotifySync,
707                                    &(pVcb->NotifyList),
708                                    IrpContext->FileObject->FsContext2,
709                                    (PSTRING)&(pFcb->PathNameU),
710                                    BooleanFlagOn(Stack->Flags, SL_WATCH_TREE),
711                                    FALSE,
712                                    Stack->Parameters.NotifyDirectory.CompletionFilter,
713                                    IrpContext->Irp,
714                                    NULL,
715                                    NULL);
716 
717     /* We won't handle IRP completion */
718     IrpContext->Flags &= ~IRPCONTEXT_COMPLETE;
719 
720     return STATUS_PENDING;
721 }
722 
723 /*
724  * FUNCTION: directory control : read/write directory informations
725  */
726 NTSTATUS
VfatDirectoryControl(PVFAT_IRP_CONTEXT IrpContext)727 VfatDirectoryControl(
728     PVFAT_IRP_CONTEXT IrpContext)
729 {
730     NTSTATUS Status = STATUS_SUCCESS;
731 
732     IrpContext->Irp->IoStatus.Information = 0;
733 
734     switch (IrpContext->MinorFunction)
735     {
736         case IRP_MN_QUERY_DIRECTORY:
737             Status = DoQuery (IrpContext);
738             break;
739 
740         case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
741             Status = VfatNotifyChangeDirectory(IrpContext);
742             break;
743 
744         default:
745             /* Error */
746             DPRINT("Unexpected minor function %x in VFAT driver\n",
747                    IrpContext->MinorFunction);
748             Status = STATUS_INVALID_DEVICE_REQUEST;
749             break;
750     }
751 
752     if (Status == STATUS_PENDING && BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_COMPLETE))
753     {
754         return VfatMarkIrpContextForQueue(IrpContext);
755     }
756 
757     return Status;
758 }
759 
760 /* EOF */
761