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