3 Copyright (c) 1989-2000 Microsoft Corporation
11 This module implements the File Directory Control routines for Fat called
12 by the dispatch driver.
20 // The Bug check file id for this module
23 #define BugCheckFileId (FAT_BUG_CHECK_DIRCTRL)
26 // The local debug trace level
29 #define Dbg (DEBUG_TRACE_DIRCTRL)
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
};
35 // Local procedure prototypes
38 _Requires_lock_held_(_Global_critical_region_
)
41 IN PIRP_CONTEXT IrpContext
,
47 PIRP_CONTEXT IrpContext
,
49 PFILE_DIRECTORY_INFORMATION DirInfo
52 _Requires_lock_held_(_Global_critical_region_
)
54 FatNotifyChangeDirectory (
55 IN PIRP_CONTEXT IrpContext
,
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)
69 _Function_class_(IRP_MJ_DIRECTORY_CONTROL
)
70 _Function_class_(DRIVER_DISPATCH
)
73 FatFsdDirectoryControl (
74 _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject
,
82 This routine implements the FSD part of directory control
86 VolumeDeviceObject - Supplies the volume device object where the
89 Irp - Supplies the Irp being processed
93 NTSTATUS - The FSD status for the IRP
99 PIRP_CONTEXT IrpContext
= NULL
;
105 DebugTrace(+1, Dbg
, "FatFsdDirectoryControl\n", 0);
108 // Call the common directory Control routine, with blocking allowed if
112 FsRtlEnterFileSystem();
114 TopLevel
= FatIsIrpTopLevel( Irp
);
118 IrpContext
= FatCreateIrpContext( Irp
, CanFsdWait( Irp
) );
120 Status
= FatCommonDirectoryControl( IrpContext
, Irp
);
122 } _SEH2_EXCEPT(FatExceptionFilter( IrpContext
, _SEH2_GetExceptionInformation() )) {
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
131 Status
= FatProcessException( IrpContext
, Irp
, _SEH2_GetExceptionCode() );
134 if (TopLevel
) { IoSetTopLevelIrp( NULL
); }
136 FsRtlExitFileSystem();
139 // And return to our caller
142 DebugTrace(-1, Dbg
, "FatFsdDirectoryControl -> %08lx\n", Status
);
144 UNREFERENCED_PARAMETER( VolumeDeviceObject
);
150 _Requires_lock_held_(_Global_critical_region_
)
152 FatCommonDirectoryControl (
153 IN PIRP_CONTEXT IrpContext
,
161 This is the common routine for doing directory control operations called
162 by both the fsd and fsp threads
166 Irp - Supplies the Irp to process
170 NTSTATUS - The return status for the operation
176 PIO_STACK_LOCATION IrpSp
;
181 // Get a pointer to the current Irp stack location
184 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
186 DebugTrace(+1, Dbg
, "FatCommonDirectoryControl\n", 0);
187 DebugTrace( 0, Dbg
, "Irp = %p\n", Irp
);
188 DebugTrace( 0, Dbg
, "MinorFunction = %08lx\n", IrpSp
->MinorFunction
);
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
196 switch ( IrpSp
->MinorFunction
) {
198 case IRP_MN_QUERY_DIRECTORY
:
200 Status
= FatQueryDirectory( IrpContext
, Irp
);
203 case IRP_MN_NOTIFY_CHANGE_DIRECTORY
:
205 Status
= FatNotifyChangeDirectory( IrpContext
, Irp
);
210 DebugTrace(0, Dbg
, "Invalid Directory Control Minor Function %08lx\n", IrpSp
->MinorFunction
);
212 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_DEVICE_REQUEST
);
213 Status
= STATUS_INVALID_DEVICE_REQUEST
;
217 DebugTrace(-1, Dbg
, "FatCommonDirectoryControl -> %08lx\n", Status
);
224 // Local Support Routine
227 _Requires_lock_held_(_Global_critical_region_
)
230 IN PIRP_CONTEXT IrpContext
,
238 This routine performs the query directory operation. It is responsible
239 for either completing of enqueuing the input Irp.
243 Irp - Supplies the Irp to process
247 NTSTATUS - The return status for the operation
253 PIO_STACK_LOCATION IrpSp
;
262 CLONG UserBufferLength
;
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
;
270 ULONG MatchFlags
= 0;
272 BOOLEAN ReturnSingleEntry
;
273 BOOLEAN IndexSpecified
;
275 BOOLEAN InitialQuery
;
279 UCHAR Fat8Dot3Buffer
[12];
280 OEM_STRING Fat8Dot3String
;
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
;
297 // Get the current Stack location
300 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
303 // Display the input values.
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
));
318 // Reference our input parameters to make things easier
321 UserBufferLength
= IrpSp
->Parameters
.QueryDirectory
.Length
;
323 FileInformationClass
= IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
;
324 FileIndex
= IrpSp
->Parameters
.QueryDirectory
.FileIndex
;
326 UniArgFileName
= IrpSp
->Parameters
.QueryDirectory
.FileName
;
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
);
333 // Check on the type of open. We return invalid parameter for all
334 // but UserDirectoryOpens. Also check that the filename is a valid
338 if (FatDecodeFileObject( IrpSp
->FileObject
,
341 &Ccb
) != UserDirectoryOpen
||
343 UniArgFileName
->Length
% sizeof(WCHAR
))) {
345 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
346 DebugTrace(-1, Dbg
, "FatQueryDirectory -> STATUS_INVALID_PARAMETER\n", 0);
348 return STATUS_INVALID_PARAMETER
;
352 // Initialize the local variables.
359 Fat8Dot3String
.MaximumLength
= 12;
360 Fat8Dot3String
.Buffer
= (PCHAR
)Fat8Dot3Buffer
;
362 LongFileName
.Length
= 0;
363 LongFileName
.MaximumLength
= sizeof( LongFileNameBuffer
);
364 LongFileName
.Buffer
= LongFileNameBuffer
;
366 InitialQuery
= (BOOLEAN
)((Ccb
->UnicodeQueryTemplate
.Buffer
== NULL
) &&
367 !FlagOn(Ccb
->Flags
, CCB_FLAG_MATCH_ALL
));
368 Status
= STATUS_SUCCESS
;
369 Irp
->IoStatus
.Information
= 0;
371 DiskAllocSize
= 1 << Vcb
->AllocationSupport
.LogOfBytesPerCluster
;
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.
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.
384 if (InitialQuery
|| RestartScan
) {
386 if (!FatAcquireExclusiveFcb( IrpContext
, Dcb
)) {
388 DebugTrace(0, Dbg
, "FatQueryDirectory -> Enqueue to Fsp\n", 0);
389 Status
= FatFsdPostRequest( IrpContext
, Irp
);
390 DebugTrace(-1, Dbg
, "FatQueryDirectory -> %08lx\n", Status
);
395 if (!RestartScan
&& (Ccb
->UnicodeQueryTemplate
.Buffer
!= NULL
)) {
397 InitialQuery
= FALSE
;
399 FatConvertToSharedFcb( IrpContext
, Dcb
);
404 if (!FatAcquireSharedFcb( IrpContext
, Dcb
)) {
406 DebugTrace(0, Dbg
, "FatQueryDirectory -> Enqueue to Fsp\n", 0);
407 Status
= FatFsdPostRequest( IrpContext
, Irp
);
408 DebugTrace(-1, Dbg
, "FatQueryDirectory -> %08lx\n", Status
);
418 ULONG BytesConverted
;
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.
426 Buffer
= FatMapUserBuffer( IrpContext
, Irp
);
429 // Make sure the Dcb is still good.
432 FatVerifyFcb( IrpContext
, Dcb
);
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
441 if (IndexSpecified
) {
443 CurrentVbo
= FileIndex
+ sizeof( DIRENT
);
445 } else if (RestartScan
) {
448 Ccb
->OffsetToStartSearchFrom
= 0;
452 CurrentVbo
= Ccb
->OffsetToStartSearchFrom
;
456 // If this is the first try then allocate a buffer for the file
461 (RestartScan
&& UniArgFileName
!= NULL
&& UniArgFileName
->Length
!= 0)) {
464 // If we're restarting the scan, clear out the pattern in the Ccb and regenerate it,
469 if (Ccb
->UnicodeQueryTemplate
.Buffer
) {
471 if (FlagOn(Ccb
->Flags
, CCB_FLAG_FREE_UNICODE
)) {
473 ExFreePoolWithTag(Ccb
->UnicodeQueryTemplate
.Buffer
, TAG_FILENAME_BUFFER
);
474 ClearFlag(Ccb
->Flags
, CCB_FLAG_FREE_UNICODE
);
477 Ccb
->UnicodeQueryTemplate
.Buffer
= NULL
;
478 Ccb
->UnicodeQueryTemplate
.Length
= 0;
479 Ccb
->UnicodeQueryTemplate
.MaximumLength
= 0;
482 if (Ccb
->OemQueryTemplate
.Wild
.Buffer
) {
484 if (FlagOn(Ccb
->Flags
, CCB_FLAG_FREE_OEM_BEST_FIT
)) {
486 RtlFreeOemString( &Ccb
->OemQueryTemplate
.Wild
);
487 ClearFlag(Ccb
->Flags
, CCB_FLAG_FREE_OEM_BEST_FIT
);
490 Ccb
->OemQueryTemplate
.Wild
.Buffer
= NULL
;
491 Ccb
->OemQueryTemplate
.Wild
.Length
= 0;
492 Ccb
->OemQueryTemplate
.Wild
.MaximumLength
= 0;
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
);
506 // - No name was specified
507 // - An empty name was specified
508 // - We received a '*'
509 // - The user specified the DOS equivolent of ????????.???
511 // then match all names.
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
,
522 12*sizeof(WCHAR
) )))) {
524 Ccb
->ContainsWildCards
= TRUE
;
526 SetFlag( Ccb
->Flags
, CCB_FLAG_MATCH_ALL
);
530 BOOLEAN ExtendedName
= FALSE
;
531 OEM_STRING LocalBestFit
;
534 // First and formost, see if the name has wild cards.
537 Ccb
->ContainsWildCards
=
538 FsRtlDoesNameContainWildCards( UniArgFileName
);
541 // Now check to see if the name contains any extended
545 for (i
=0; i
< UniArgFileName
->Length
/ sizeof(WCHAR
); i
++) {
547 if (UniArgFileName
->Buffer
[i
] >= 0x80) {
555 // OK, now do the conversions we need.
560 Status
= RtlUpcaseUnicodeString( &Ccb
->UnicodeQueryTemplate
,
564 if (!NT_SUCCESS(Status
)) {
566 try_return( Status
);
569 SetFlag( Ccb
->Flags
, CCB_FLAG_FREE_UNICODE
);
572 // Upcase the name and convert it to the Oem code page.
575 Status
= RtlUpcaseUnicodeStringToCountedOemString( &LocalBestFit
,
580 // If this conversion failed for any reason other than
581 // an unmappable character fail the request.
584 if (!NT_SUCCESS(Status
)) {
586 if (Status
== STATUS_UNMAPPABLE_CHARACTER
) {
588 SetFlag( Ccb
->Flags
, CCB_FLAG_SKIP_SHORT_NAME_COMPARE
);
592 try_return( Status
);
597 SetFlag( Ccb
->Flags
, CCB_FLAG_FREE_OEM_BEST_FIT
);
605 // This case is optimized because I know I only have to
609 Buffers
= FsRtlAllocatePoolWithTag( PagedPool
,
610 UniArgFileName
->Length
+
611 UniArgFileName
->Length
/ sizeof(WCHAR
),
612 TAG_FILENAME_BUFFER
);
614 Ccb
->UnicodeQueryTemplate
.Buffer
= Buffers
;
615 Ccb
->UnicodeQueryTemplate
.Length
= UniArgFileName
->Length
;
616 Ccb
->UnicodeQueryTemplate
.MaximumLength
= UniArgFileName
->Length
;
618 LocalBestFit
.Buffer
= (PCHAR
)Buffers
+ UniArgFileName
->Length
;
619 LocalBestFit
.Length
= UniArgFileName
->Length
/ sizeof(WCHAR
);
620 LocalBestFit
.MaximumLength
= LocalBestFit
.Length
;
622 SetFlag( Ccb
->Flags
, CCB_FLAG_FREE_UNICODE
);
624 for (i
=0; i
< UniArgFileName
->Length
/ sizeof(WCHAR
); i
++) {
626 WCHAR c
= UniArgFileName
->Buffer
[i
];
628 LocalBestFit
.Buffer
[i
] = (UCHAR
)
629 (Ccb
->UnicodeQueryTemplate
.Buffer
[i
] =
630 (c
< 'a' ? c
: c
<= 'z' ? c
- ('a' - 'A') : c
));
635 // At this point we now have the upcased unicode name,
636 // and the two Oem names if they could be represented in
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.
645 if (!FlagOn( Ccb
->Flags
, CCB_FLAG_SKIP_SHORT_NAME_COMPARE
) &&
646 !FatIsNameShortOemValid( IrpContext
,
648 Ccb
->ContainsWildCards
,
654 RtlFreeOemString( &LocalBestFit
);
655 ClearFlag( Ccb
->Flags
, CCB_FLAG_FREE_OEM_BEST_FIT
);
658 SetFlag( Ccb
->Flags
, CCB_FLAG_SKIP_SHORT_NAME_COMPARE
);
662 // OK, now both locals oem strings correctly reflect their
663 // usability. Now we want to load up the Ccb structure.
665 // Now we will branch on two paths of wheather the name
669 if (!FlagOn( Ccb
->Flags
, CCB_FLAG_SKIP_SHORT_NAME_COMPARE
)) {
671 if (Ccb
->ContainsWildCards
) {
673 Ccb
->OemQueryTemplate
.Wild
= LocalBestFit
;
677 FatStringTo8dot3( IrpContext
,
679 &Ccb
->OemQueryTemplate
.Constant
);
681 if (FlagOn(Ccb
->Flags
, CCB_FLAG_FREE_OEM_BEST_FIT
)) {
683 RtlFreeOemString( &LocalBestFit
);
684 ClearFlag( Ccb
->Flags
, CCB_FLAG_FREE_OEM_BEST_FIT
);
691 // We convert to shared access.
694 FatConvertToSharedFcb( IrpContext
, Dcb
);
700 switch (FileInformationClass
) {
702 case FileDirectoryInformation
:
704 BaseLength
= FIELD_OFFSET( FILE_DIRECTORY_INFORMATION
,
708 case FileFullDirectoryInformation
:
710 BaseLength
= FIELD_OFFSET( FILE_FULL_DIR_INFORMATION
,
714 case FileIdFullDirectoryInformation
:
716 BaseLength
= FIELD_OFFSET( FILE_ID_FULL_DIR_INFORMATION
,
720 case FileNamesInformation
:
722 BaseLength
= FIELD_OFFSET( FILE_NAMES_INFORMATION
,
726 case FileBothDirectoryInformation
:
728 BaseLength
= FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION
,
732 case FileIdBothDirectoryInformation
:
734 BaseLength
= FIELD_OFFSET( FILE_ID_BOTH_DIR_INFORMATION
,
740 try_return( Status
= STATUS_INVALID_INFO_CLASS
);
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.
755 ULONG FileNameLength
;
756 ULONG BytesRemainingInBuffer
;
759 DebugTrace(0, Dbg
, "FatQueryDirectory -> Top of loop\n", 0);
762 // If the user had requested only a single match and we have
763 // returned that, then we stop at this point.
766 if (ReturnSingleEntry
&& NextEntry
!= 0) {
768 try_return( Status
);
773 // We call FatLocateDirent to lock down the next matching dirent.
776 FatLocateDirent( IrpContext
,
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.
796 DebugTrace(0, Dbg
, "FatQueryDirectory -> No dirent\n", 0);
798 if (NextEntry
== 0) {
804 Status
= STATUS_NO_SUCH_FILE
;
808 Status
= STATUS_NO_MORE_FILES
;
812 try_return( Status
);
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
825 Fat8dot3ToString( IrpContext
, Dirent
, TRUE
, &Fat8Dot3String
);
828 if (LongFileName
.Length
== 0) {
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.
838 // Determine the UNICODE length of the file name.
841 FileNameLength
= RtlOemStringToCountedUnicodeSize(&Fat8Dot3String
);
844 // Here are the rules concerning filling up the buffer:
846 // 1. The Io system garentees that there will always be
847 // enough room for at least one base record.
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.
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.
859 BytesRemainingInBuffer
= UserBufferLength
- NextEntry
;
861 if ( (NextEntry
!= 0) &&
862 ( (BaseLength
+ FileNameLength
> BytesRemainingInBuffer
) ||
863 (UserBufferLength
< NextEntry
) ) ) {
865 DebugTrace(0, Dbg
, "Next entry won't fit\n", 0);
867 try_return( Status
= STATUS_SUCCESS
);
870 NT_ASSERT( BytesRemainingInBuffer
>= BaseLength
);
873 // Zero the base part of the structure.
876 RtlZeroMemory( &Buffer
[NextEntry
], BaseLength
);
878 switch ( FileInformationClass
) {
881 // Now fill the base parts of the strucure that are applicable.
884 case FileBothDirectoryInformation
:
885 case FileFullDirectoryInformation
:
886 case FileIdBothDirectoryInformation
:
887 case FileIdFullDirectoryInformation
:
889 DebugTrace(0, Dbg
, "FatQueryDirectory -> Getting file full directory information\n", 0);
892 // Get the Ea file length.
895 FullDirInfo
= (PFILE_FULL_DIR_INFORMATION
)&Buffer
[NextEntry
];
898 // If the EAs are corrupt, ignore the error. We don't want
899 // to abort the directory query.
904 FatGetEaLength( IrpContext
,
907 &FullDirInfo
->EaSize
);
909 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
911 FatResetExceptionState( IrpContext
);
912 FullDirInfo
->EaSize
= 0;
915 case FileDirectoryInformation
:
917 DirInfo
= (PFILE_DIRECTORY_INFORMATION
)&Buffer
[NextEntry
];
919 FatGetDirTimes( IrpContext
, Dirent
, DirInfo
);
921 DirInfo
->EndOfFile
.QuadPart
= Dirent
->FileSize
;
923 if (!FlagOn( Dirent
->Attributes
, FAT_DIRENT_ATTR_DIRECTORY
)) {
926 DirInfo
->AllocationSize
.QuadPart
=
927 (((Dirent
->FileSize
+ DiskAllocSize
- 1) / DiskAllocSize
) *
931 if (Dirent
->Attributes
!= 0) {
932 DirInfo
->FileAttributes
= Dirent
->Attributes
;
937 DirInfo
->FileAttributes
= 0;
939 DirInfo
->FileAttributes
|= FILE_ATTRIBUTE_NORMAL
;
942 DirInfo
->FileIndex
= NextVbo
;
944 DirInfo
->FileNameLength
= FileNameLength
;
946 DebugTrace(0, Dbg
, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String
);
950 case FileNamesInformation
:
952 DebugTrace(0, Dbg
, "FatQueryDirectory -> Getting file names information\n", 0);
954 NamesInfo
= (PFILE_NAMES_INFORMATION
)&Buffer
[NextEntry
];
956 NamesInfo
->FileIndex
= NextVbo
;
958 NamesInfo
->FileNameLength
= FileNameLength
;
960 DebugTrace(0, Dbg
, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String
);
967 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
969 FatBugCheck( FileInformationClass
, 0, 0 );
974 Status
= RtlOemToUnicodeN( (PWCH
)&Buffer
[NextEntry
+ BaseLength
],
975 BytesRemainingInBuffer
- BaseLength
,
977 Fat8Dot3String
.Buffer
,
978 Fat8Dot3String
.Length
);
981 // Check for the case that a single entry doesn't fit.
982 // This should only get this far on the first entry
985 if (BytesConverted
< FileNameLength
) {
987 NT_ASSERT( NextEntry
== 0 );
988 Status
= STATUS_BUFFER_OVERFLOW
;
992 // Set up the previous next entry offset
995 *((PULONG
)(&Buffer
[LastEntry
])) = NextEntry
- LastEntry
;
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
1003 Irp
->IoStatus
.Information
= QuadAlign( Irp
->IoStatus
.Information
) +
1004 BaseLength
+ BytesConverted
;
1007 // If something happened with the conversion, bail here.
1010 if ( !NT_SUCCESS( Status
) ) {
1012 try_return( NOTHING
);
1017 ULONG ShortNameLength
;
1019 FileNameLength
= LongFileName
.Length
;
1022 // Here are the rules concerning filling up the buffer:
1024 // 1. The Io system garentees that there will always be
1025 // enough room for at least one base record.
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.
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.
1037 BytesRemainingInBuffer
= UserBufferLength
- NextEntry
;
1039 if ( (NextEntry
!= 0) &&
1040 ( (BaseLength
+ FileNameLength
> BytesRemainingInBuffer
) ||
1041 (UserBufferLength
< NextEntry
) ) ) {
1043 DebugTrace(0, Dbg
, "Next entry won't fit\n", 0);
1045 try_return( Status
= STATUS_SUCCESS
);
1048 NT_ASSERT( BytesRemainingInBuffer
>= BaseLength
);
1051 // Zero the base part of the structure.
1054 RtlZeroMemory( &Buffer
[NextEntry
], BaseLength
);
1056 switch ( FileInformationClass
) {
1059 // Now fill the base parts of the strucure that are applicable.
1062 case FileBothDirectoryInformation
:
1063 case FileIdBothDirectoryInformation
:
1065 BothDirInfo
= (PFILE_BOTH_DIR_INFORMATION
)&Buffer
[NextEntry
];
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.
1074 Fat8dot3ToString( IrpContext
, Dirent
, FALSE
, &Fat8Dot3String
);
1076 NT_ASSERT( Fat8Dot3String
.Length
<= 12 );
1078 Status
= RtlOemToUnicodeN( &BothDirInfo
->ShortName
[0],
1081 Fat8Dot3String
.Buffer
,
1082 Fat8Dot3String
.Length
);
1084 NT_ASSERT( Status
!= STATUS_BUFFER_OVERFLOW
);
1085 NT_ASSERT( ShortNameLength
<= 12*sizeof(WCHAR
) );
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.
1093 BothDirInfo
->ShortNameLength
= (UCHAR
)ShortNameLength
;
1096 // If something happened with the conversion, bail here.
1099 if ( !NT_SUCCESS( Status
) ) {
1101 try_return( NOTHING
);
1104 case FileFullDirectoryInformation
:
1105 case FileIdFullDirectoryInformation
:
1107 DebugTrace(0, Dbg
, "FatQueryDirectory -> Getting file full directory information\n", 0);
1110 // Get the Ea file length.
1113 FullDirInfo
= (PFILE_FULL_DIR_INFORMATION
)&Buffer
[NextEntry
];
1116 // If the EAs are corrupt, ignore the error. We don't want
1117 // to abort the directory query.
1122 FatGetEaLength( IrpContext
,
1125 &FullDirInfo
->EaSize
);
1127 } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
1129 FatResetExceptionState( IrpContext
);
1130 FullDirInfo
->EaSize
= 0;
1133 case FileDirectoryInformation
:
1135 DirInfo
= (PFILE_DIRECTORY_INFORMATION
)&Buffer
[NextEntry
];
1137 FatGetDirTimes( IrpContext
, Dirent
, DirInfo
);
1139 DirInfo
->EndOfFile
.QuadPart
= Dirent
->FileSize
;
1141 if (!FlagOn( Dirent
->Attributes
, FAT_DIRENT_ATTR_DIRECTORY
)) {
1144 DirInfo
->AllocationSize
.QuadPart
= (
1146 + DiskAllocSize
- 1 )
1151 if (Dirent
->Attributes
!= 0) {
1152 DirInfo
->FileAttributes
= Dirent
->Attributes
;
1157 DirInfo
->FileAttributes
= 0;
1160 DirInfo
->FileAttributes
|= FILE_ATTRIBUTE_NORMAL
;
1164 DirInfo
->FileIndex
= NextVbo
;
1166 DirInfo
->FileNameLength
= FileNameLength
;
1168 DebugTrace(0, Dbg
, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String
);
1172 case FileNamesInformation
:
1174 DebugTrace(0, Dbg
, "FatQueryDirectory -> Getting file names information\n", 0);
1176 NamesInfo
= (PFILE_NAMES_INFORMATION
)&Buffer
[NextEntry
];
1178 NamesInfo
->FileIndex
= NextVbo
;
1180 NamesInfo
->FileNameLength
= FileNameLength
;
1182 DebugTrace(0, Dbg
, "FatQueryDirectory -> Name = \"%Z\"\n", &Fat8Dot3String
);
1189 #pragma prefast( suppress:28159, "things are seriously wrong if we get here" )
1191 FatBugCheck( FileInformationClass
, 0, 0 );
1194 BytesConverted
= BytesRemainingInBuffer
- BaseLength
>= FileNameLength
?
1196 BytesRemainingInBuffer
- BaseLength
;
1198 RtlCopyMemory( &Buffer
[NextEntry
+ BaseLength
],
1199 &LongFileName
.Buffer
[0],
1203 // Set up the previous next entry offset
1206 *((PULONG
)(&Buffer
[LastEntry
])) = NextEntry
- LastEntry
;
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
1214 Irp
->IoStatus
.Information
= QuadAlign( Irp
->IoStatus
.Information
) +
1215 BaseLength
+ BytesConverted
;
1218 // Check for the case that a single entry doesn't fit.
1219 // This should only get this far on the first entry.
1222 if (BytesConverted
< FileNameLength
) {
1224 NT_ASSERT( NextEntry
== 0 );
1226 try_return( Status
= STATUS_BUFFER_OVERFLOW
);
1231 // Finish up by filling in the FileId
1234 switch ( FileInformationClass
) {
1236 case FileIdBothDirectoryInformation
:
1238 IdBothDirInfo
= (PFILE_ID_BOTH_DIR_INFORMATION
)&Buffer
[NextEntry
];
1239 IdBothDirInfo
->FileId
.QuadPart
= FatGenerateFileIdFromDirentAndOffset( Dcb
, Dirent
, NextVbo
);
1242 case FileIdFullDirectoryInformation
:
1244 IdFullDirInfo
= (PFILE_ID_FULL_DIR_INFORMATION
)&Buffer
[NextEntry
];
1245 IdFullDirInfo
->FileId
.QuadPart
= FatGenerateFileIdFromDirentAndOffset( Dcb
, Dirent
, NextVbo
);
1252 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
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.
1260 Irp
->IoStatus
.Information
= 0;
1262 try_return( Status
= _SEH2_GetExceptionCode());
1266 // Set ourselves up for the next iteration
1269 LastEntry
= NextEntry
;
1270 NextEntry
+= (ULONG
)QuadAlign(BaseLength
+ BytesConverted
);
1272 CurrentVbo
= NextVbo
+ sizeof( DIRENT
);
1278 DebugUnwind( FatQueryDirectory
);
1280 FatReleaseFcb( IrpContext
, Dcb
);
1283 // Unpin data in cache if still held.
1286 FatUnpinBcb( IrpContext
, Bcb
);
1289 // Free any dynamically allocated string buffer
1292 FatFreeStringBuffer( &LongFileName
);
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.
1302 if (!_SEH2_AbnormalTermination()) {
1307 // Store the most recent VBO to use as a starting point for
1311 Ccb
->OffsetToStartSearchFrom
= CurrentVbo
;
1314 FatCompleteRequest( IrpContext
, Irp
, Status
);
1317 DebugTrace(-1, Dbg
, "FatQueryDirectory -> %08lx\n", Status
);
1326 // Local Support Routine
1331 PIRP_CONTEXT IrpContext
,
1333 PFILE_DIRECTORY_INFORMATION DirInfo
1338 Routine Description:
1340 This routine pulls the date/time information from a dirent and fills
1341 in the DirInfo structure.
1345 Dirent - Supplies the dirent
1346 DirInfo - Supplies the target structure
1359 // Start with the Last Write Time.
1362 DirInfo
->LastWriteTime
=
1363 FatFatTimeToNtTime( IrpContext
,
1364 Dirent
->LastWriteTime
,
1368 // These fields are only non-zero when in Chicago mode.
1371 if (FatData
.ChicagoMode
) {
1374 // Do a quick check here for Creation and LastAccess
1375 // times that are the same as the LastWriteTime.
1378 if (*((UNALIGNED LONG
*)&Dirent
->CreationTime
) ==
1379 *((UNALIGNED LONG
*)&Dirent
->LastWriteTime
)) {
1381 DirInfo
->CreationTime
.QuadPart
=
1383 DirInfo
->LastWriteTime
.QuadPart
+
1384 Dirent
->CreationMSec
* 10 * 1000 * 10;
1389 // Only do the really hard work if this field is non-zero.
1392 if (((PUSHORT
)Dirent
)[8] != 0) {
1394 DirInfo
->CreationTime
=
1395 FatFatTimeToNtTime( IrpContext
,
1396 Dirent
->CreationTime
,
1397 Dirent
->CreationMSec
);
1401 ExLocalTimeToSystemTime( &FatJanOne1980
,
1402 &DirInfo
->CreationTime
);
1407 // Do a quick check for LastAccessDate.
1410 if (*((PUSHORT
)&Dirent
->LastAccessDate
) ==
1411 *((PUSHORT
)&Dirent
->LastWriteTime
.Date
)) {
1413 PFAT_TIME WriteTime
;
1415 WriteTime
= &Dirent
->LastWriteTime
.Time
;
1417 DirInfo
->LastAccessTime
.QuadPart
=
1418 DirInfo
->LastWriteTime
.QuadPart
-
1419 UInt32x32To64(((WriteTime
->DoubleSeconds
* 2) +
1420 (WriteTime
->Minute
* 60) +
1421 (WriteTime
->Hour
* 60 * 60)),
1427 // Only do the really hard work if this field is non-zero.
1430 if (((PUSHORT
)Dirent
)[9] != 0) {
1432 DirInfo
->LastAccessTime
=
1433 FatFatDateToNtTime( IrpContext
,
1434 Dirent
->LastAccessDate
);
1438 ExLocalTimeToSystemTime( &FatJanOne1980
,
1439 &DirInfo
->LastAccessTime
);
1447 // Local Support Routine
1450 _Requires_lock_held_(_Global_critical_region_
)
1452 FatNotifyChangeDirectory (
1453 IN PIRP_CONTEXT IrpContext
,
1459 Routine Description:
1461 This routine performs the notify change directory operation. It is
1462 responsible for either completing of enqueuing the input Irp.
1466 Irp - Supplies the Irp to process
1470 NTSTATUS - The return status for the operation
1475 NTSTATUS Status
= STATUS_SUCCESS
;
1476 PIO_STACK_LOCATION IrpSp
;
1480 ULONG CompletionFilter
;
1483 BOOLEAN CompleteRequest
;
1488 // Get the current Stack location
1491 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
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
);
1499 // Always set the wait flag in the Irp context for the original request.
1502 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
1505 // Assume we don't complete request.
1508 CompleteRequest
= FALSE
;
1511 // Check on the type of open. We return invalid parameter for all
1512 // but UserDirectoryOpens.
1515 if (FatDecodeFileObject( IrpSp
->FileObject
,
1518 &Ccb
) != UserDirectoryOpen
) {
1520 FatCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
1521 DebugTrace(-1, Dbg
, "FatQueryDirectory -> STATUS_INVALID_PARAMETER\n", 0);
1523 return STATUS_INVALID_PARAMETER
;
1528 // Reference our input parameter to make things easier
1531 CompletionFilter
= IrpSp
->Parameters
.NotifyDirectory
.CompletionFilter
;
1532 WatchTree
= BooleanFlagOn( IrpSp
->Flags
, SL_WATCH_TREE
);
1535 // Try to acquire exclusive access to the Dcb and enqueue the Irp to the
1536 // Fsp if we didn't get access
1539 if (!FatAcquireExclusiveFcb( IrpContext
, Dcb
)) {
1541 DebugTrace(0, Dbg
, "FatNotifyChangeDirectory -> Cannot Acquire Fcb\n", 0);
1543 Status
= FatFsdPostRequest( IrpContext
, Irp
);
1545 DebugTrace(-1, Dbg
, "FatNotifyChangeDirectory -> %08lx\n", Status
);
1552 // Make sure the Fcb is still good
1555 FatVerifyFcb( IrpContext
, Dcb
);
1558 // We need the full name.
1561 FatSetFullFileNameInFcb( IrpContext
, Dcb
);
1564 // If the file is marked as DELETE_PENDING then complete this
1565 // request immediately.
1568 if (FlagOn( Dcb
->FcbState
, FCB_STATE_DELETE_ON_CLOSE
)) {
1570 FatRaiseStatus( IrpContext
, STATUS_DELETE_PENDING
);
1574 // Call the Fsrtl package to process the request.
1577 FsRtlNotifyFullChangeDirectory( Vcb
->NotifySync
,
1578 &Vcb
->DirNotifyList
,
1580 (PSTRING
)&Dcb
->FullFileName
,
1588 Status
= STATUS_PENDING
;
1590 CompleteRequest
= TRUE
;
1594 DebugUnwind( FatNotifyChangeDirectory
);
1596 FatReleaseFcb( IrpContext
, Dcb
);
1599 // If the dir notify package is holding the Irp, we discard the
1603 if (CompleteRequest
) {
1605 FatCompleteRequest( IrpContext
, FatNull
, 0 );
1608 DebugTrace(-1, Dbg
, "FatNotifyChangeDirectory -> %08lx\n", Status
);