1 /*++
2 
3 Copyright (c) 1989-2000 Microsoft Corporation
4 
5 Module Name:
6 
7     DirCtrl.c
8 
9 Abstract:
10 
11     This module implements the File Directory Control routines for Fat called
12     by the dispatch driver.
13 
14 
15 --*/
16 
17 #include "fatprocs.h"
18 
19 //
20 //  The Bug check file id for this module
21 //
22 
23 #define BugCheckFileId                   (FAT_BUG_CHECK_DIRCTRL)
24 
25 //
26 //  The local debug trace level
27 //
28 
29 #define Dbg                              (DEBUG_TRACE_DIRCTRL)
30 
31 WCHAR Fat8QMdot3QM[12] = { DOS_QM, DOS_QM, DOS_QM, DOS_QM, DOS_QM, DOS_QM, DOS_QM, DOS_QM,
32                            L'.', DOS_QM, DOS_QM, DOS_QM};
33 
34 //
35 //  Local procedure prototypes
36 //
37 
38 _Requires_lock_held_(_Global_critical_region_)
39 NTSTATUS
40 FatQueryDirectory (
41     IN PIRP_CONTEXT IrpContext,
42     IN PIRP Irp
43     );
44 
45 VOID
46 FatGetDirTimes(
47     PIRP_CONTEXT IrpContext,
48     PDIRENT Dirent,
49     PFILE_DIRECTORY_INFORMATION DirInfo
50     );
51 
52 _Requires_lock_held_(_Global_critical_region_)
53 NTSTATUS
54 FatNotifyChangeDirectory (
55     IN PIRP_CONTEXT IrpContext,
56     IN PIRP Irp
57     );
58 
59 #ifdef ALLOC_PRAGMA
60 #pragma alloc_text(PAGE, FatCommonDirectoryControl)
61 #pragma alloc_text(PAGE, FatFsdDirectoryControl)
62 #pragma alloc_text(PAGE, FatNotifyChangeDirectory)
63 #pragma alloc_text(PAGE, FatQueryDirectory)
64 #pragma alloc_text(PAGE, FatGetDirTimes)
65 
66 #endif
67 
68 
69 _Function_class_(IRP_MJ_DIRECTORY_CONTROL)
70 _Function_class_(DRIVER_DISPATCH)
71 NTSTATUS
72 NTAPI
73 FatFsdDirectoryControl (
74     _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
75     _Inout_ PIRP Irp
76     )
77 
78 /*++
79 
80 Routine Description:
81 
82     This routine implements the FSD part of directory control
83 
84 Arguments:
85 
86     VolumeDeviceObject - Supplies the volume device object where the
87         file exists
88 
89     Irp - Supplies the Irp being processed
90 
91 Return Value:
92 
93     NTSTATUS - The FSD status for the IRP
94 
95 --*/
96 
97 {
98     NTSTATUS Status;
99     PIRP_CONTEXT IrpContext = NULL;
100 
101     BOOLEAN TopLevel;
102 
103     PAGED_CODE();
104 
105     DebugTrace(+1, Dbg, "FatFsdDirectoryControl\n", 0);
106 
107     //
108     //  Call the common directory Control routine, with blocking allowed if
109     //  synchronous
110     //
111 
112     FsRtlEnterFileSystem();
113 
114     TopLevel = FatIsIrpTopLevel( Irp );
115 
116     _SEH2_TRY {
117 
118         IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );
119 
120         Status = FatCommonDirectoryControl( IrpContext, Irp );
121 
122     } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
123 
124         //
125         //  We had some trouble trying to perform the requested
126         //  operation, so we'll abort the I/O request with
127         //  the error status that we get back from the
128         //  execption code
129         //
130 
131         Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() );
132     } _SEH2_END;
133 
134     if (TopLevel) { IoSetTopLevelIrp( NULL ); }
135 
136     FsRtlExitFileSystem();
137 
138     //
139     //  And return to our caller
140     //
141 
142     DebugTrace(-1, Dbg, "FatFsdDirectoryControl -> %08lx\n", Status);
143 
144     UNREFERENCED_PARAMETER( VolumeDeviceObject );
145 
146     return Status;
147 }
148 
149 
150 _Requires_lock_held_(_Global_critical_region_)
151 NTSTATUS
152 FatCommonDirectoryControl (
153     IN PIRP_CONTEXT IrpContext,
154     IN PIRP Irp
155     )
156 
157 /*++
158 
159 Routine Description:
160 
161     This is the common routine for doing directory control operations called
162     by both the fsd and fsp threads
163 
164 Arguments:
165 
166     Irp - Supplies the Irp to process
167 
168 Return Value:
169 
170     NTSTATUS - The return status for the operation
171 
172 --*/
173 
174 {
175     NTSTATUS Status;
176     PIO_STACK_LOCATION IrpSp;
177 
178     PAGED_CODE();
179 
180     //
181     //  Get a pointer to the current Irp stack location
182     //
183 
184     IrpSp = IoGetCurrentIrpStackLocation( Irp );
185 
186     DebugTrace(+1, Dbg, "FatCommonDirectoryControl\n", 0);
187     DebugTrace( 0, Dbg, "Irp           = %p\n", Irp );
188     DebugTrace( 0, Dbg, "MinorFunction = %08lx\n", IrpSp->MinorFunction );
189 
190     //
191     //  We know this is a directory control so we'll case on the
192     //  minor function, and call a internal worker routine to complete
193     //  the irp.
194     //
195 
196     switch ( IrpSp->MinorFunction ) {
197 
198     case IRP_MN_QUERY_DIRECTORY:
199 
200         Status = FatQueryDirectory( IrpContext, Irp );
201         break;
202 
203     case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
204 
205         Status = FatNotifyChangeDirectory( IrpContext, Irp );
206         break;
207 
208     default:
209 
210         DebugTrace(0, Dbg, "Invalid Directory Control Minor Function %08lx\n", IrpSp->MinorFunction);
211 
212         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST );
213         Status = STATUS_INVALID_DEVICE_REQUEST;
214         break;
215     }
216 
217     DebugTrace(-1, Dbg, "FatCommonDirectoryControl -> %08lx\n", Status);
218 
219     return Status;
220 }
221 
222 
223 //
224 //  Local Support Routine
225 //
226 
227 _Requires_lock_held_(_Global_critical_region_)
228 NTSTATUS
229 FatQueryDirectory (
230     IN PIRP_CONTEXT IrpContext,
231     IN PIRP Irp
232     )
233 
234 /*++
235 
236 Routine Description:
237 
238     This routine performs the query directory operation.  It is responsible
239     for either completing of enqueuing the input Irp.
240 
241 Arguments:
242 
243     Irp - Supplies the Irp to process
244 
245 Return Value:
246 
247     NTSTATUS - The return status for the operation
248 
249 --*/
250 
251 {
252     NTSTATUS Status;
253     PIO_STACK_LOCATION IrpSp;
254 
255     PVCB Vcb;
256     PDCB Dcb;
257     PCCB Ccb;
258     PBCB Bcb;
259 
260     ULONG i;
261     PUCHAR Buffer;
262     CLONG UserBufferLength;
263 
264     PUNICODE_STRING UniArgFileName;
265     WCHAR LongFileNameBuffer[ FAT_CREATE_INITIAL_NAME_BUF_SIZE];
266     UNICODE_STRING LongFileName;
267     UNICODE_STRING OrigFileName;
268     FILE_INFORMATION_CLASS FileInformationClass;
269     ULONG FileIndex;
270     ULONG MatchFlags = 0;
271     BOOLEAN RestartScan;
272     BOOLEAN ReturnSingleEntry;
273     BOOLEAN IndexSpecified;
274 
275     BOOLEAN InitialQuery;
276     VBO CurrentVbo = 0;
277     BOOLEAN UpdateCcb;
278     PDIRENT Dirent;
279     UCHAR Fat8Dot3Buffer[12];
280     OEM_STRING Fat8Dot3String;
281     ULONG DiskAllocSize;
282 
283     ULONG NextEntry;
284     ULONG LastEntry;
285 
286     PFILE_DIRECTORY_INFORMATION DirInfo;
287     PFILE_FULL_DIR_INFORMATION FullDirInfo;
288     PFILE_BOTH_DIR_INFORMATION BothDirInfo;
289     PFILE_ID_FULL_DIR_INFORMATION IdFullDirInfo;
290     PFILE_ID_BOTH_DIR_INFORMATION IdBothDirInfo;
291     PFILE_NAMES_INFORMATION NamesInfo;
292 
293 
294     PAGED_CODE();
295 
296     //
297     //  Get the current Stack location
298     //
299 
300     IrpSp = IoGetCurrentIrpStackLocation( Irp );
301 
302     //
303     //  Display the input values.
304     //
305     DebugTrace(+1, Dbg, "FatQueryDirectory...\n", 0);
306     DebugTrace( 0, Dbg, " Wait                   = %08lx\n", FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT));
307     DebugTrace( 0, Dbg, " Irp                    = %p\n", Irp);
308     DebugTrace( 0, Dbg, " ->Length               = %08lx\n", IrpSp->Parameters.QueryDirectory.Length);
309     DebugTrace( 0, Dbg, " ->FileName             = %wZ\n", IrpSp->Parameters.QueryDirectory.FileName);
310     DebugTrace( 0, Dbg, " ->FileInformationClass = %08lx\n", IrpSp->Parameters.QueryDirectory.FileInformationClass);
311     DebugTrace( 0, Dbg, " ->FileIndex            = %08lx\n", IrpSp->Parameters.QueryDirectory.FileIndex);
312     DebugTrace( 0, Dbg, " ->UserBuffer           = %p\n", Irp->AssociatedIrp.SystemBuffer);
313     DebugTrace( 0, Dbg, " ->RestartScan          = %08lx\n", FlagOn( IrpSp->Flags, SL_RESTART_SCAN ));
314     DebugTrace( 0, Dbg, " ->ReturnSingleEntry    = %08lx\n", FlagOn( IrpSp->Flags, SL_RETURN_SINGLE_ENTRY ));
315     DebugTrace( 0, Dbg, " ->IndexSpecified       = %08lx\n", FlagOn( IrpSp->Flags, SL_INDEX_SPECIFIED ));
316 
317     //
318     //  Reference our input parameters to make things easier
319     //
320 
321     UserBufferLength = IrpSp->Parameters.QueryDirectory.Length;
322 
323     FileInformationClass = IrpSp->Parameters.QueryDirectory.FileInformationClass;
324     FileIndex = IrpSp->Parameters.QueryDirectory.FileIndex;
325 
326     UniArgFileName = IrpSp->Parameters.QueryDirectory.FileName;
327 
328     RestartScan       = BooleanFlagOn(IrpSp->Flags, SL_RESTART_SCAN);
329     ReturnSingleEntry = BooleanFlagOn(IrpSp->Flags, SL_RETURN_SINGLE_ENTRY);
330     IndexSpecified    = BooleanFlagOn(IrpSp->Flags, SL_INDEX_SPECIFIED);
331 
332     //
333     //  Check on the type of open.  We return invalid parameter for all
334     //  but UserDirectoryOpens.  Also check that the filename is a valid
335     //  UNICODE string.
336     //
337 
338     if (FatDecodeFileObject( IrpSp->FileObject,
339                              &Vcb,
340                              &Dcb,
341                              &Ccb) != UserDirectoryOpen ||
342         (UniArgFileName &&
343          UniArgFileName->Length % sizeof(WCHAR))) {
344 
345         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
346         DebugTrace(-1, Dbg, "FatQueryDirectory -> STATUS_INVALID_PARAMETER\n", 0);
347 
348         return STATUS_INVALID_PARAMETER;
349     }
350 
351     //
352     //  Initialize the local variables.
353     //
354 
355     Bcb = NULL;
356     UpdateCcb = TRUE;
357     Dirent = NULL;
358 
359     Fat8Dot3String.MaximumLength = 12;
360     Fat8Dot3String.Buffer = (PCHAR)Fat8Dot3Buffer;
361 
362     LongFileName.Length = 0;
363     LongFileName.MaximumLength = sizeof( LongFileNameBuffer);
364     LongFileName.Buffer = LongFileNameBuffer;
365 
366     InitialQuery = (BOOLEAN)((Ccb->UnicodeQueryTemplate.Buffer == NULL) &&
367                              !FlagOn(Ccb->Flags, CCB_FLAG_MATCH_ALL));
368     Status = STATUS_SUCCESS;
369     Irp->IoStatus.Information = 0;
370 
371     DiskAllocSize = 1 << Vcb->AllocationSupport.LogOfBytesPerCluster;
372 
373     //
374     //  If this is the initial query, then grab exclusive access in
375     //  order to update the search string in the Ccb.  We may
376     //  discover that we are not the initial query once we grab the Fcb
377     //  and downgrade our status.
378     //
379     //  If restartscan is set, we may be replacing the query template,
380     //  so take the FCB exclusive to protect against multiple people
381     //  changing the CCB at once.
382     //
383 
384     if (InitialQuery || RestartScan) {
385 
386         if (!FatAcquireExclusiveFcb( IrpContext, Dcb )) {
387 
388             DebugTrace(0, Dbg, "FatQueryDirectory -> Enqueue to Fsp\n", 0);
389             Status = FatFsdPostRequest( IrpContext, Irp );
390             DebugTrace(-1, Dbg, "FatQueryDirectory -> %08lx\n", Status);
391 
392             return Status;
393         }
394 
395         if (!RestartScan && (Ccb->UnicodeQueryTemplate.Buffer != NULL)) {
396 
397             InitialQuery = FALSE;
398 
399             FatConvertToSharedFcb( IrpContext, Dcb );
400         }
401 
402     } else {
403 
404         if (!FatAcquireSharedFcb( IrpContext, Dcb )) {
405 
406             DebugTrace(0, Dbg, "FatQueryDirectory -> Enqueue to Fsp\n", 0);
407             Status = FatFsdPostRequest( IrpContext, Irp );
408             DebugTrace(-1, Dbg, "FatQueryDirectory -> %08lx\n", Status);
409 
410             return Status;
411 
412         }
413     }
414 
415     _SEH2_TRY {
416 
417         ULONG BaseLength;
418         ULONG BytesConverted;
419 
420         //
421         // If we are in the Fsp now because we had to wait earlier,
422         // we must map the user buffer, otherwise we can use the
423         // user's buffer directly.
424         //
425 
426         Buffer = FatMapUserBuffer( IrpContext, Irp );
427 
428         //
429         //  Make sure the Dcb is still good.
430         //
431 
432         FatVerifyFcb( IrpContext, Dcb );
433 
434         //
435         //  Determine where to start the scan.  Highest priority is given
436         //  to the file index.  Lower priority is the restart flag.  If
437         //  neither of these is specified, then the Vbo offset field in the
438         //  Ccb is used.
439         //
440 
441         if (IndexSpecified) {
442 
443             CurrentVbo = FileIndex + sizeof( DIRENT );
444 
445         } else if (RestartScan) {
446 
447             CurrentVbo = 0;
448             Ccb->OffsetToStartSearchFrom = 0;
449 
450         } else {
451 
452             CurrentVbo = Ccb->OffsetToStartSearchFrom;
453         }
454 
455         //
456         //  If this is the first try then allocate a buffer for the file
457         //  name.
458         //
459 
460         if (InitialQuery ||
461             (RestartScan && UniArgFileName != NULL && UniArgFileName->Length != 0)) {
462 
463             //
464             //  If we're restarting the scan, clear out the pattern in the Ccb and regenerate it,
465             //
466 
467             if (RestartScan) {
468 
469                 if (Ccb->UnicodeQueryTemplate.Buffer) {
470 
471                     if (FlagOn(Ccb->Flags, CCB_FLAG_FREE_UNICODE)) {
472 
473                         ExFreePoolWithTag(Ccb->UnicodeQueryTemplate.Buffer, TAG_FILENAME_BUFFER);
474                         ClearFlag(Ccb->Flags, CCB_FLAG_FREE_UNICODE);
475                     }
476 
477                     Ccb->UnicodeQueryTemplate.Buffer = NULL;
478                     Ccb->UnicodeQueryTemplate.Length = 0;
479                     Ccb->UnicodeQueryTemplate.MaximumLength = 0;
480                 }
481 
482                 if (Ccb->OemQueryTemplate.Wild.Buffer) {
483 
484                     if (FlagOn(Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT)) {
485 
486                         RtlFreeOemString( &Ccb->OemQueryTemplate.Wild );
487                         ClearFlag(Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT);
488                     }
489 
490                     Ccb->OemQueryTemplate.Wild.Buffer = NULL;
491                     Ccb->OemQueryTemplate.Wild.Length = 0;
492                     Ccb->OemQueryTemplate.Wild.MaximumLength = 0;
493                 }
494 
495                 Ccb->ContainsWildCards = FALSE;
496                 ClearFlag(Ccb->Flags, CCB_FLAG_MATCH_ALL);
497                 ClearFlag(Ccb->Flags, CCB_FLAG_FREE_UNICODE);
498                 ClearFlag(Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE);
499                 ClearFlag(Ccb->Flags, CCB_FLAG_QUERY_TEMPLATE_MIXED);
500 
501             }
502 
503             //
504             //  If either:
505             //
506             //  - No name was specified
507             //  - An empty name was specified
508             //  - We received a '*'
509             //  - The user specified the DOS equivolent of ????????.???
510             //
511             //  then match all names.
512             //
513 
514             if ((UniArgFileName == NULL) ||
515                 (UniArgFileName->Length == 0) ||
516                 (UniArgFileName->Buffer == NULL) ||
517                 ((UniArgFileName->Length == sizeof(WCHAR)) &&
518                  (UniArgFileName->Buffer[0] == L'*')) ||
519                 ((UniArgFileName->Length == 12*sizeof(WCHAR)) &&
520                  (RtlEqualMemory( UniArgFileName->Buffer,
521                                   Fat8QMdot3QM,
522                                   12*sizeof(WCHAR) )))) {
523 
524                 Ccb->ContainsWildCards = TRUE;
525 
526                 SetFlag( Ccb->Flags, CCB_FLAG_MATCH_ALL );
527 
528             } else {
529 
530                 BOOLEAN ExtendedName = FALSE;
531                 OEM_STRING LocalBestFit;
532 
533                 //
534                 //  First and formost, see if the name has wild cards.
535                 //
536 
537                 Ccb->ContainsWildCards =
538                     FsRtlDoesNameContainWildCards( UniArgFileName );
539 
540                 //
541                 //  Now check to see if the name contains any extended
542                 //  characters
543                 //
544 
545                 for (i=0; i < UniArgFileName->Length / sizeof(WCHAR); i++) {
546 
547                     if (UniArgFileName->Buffer[i] >= 0x80) {
548 
549                         ExtendedName = TRUE;
550                         break;
551                     }
552                 }
553 
554                 //
555                 //  OK, now do the conversions we need.
556                 //
557 
558                 if (ExtendedName) {
559 
560                     Status = RtlUpcaseUnicodeString( &Ccb->UnicodeQueryTemplate,
561                                                      UniArgFileName,
562                                                      TRUE );
563 
564                     if (!NT_SUCCESS(Status)) {
565 
566                         try_return( Status );
567                     }
568 
569                     SetFlag( Ccb->Flags, CCB_FLAG_FREE_UNICODE );
570 
571                     //
572                     //  Upcase the name and convert it to the Oem code page.
573                     //
574 
575                     Status = RtlUpcaseUnicodeStringToCountedOemString( &LocalBestFit,
576                                                                        UniArgFileName,
577                                                                        TRUE );
578 
579                     //
580                     //  If this conversion failed for any reason other than
581                     //  an unmappable character fail the request.
582                     //
583 
584                     if (!NT_SUCCESS(Status)) {
585 
586                         if (Status == STATUS_UNMAPPABLE_CHARACTER) {
587 
588                             SetFlag( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE );
589 
590                         } else {
591 
592                             try_return( Status );
593                         }
594 
595                     } else {
596 
597                         SetFlag( Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT );
598                     }
599 
600                 } else {
601 
602                     PVOID Buffers;
603 
604                     //
605                     //  This case is optimized because I know I only have to
606                     //  worry about a-z.
607                     //
608 
609                     Buffers = FsRtlAllocatePoolWithTag( PagedPool,
610                                                         UniArgFileName->Length +
611                                                         UniArgFileName->Length / sizeof(WCHAR),
612                                                         TAG_FILENAME_BUFFER );
613 
614                     Ccb->UnicodeQueryTemplate.Buffer = Buffers;
615                     Ccb->UnicodeQueryTemplate.Length = UniArgFileName->Length;
616                     Ccb->UnicodeQueryTemplate.MaximumLength = UniArgFileName->Length;
617 
618                     LocalBestFit.Buffer = (PCHAR)Buffers + UniArgFileName->Length;
619                     LocalBestFit.Length = UniArgFileName->Length / sizeof(WCHAR);
620                     LocalBestFit.MaximumLength = LocalBestFit.Length;
621 
622                     SetFlag( Ccb->Flags, CCB_FLAG_FREE_UNICODE );
623 
624                     for (i=0; i < UniArgFileName->Length / sizeof(WCHAR); i++) {
625 
626                         WCHAR c = UniArgFileName->Buffer[i];
627 
628                         LocalBestFit.Buffer[i] = (UCHAR)
629                         (Ccb->UnicodeQueryTemplate.Buffer[i] =
630                              (c < 'a' ? c : c <= 'z' ? c - ('a' - 'A') : c));
631                     }
632                 }
633 
634                 //
635                 //  At this point we now have the upcased unicode name,
636                 //  and the two Oem names if they could be represented in
637                 //  this code page.
638                 //
639                 //  Now determine if the Oem names are legal for what we
640                 //  going to try and do.  Mark them as not usable is they
641                 //  are not legal.  Note that we can optimize extended names
642                 //  since they are actually both the same string.
643                 //
644 
645                 if (!FlagOn( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE ) &&
646                     !FatIsNameShortOemValid( IrpContext,
647                                              LocalBestFit,
648                                              Ccb->ContainsWildCards,
649                                              FALSE,
650                                              FALSE )) {
651 
652                     if (ExtendedName) {
653 
654                         RtlFreeOemString( &LocalBestFit );
655                         ClearFlag( Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT );
656                     }
657 
658                     SetFlag( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE );
659                 }
660 
661                 //
662                 //  OK, now both locals oem strings correctly reflect their
663                 //  usability.  Now we want to load up the Ccb structure.
664                 //
665                 //  Now we will branch on two paths of wheather the name
666                 //  is wild or not.
667                 //
668 
669                 if (!FlagOn( Ccb->Flags, CCB_FLAG_SKIP_SHORT_NAME_COMPARE )) {
670 
671                     if (Ccb->ContainsWildCards) {
672 
673                         Ccb->OemQueryTemplate.Wild = LocalBestFit;
674 
675                     } else {
676 
677                         FatStringTo8dot3( IrpContext,
678                                           LocalBestFit,
679                                           &Ccb->OemQueryTemplate.Constant );
680 
681                         if (FlagOn(Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT)) {
682 
683                             RtlFreeOemString( &LocalBestFit );
684                             ClearFlag( Ccb->Flags, CCB_FLAG_FREE_OEM_BEST_FIT );
685                         }
686                     }
687                 }
688             }
689 
690             //
691             //  We convert to shared access.
692             //
693 
694             FatConvertToSharedFcb( IrpContext, Dcb );
695         }
696 
697         LastEntry = 0;
698         NextEntry = 0;
699 
700         switch (FileInformationClass) {
701 
702         case FileDirectoryInformation:
703 
704             BaseLength = FIELD_OFFSET( FILE_DIRECTORY_INFORMATION,
705                                        FileName[0] );
706             break;
707 
708         case FileFullDirectoryInformation:
709 
710             BaseLength = FIELD_OFFSET( FILE_FULL_DIR_INFORMATION,
711                                        FileName[0] );
712             break;
713 
714         case FileIdFullDirectoryInformation:
715 
716             BaseLength = FIELD_OFFSET( FILE_ID_FULL_DIR_INFORMATION,
717                                        FileName[0] );
718             break;
719 
720         case FileNamesInformation:
721 
722             BaseLength = FIELD_OFFSET( FILE_NAMES_INFORMATION,
723                                        FileName[0] );
724             break;
725 
726         case FileBothDirectoryInformation:
727 
728             BaseLength = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION,
729                                        FileName[0] );
730             break;
731 
732         case FileIdBothDirectoryInformation:
733 
734             BaseLength = FIELD_OFFSET( FILE_ID_BOTH_DIR_INFORMATION,
735                                        FileName[0] );
736             break;
737 
738         default:
739 
740             try_return( Status = STATUS_INVALID_INFO_CLASS );
741         }
742 
743         //
744         //  At this point we are about to enter our query loop.  We have
745         //  determined the index into the directory file to begin the
746         //  search.  LastEntry and NextEntry are used to index into the user
747         //  buffer.  LastEntry is the last entry we've added, NextEntry is
748         //  current one we're working on.  If NextEntry is non-zero, then
749         //  at least one entry was added.
750         //
751 
752         while ( TRUE ) {
753 
754             VBO NextVbo;
755             ULONG FileNameLength;
756             ULONG BytesRemainingInBuffer;
757             BOOLEAN FileNameDos;
758 
759             DebugTrace(0, Dbg, "FatQueryDirectory -> Top of loop\n", 0);
760 
761             //
762             //  If the user had requested only a single match and we have
763             //  returned that, then we stop at this point.
764             //
765 
766             if (ReturnSingleEntry && NextEntry != 0) {
767 
768                 try_return( Status );
769             }
770 
771 
772             //
773             //  We call FatLocateDirent to lock down the next matching dirent.
774             //
775 
776             FatLocateDirent( IrpContext,
777                              Dcb,
778                              Ccb,
779                              CurrentVbo,
780                              &MatchFlags,
781                              &Dirent,
782                              &Bcb,
783                              &NextVbo,
784                              &FileNameDos,
785                              &LongFileName,
786                              &OrigFileName );
787 
788             //
789             //  If we didn't receive a dirent, then we are at the end of the
790             //  directory.  If we have returned any files, we exit with
791             //  success, otherwise we return STATUS_NO_MORE_FILES.
792             //
793 
794             if (!Dirent) {
795 
796                 DebugTrace(0, Dbg, "FatQueryDirectory -> No dirent\n", 0);
797 
798                 if (NextEntry == 0) {
799 
800                     UpdateCcb = FALSE;
801 
802                     if (InitialQuery) {
803 
804                         Status = STATUS_NO_SUCH_FILE;
805 
806                     } else {
807 
808                         Status = STATUS_NO_MORE_FILES;
809                     }
810                 }
811 
812                 try_return( Status );
813             }
814 
815 
816             //
817             //  Protect access to the user buffer with an exception handler.
818             //  Since (at our request) IO doesn't buffer these requests, we have
819             //  to guard against a user messing with the page protection and other
820             //  such trickery.
821             //
822 
823             _SEH2_TRY {
824 
825                 Fat8dot3ToString( IrpContext, Dirent, TRUE, &Fat8Dot3String );
826 
827 
828                 if (LongFileName.Length == 0) {
829 
830                     //
831                     //  Now we have an entry to return to our caller.  We'll convert
832                     //  the name from the form in the dirent to a <name>.<ext> form.
833                     //  We'll case on the type of information requested and fill up
834                     //  the user buffer if everything fits.
835                     //
836 
837                     //
838                     //  Determine the UNICODE length of the file name.
839                     //
840 
841                     FileNameLength = RtlOemStringToCountedUnicodeSize(&Fat8Dot3String);
842 
843                     //
844                     //  Here are the rules concerning filling up the buffer:
845                     //
846                     //  1.  The Io system garentees that there will always be
847                     //      enough room for at least one base record.
848                     //
849                     //  2.  If the full first record (including file name) cannot
850                     //      fit, as much of the name as possible is copied and
851                     //      STATUS_BUFFER_OVERFLOW is returned.
852                     //
853                     //  3.  If a subsequent record cannot completely fit into the
854                     //      buffer, none of it (as in 0 bytes) is copied, and
855                     //      STATUS_SUCCESS is returned.  A subsequent query will
856                     //      pick up with this record.
857                     //
858 
859                     BytesRemainingInBuffer = UserBufferLength - NextEntry;
860 
861                     if ( (NextEntry != 0) &&
862                          ( (BaseLength + FileNameLength > BytesRemainingInBuffer) ||
863                            (UserBufferLength < NextEntry) ) ) {
864 
865                         DebugTrace(0, Dbg, "Next entry won't fit\n", 0);
866 
867                         try_return( Status = STATUS_SUCCESS );
868                     }
869 
870                     NT_ASSERT( BytesRemainingInBuffer >= BaseLength );
871 
872                     //
873                     //  Zero the base part of the structure.
874                     //
875 
876                     RtlZeroMemory( &Buffer[NextEntry], BaseLength );
877 
878                     switch ( FileInformationClass ) {
879 
880                     //
881                     //  Now fill the base parts of the strucure that are applicable.
882                     //
883 
884                     case FileBothDirectoryInformation:
885                     case FileFullDirectoryInformation:
886                     case FileIdBothDirectoryInformation:
887                     case FileIdFullDirectoryInformation:
888 
889                         DebugTrace(0, Dbg, "FatQueryDirectory -> Getting file full directory information\n", 0);
890 
891                         //
892                         //  Get the Ea file length.
893                         //
894 
895                         FullDirInfo = (PFILE_FULL_DIR_INFORMATION)&Buffer[NextEntry];
896 
897                         //
898                         //  If the EAs are corrupt, ignore the error.  We don't want
899                         //  to abort the directory query.
900                         //
901 
902                         _SEH2_TRY {
903 
904                             FatGetEaLength( IrpContext,
905                                             Vcb,
906                                             Dirent,
907                                             &FullDirInfo->EaSize );
908 
909                         } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
910 
911                               FatResetExceptionState( IrpContext );
912                               FullDirInfo->EaSize = 0;
913                         } _SEH2_END;
914 
915                     case FileDirectoryInformation:
916 
917                         DirInfo = (PFILE_DIRECTORY_INFORMATION)&Buffer[NextEntry];
918 
919                         FatGetDirTimes( IrpContext, Dirent, DirInfo );
920 
921                         DirInfo->EndOfFile.QuadPart = Dirent->FileSize;
922 
923                         if (!FlagOn( Dirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY )) {
924 
925 
926                             DirInfo->AllocationSize.QuadPart =
927                                (((Dirent->FileSize + DiskAllocSize - 1) / DiskAllocSize) *
928                                 DiskAllocSize );
929                         }
930 
931                         if (Dirent->Attributes != 0) {
932                             DirInfo->FileAttributes = Dirent->Attributes;
933 
934 
935                         } else {
936 
937                             DirInfo->FileAttributes = 0;
938 
939                                 DirInfo->FileAttributes |= FILE_ATTRIBUTE_NORMAL;
940                         }
941 
942                         DirInfo->FileIndex = NextVbo;
943 
944                         DirInfo->FileNameLength = FileNameLength;
945 
946                         DebugTrace(0, Dbg, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String);
947 
948                         break;
949 
950                     case FileNamesInformation:
951 
952                         DebugTrace(0, Dbg, "FatQueryDirectory -> Getting file names information\n", 0);
953 
954                         NamesInfo = (PFILE_NAMES_INFORMATION)&Buffer[NextEntry];
955 
956                         NamesInfo->FileIndex = NextVbo;
957 
958                         NamesInfo->FileNameLength = FileNameLength;
959 
960                         DebugTrace(0, Dbg, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String );
961 
962                         break;
963 
964                     default:
965 
966 #ifdef _MSC_VER
967 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
968 #endif
969                         FatBugCheck( FileInformationClass, 0, 0 );
970                     }
971 
972                     BytesConverted = 0;
973 
974                     Status = RtlOemToUnicodeN( (PWCH)&Buffer[NextEntry + BaseLength],
975                                                BytesRemainingInBuffer - BaseLength,
976                                                &BytesConverted,
977                                                Fat8Dot3String.Buffer,
978                                                Fat8Dot3String.Length );
979 
980                     //
981                     //  Check for the case that a single entry doesn't fit.
982                     //  This should only get this far on the first entry
983                     //
984 
985                     if (BytesConverted < FileNameLength) {
986 
987                         NT_ASSERT( NextEntry == 0 );
988                         Status = STATUS_BUFFER_OVERFLOW;
989                     }
990 
991                     //
992                     //  Set up the previous next entry offset
993                     //
994 
995                     *((PULONG)(&Buffer[LastEntry])) = NextEntry - LastEntry;
996 
997                     //
998                     //  And indicate how much of the user buffer we have currently
999                     //  used up.  We must compute this value before we long align
1000                     //  ourselves for the next entry
1001                     //
1002 
1003                     Irp->IoStatus.Information = QuadAlign( Irp->IoStatus.Information ) +
1004                                                 BaseLength + BytesConverted;
1005 
1006                     //
1007                     //  If something happened with the conversion, bail here.
1008                     //
1009 
1010                     if ( !NT_SUCCESS( Status ) ) {
1011 
1012                         try_return( NOTHING );
1013                     }
1014 
1015                 } else {
1016 
1017                     ULONG ShortNameLength;
1018 
1019                     FileNameLength = LongFileName.Length;
1020 
1021                     //
1022                     //  Here are the rules concerning filling up the buffer:
1023                     //
1024                     //  1.  The Io system garentees that there will always be
1025                     //      enough room for at least one base record.
1026                     //
1027                     //  2.  If the full first record (including file name) cannot
1028                     //      fit, as much of the name as possible is copied and
1029                     //      STATUS_BUFFER_OVERFLOW is returned.
1030                     //
1031                     //  3.  If a subsequent record cannot completely fit into the
1032                     //      buffer, none of it (as in 0 bytes) is copied, and
1033                     //      STATUS_SUCCESS is returned.  A subsequent query will
1034                     //      pick up with this record.
1035                     //
1036 
1037                     BytesRemainingInBuffer = UserBufferLength - NextEntry;
1038 
1039                     if ( (NextEntry != 0) &&
1040                          ( (BaseLength + FileNameLength > BytesRemainingInBuffer) ||
1041                            (UserBufferLength < NextEntry) ) ) {
1042 
1043                         DebugTrace(0, Dbg, "Next entry won't fit\n", 0);
1044 
1045                         try_return( Status = STATUS_SUCCESS );
1046                     }
1047 
1048                     NT_ASSERT( BytesRemainingInBuffer >= BaseLength );
1049 
1050                     //
1051                     //  Zero the base part of the structure.
1052                     //
1053 
1054                     RtlZeroMemory( &Buffer[NextEntry], BaseLength );
1055 
1056                     switch ( FileInformationClass ) {
1057 
1058                     //
1059                     //  Now fill the base parts of the strucure that are applicable.
1060                     //
1061 
1062                     case FileBothDirectoryInformation:
1063                     case FileIdBothDirectoryInformation:
1064 
1065                         BothDirInfo = (PFILE_BOTH_DIR_INFORMATION)&Buffer[NextEntry];
1066 
1067                         //
1068                         //  Now we have an entry to return to our caller.  We'll convert
1069                         //  the name from the form in the dirent to a <name>.<ext> form.
1070                         //  We'll case on the type of information requested and fill up
1071                         //  the user buffer if everything fits.
1072                         //
1073 
1074                         Fat8dot3ToString( IrpContext, Dirent, FALSE, &Fat8Dot3String );
1075 
1076                         NT_ASSERT( Fat8Dot3String.Length <= 12 );
1077 
1078                         Status = RtlOemToUnicodeN( &BothDirInfo->ShortName[0],
1079                                                    12*sizeof(WCHAR),
1080                                                    &ShortNameLength,
1081                                                    Fat8Dot3String.Buffer,
1082                                                    Fat8Dot3String.Length );
1083 
1084                         NT_ASSERT( Status != STATUS_BUFFER_OVERFLOW );
1085                         NT_ASSERT( ShortNameLength <= 12*sizeof(WCHAR) );
1086 
1087                         //
1088                         //  Copy the length into the dirinfo structure.  Note
1089                         //  that the LHS below is a USHORT, so it can not
1090                         //  be specificed as the OUT parameter above.
1091                         //
1092 
1093                         BothDirInfo->ShortNameLength = (UCHAR)ShortNameLength;
1094 
1095                         //
1096                         //  If something happened with the conversion, bail here.
1097                         //
1098 
1099                         if ( !NT_SUCCESS( Status ) ) {
1100 
1101                             try_return( NOTHING );
1102                         }
1103 
1104                     case FileFullDirectoryInformation:
1105                     case FileIdFullDirectoryInformation:
1106 
1107                         DebugTrace(0, Dbg, "FatQueryDirectory -> Getting file full directory information\n", 0);
1108 
1109                         //
1110                         //  Get the Ea file length.
1111                         //
1112 
1113                         FullDirInfo = (PFILE_FULL_DIR_INFORMATION)&Buffer[NextEntry];
1114 
1115                         //
1116                         //  If the EAs are corrupt, ignore the error.  We don't want
1117                         //  to abort the directory query.
1118                         //
1119 
1120                         _SEH2_TRY {
1121 
1122                             FatGetEaLength( IrpContext,
1123                                             Vcb,
1124                                             Dirent,
1125                                             &FullDirInfo->EaSize );
1126 
1127                         } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
1128 
1129                               FatResetExceptionState( IrpContext );
1130                               FullDirInfo->EaSize = 0;
1131                         } _SEH2_END;
1132 
1133                     case FileDirectoryInformation:
1134 
1135                         DirInfo = (PFILE_DIRECTORY_INFORMATION)&Buffer[NextEntry];
1136 
1137                         FatGetDirTimes( IrpContext, Dirent, DirInfo );
1138 
1139                         DirInfo->EndOfFile.QuadPart = Dirent->FileSize;
1140 
1141                         if (!FlagOn( Dirent->Attributes, FAT_DIRENT_ATTR_DIRECTORY )) {
1142 
1143 
1144                             DirInfo->AllocationSize.QuadPart = (
1145                                                             (( Dirent->FileSize
1146                                                                + DiskAllocSize - 1 )
1147                                                              / DiskAllocSize )
1148                                                             * DiskAllocSize );
1149                         }
1150 
1151                         if (Dirent->Attributes != 0) {
1152                             DirInfo->FileAttributes = Dirent->Attributes;
1153 
1154 
1155                         } else {
1156 
1157                             DirInfo->FileAttributes = 0;
1158 
1159 
1160                                 DirInfo->FileAttributes |= FILE_ATTRIBUTE_NORMAL;
1161                             }
1162 
1163 
1164                         DirInfo->FileIndex = NextVbo;
1165 
1166                         DirInfo->FileNameLength = FileNameLength;
1167 
1168                         DebugTrace(0, Dbg, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String);
1169 
1170                         break;
1171 
1172                     case FileNamesInformation:
1173 
1174                         DebugTrace(0, Dbg, "FatQueryDirectory -> Getting file names information\n", 0);
1175 
1176                         NamesInfo = (PFILE_NAMES_INFORMATION)&Buffer[NextEntry];
1177 
1178                         NamesInfo->FileIndex = NextVbo;
1179 
1180                         NamesInfo->FileNameLength = FileNameLength;
1181 
1182                         DebugTrace(0, Dbg, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String );
1183 
1184                         break;
1185 
1186                     default:
1187 
1188 #ifdef _MSC_VER
1189 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
1190 #endif
1191                         FatBugCheck( FileInformationClass, 0, 0 );
1192                     }
1193 
1194                     BytesConverted = BytesRemainingInBuffer - BaseLength >= FileNameLength ?
1195                                      FileNameLength :
1196                                      BytesRemainingInBuffer - BaseLength;
1197 
1198                     RtlCopyMemory( &Buffer[NextEntry + BaseLength],
1199                                    &LongFileName.Buffer[0],
1200                                    BytesConverted );
1201 
1202                     //
1203                     //  Set up the previous next entry offset
1204                     //
1205 
1206                     *((PULONG)(&Buffer[LastEntry])) = NextEntry - LastEntry;
1207 
1208                     //
1209                     //  And indicate how much of the user buffer we have currently
1210                     //  used up.  We must compute this value before we long align
1211                     //  ourselves for the next entry
1212                     //
1213 
1214                     Irp->IoStatus.Information = QuadAlign( Irp->IoStatus.Information ) +
1215                                                 BaseLength + BytesConverted;
1216 
1217                     //
1218                     //  Check for the case that a single entry doesn't fit.
1219                     //  This should only get this far on the first entry.
1220                     //
1221 
1222                     if (BytesConverted < FileNameLength) {
1223 
1224                         NT_ASSERT( NextEntry == 0 );
1225 
1226                         try_return( Status = STATUS_BUFFER_OVERFLOW );
1227                     }
1228                 }
1229 
1230                 //
1231                 //  Finish up by filling in the FileId
1232                 //
1233 
1234                 switch ( FileInformationClass ) {
1235 
1236                 case FileIdBothDirectoryInformation:
1237 
1238                     IdBothDirInfo = (PFILE_ID_BOTH_DIR_INFORMATION)&Buffer[NextEntry];
1239                     IdBothDirInfo->FileId.QuadPart = FatGenerateFileIdFromDirentAndOffset( Dcb, Dirent, NextVbo );
1240                     break;
1241 
1242                 case FileIdFullDirectoryInformation:
1243 
1244                     IdFullDirInfo = (PFILE_ID_FULL_DIR_INFORMATION)&Buffer[NextEntry];
1245                     IdFullDirInfo->FileId.QuadPart = FatGenerateFileIdFromDirentAndOffset( Dcb, Dirent, NextVbo );
1246                     break;
1247 
1248                 default:
1249                     break;
1250                 }
1251 
1252             }  _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
1253 
1254                   //
1255                   //  We had a problem filling in the user's buffer, so stop and
1256                   //  fail this request.  This is the only reason any exception
1257                   //  would have occured at this level.
1258                   //
1259 
1260                   Irp->IoStatus.Information = 0;
1261                   UpdateCcb = FALSE;
1262                   try_return( Status = _SEH2_GetExceptionCode());
1263             } _SEH2_END;
1264 
1265             //
1266             //  Set ourselves up for the next iteration
1267             //
1268 
1269             LastEntry = NextEntry;
1270             NextEntry += (ULONG)QuadAlign(BaseLength + BytesConverted);
1271 
1272             CurrentVbo = NextVbo + sizeof( DIRENT );
1273         }
1274 
1275     try_exit: NOTHING;
1276     } _SEH2_FINALLY {
1277 
1278         DebugUnwind( FatQueryDirectory );
1279 
1280         FatReleaseFcb( IrpContext, Dcb );
1281 
1282         //
1283         //  Unpin data in cache if still held.
1284         //
1285 
1286         FatUnpinBcb( IrpContext, Bcb );
1287 
1288         //
1289         //  Free any dynamically allocated string buffer
1290         //
1291 
1292         FatFreeStringBuffer( &LongFileName);
1293 
1294         //
1295         //  Perform any cleanup.  If this is the first query, then store
1296         //  the filename in the Ccb if successful.  Also update the
1297         //  VBO index for the next search.  This is done by transferring
1298         //  from shared access to exclusive access and copying the
1299         //  data from the local copies.
1300         //
1301 
1302         if (!_SEH2_AbnormalTermination()) {
1303 
1304             if (UpdateCcb) {
1305 
1306                 //
1307                 //  Store the most recent VBO to use as a starting point for
1308                 //  the next search.
1309                 //
1310 
1311                 Ccb->OffsetToStartSearchFrom = CurrentVbo;
1312             }
1313 
1314             FatCompleteRequest( IrpContext, Irp, Status );
1315         }
1316 
1317         DebugTrace(-1, Dbg, "FatQueryDirectory -> %08lx\n", Status);
1318 
1319     } _SEH2_END;
1320 
1321     return Status;
1322 }
1323 
1324 
1325 //
1326 //  Local Support Routine
1327 //
1328 
1329 VOID
1330 FatGetDirTimes(
1331     PIRP_CONTEXT IrpContext,
1332     PDIRENT Dirent,
1333     PFILE_DIRECTORY_INFORMATION DirInfo
1334     )
1335 
1336 /*++
1337 
1338 Routine Description:
1339 
1340     This routine pulls the date/time information from a dirent and fills
1341     in the DirInfo structure.
1342 
1343 Arguments:
1344 
1345     Dirent - Supplies the dirent
1346     DirInfo - Supplies the target structure
1347 
1348 Return Value:
1349 
1350     VOID
1351 
1352 --*/
1353 
1354 
1355 {
1356     PAGED_CODE();
1357 
1358     //
1359     //  Start with the Last Write Time.
1360     //
1361 
1362     DirInfo->LastWriteTime =
1363         FatFatTimeToNtTime( IrpContext,
1364                             Dirent->LastWriteTime,
1365                             0 );
1366 
1367     //
1368     //  These fields are only non-zero when in Chicago mode.
1369     //
1370 
1371     if (FatData.ChicagoMode) {
1372 
1373         //
1374         //  Do a quick check here for Creation and LastAccess
1375         //  times that are the same as the LastWriteTime.
1376         //
1377 
1378         if (*((UNALIGNED LONG *)&Dirent->CreationTime) ==
1379             *((UNALIGNED LONG *)&Dirent->LastWriteTime)) {
1380 
1381             DirInfo->CreationTime.QuadPart =
1382 
1383                 DirInfo->LastWriteTime.QuadPart +
1384                 Dirent->CreationMSec * 10 * 1000 * 10;
1385 
1386         } else {
1387 
1388             //
1389             //  Only do the really hard work if this field is non-zero.
1390             //
1391 
1392             if (((PUSHORT)Dirent)[8] != 0) {
1393 
1394                 DirInfo->CreationTime =
1395                     FatFatTimeToNtTime( IrpContext,
1396                                         Dirent->CreationTime,
1397                                         Dirent->CreationMSec );
1398 
1399             } else {
1400 
1401                 ExLocalTimeToSystemTime( &FatJanOne1980,
1402                                          &DirInfo->CreationTime );
1403             }
1404         }
1405 
1406         //
1407         //  Do a quick check for LastAccessDate.
1408         //
1409 
1410         if (*((PUSHORT)&Dirent->LastAccessDate) ==
1411             *((PUSHORT)&Dirent->LastWriteTime.Date)) {
1412 
1413             PFAT_TIME WriteTime;
1414 
1415             WriteTime = &Dirent->LastWriteTime.Time;
1416 
1417             DirInfo->LastAccessTime.QuadPart =
1418                 DirInfo->LastWriteTime.QuadPart -
1419                 UInt32x32To64(((WriteTime->DoubleSeconds * 2) +
1420                                (WriteTime->Minute * 60) +
1421                                (WriteTime->Hour * 60 * 60)),
1422                               1000 * 1000 * 10);
1423 
1424         } else {
1425 
1426             //
1427             //  Only do the really hard work if this field is non-zero.
1428             //
1429 
1430             if (((PUSHORT)Dirent)[9] != 0) {
1431 
1432                 DirInfo->LastAccessTime =
1433                     FatFatDateToNtTime( IrpContext,
1434                                         Dirent->LastAccessDate );
1435 
1436             } else {
1437 
1438                 ExLocalTimeToSystemTime( &FatJanOne1980,
1439                                          &DirInfo->LastAccessTime );
1440             }
1441         }
1442     }
1443 }
1444 
1445 
1446 //
1447 //  Local Support Routine
1448 //
1449 
1450 _Requires_lock_held_(_Global_critical_region_)
1451 NTSTATUS
1452 FatNotifyChangeDirectory (
1453     IN PIRP_CONTEXT IrpContext,
1454     IN PIRP Irp
1455     )
1456 
1457 /*++
1458 
1459 Routine Description:
1460 
1461     This routine performs the notify change directory operation.  It is
1462     responsible for either completing of enqueuing the input Irp.
1463 
1464 Arguments:
1465 
1466     Irp - Supplies the Irp to process
1467 
1468 Return Value:
1469 
1470     NTSTATUS - The return status for the operation
1471 
1472 --*/
1473 
1474 {
1475     NTSTATUS Status = STATUS_SUCCESS;
1476     PIO_STACK_LOCATION IrpSp;
1477     PVCB Vcb;
1478     PDCB Dcb;
1479     PCCB Ccb;
1480     ULONG CompletionFilter;
1481     BOOLEAN WatchTree;
1482 
1483     BOOLEAN CompleteRequest;
1484 
1485     PAGED_CODE();
1486 
1487     //
1488     //  Get the current Stack location
1489     //
1490 
1491     IrpSp = IoGetCurrentIrpStackLocation( Irp );
1492 
1493     DebugTrace(+1, Dbg, "FatNotifyChangeDirectory...\n", 0);
1494     DebugTrace( 0, Dbg, " Wait               = %08lx\n", FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT));
1495     DebugTrace( 0, Dbg, " Irp                = %p\n", Irp);
1496     DebugTrace( 0, Dbg, " ->CompletionFilter = %08lx\n", IrpSp->Parameters.NotifyDirectory.CompletionFilter);
1497 
1498     //
1499     //  Always set the wait flag in the Irp context for the original request.
1500     //
1501 
1502     SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
1503 
1504     //
1505     //  Assume we don't complete request.
1506     //
1507 
1508     CompleteRequest = FALSE;
1509 
1510     //
1511     //  Check on the type of open.  We return invalid parameter for all
1512     //  but UserDirectoryOpens.
1513     //
1514 
1515     if (FatDecodeFileObject( IrpSp->FileObject,
1516                              &Vcb,
1517                              &Dcb,
1518                              &Ccb ) != UserDirectoryOpen) {
1519 
1520         FatCompleteRequest( IrpContext, Irp, STATUS_INVALID_PARAMETER );
1521         DebugTrace(-1, Dbg, "FatQueryDirectory -> STATUS_INVALID_PARAMETER\n", 0);
1522 
1523         return STATUS_INVALID_PARAMETER;
1524 
1525     }
1526 
1527     //
1528     //  Reference our input parameter to make things easier
1529     //
1530 
1531     CompletionFilter = IrpSp->Parameters.NotifyDirectory.CompletionFilter;
1532     WatchTree = BooleanFlagOn( IrpSp->Flags, SL_WATCH_TREE );
1533 
1534     //
1535     //  Try to acquire exclusive access to the Dcb and enqueue the Irp to the
1536     //  Fsp if we didn't get access
1537     //
1538 
1539     if (!FatAcquireExclusiveFcb( IrpContext, Dcb )) {
1540 
1541         DebugTrace(0, Dbg, "FatNotifyChangeDirectory -> Cannot Acquire Fcb\n", 0);
1542 
1543         Status = FatFsdPostRequest( IrpContext, Irp );
1544 
1545         DebugTrace(-1, Dbg, "FatNotifyChangeDirectory -> %08lx\n", Status);
1546         return Status;
1547     }
1548 
1549     _SEH2_TRY {
1550 
1551         //
1552         //  Make sure the Fcb is still good
1553         //
1554 
1555         FatVerifyFcb( IrpContext, Dcb );
1556 
1557         //
1558         //  We need the full name.
1559         //
1560 
1561         FatSetFullFileNameInFcb( IrpContext, Dcb );
1562 
1563         //
1564         //  If the file is marked as DELETE_PENDING then complete this
1565         //  request immediately.
1566         //
1567 
1568         if (FlagOn( Dcb->FcbState, FCB_STATE_DELETE_ON_CLOSE )) {
1569 
1570             FatRaiseStatus( IrpContext, STATUS_DELETE_PENDING );
1571         }
1572 
1573         //
1574         //  Call the Fsrtl package to process the request.
1575         //
1576 
1577         FsRtlNotifyFullChangeDirectory( Vcb->NotifySync,
1578                                         &Vcb->DirNotifyList,
1579                                         Ccb,
1580                                         (PSTRING)&Dcb->FullFileName,
1581                                         WatchTree,
1582                                         FALSE,
1583                                         CompletionFilter,
1584                                         Irp,
1585                                         NULL,
1586                                         NULL );
1587 
1588         Status = STATUS_PENDING;
1589 
1590         CompleteRequest = TRUE;
1591 
1592     } _SEH2_FINALLY {
1593 
1594         DebugUnwind( FatNotifyChangeDirectory );
1595 
1596         FatReleaseFcb( IrpContext, Dcb );
1597 
1598         //
1599         //  If the dir notify package is holding the Irp, we discard the
1600         //  the IrpContext.
1601         //
1602 
1603         if (CompleteRequest) {
1604 
1605             FatCompleteRequest( IrpContext, FatNull, 0 );
1606         }
1607 
1608         DebugTrace(-1, Dbg, "FatNotifyChangeDirectory -> %08lx\n", Status);
1609     } _SEH2_END;
1610 
1611     return Status;
1612 }
1613 
1614