[FASTFAT] Fix size checking in VfatGetFileNameInformation()
[reactos.git] / drivers / filesystems / fastfat_new / dirctrl.c
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 \f
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 \f
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 \f
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 \f
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 \f
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