3 Copyright (c) 1989-2000 Microsoft Corporation
11 This module implements the File Directory Control routines for Cdfs called
12 by the Fsd/Fsp dispatch drivers.
20 // The Bug check file id for this module
23 #define BugCheckFileId (CDFS_BUG_CHECK_DIRCTRL)
26 // Local support routines
31 IN PIRP_CONTEXT IrpContext
,
33 IN PIO_STACK_LOCATION IrpSp
,
39 CdNotifyChangeDirectory (
40 IN PIRP_CONTEXT IrpContext
,
42 IN PIO_STACK_LOCATION IrpSp
,
47 CdInitializeEnumeration (
48 IN PIRP_CONTEXT IrpContext
,
49 IN PIO_STACK_LOCATION IrpSp
,
52 IN OUT PFILE_ENUM_CONTEXT FileContext
,
53 OUT PBOOLEAN ReturnNextEntry
,
54 OUT PBOOLEAN ReturnSingleEntry
,
55 OUT PBOOLEAN InitialQuery
60 IN PIRP_CONTEXT IrpContext
,
62 IN OUT PFILE_ENUM_CONTEXT FileContext
,
63 IN BOOLEAN ReturnNextEntry
67 #pragma alloc_text(PAGE, CdCommonDirControl)
68 #pragma alloc_text(PAGE, CdEnumerateIndex)
69 #pragma alloc_text(PAGE, CdInitializeEnumeration)
70 #pragma alloc_text(PAGE, CdNotifyChangeDirectory)
71 #pragma alloc_text(PAGE, CdQueryDirectory)
77 IN PIRP_CONTEXT IrpContext
,
85 This routine is the entry point for the directory control operations. These
86 are directory enumerations and directory notify calls. We verify the
87 user's handle is for a directory and then call the appropriate routine.
91 Irp - Irp for this request.
95 NTSTATUS - Status returned from the lower level routines.
101 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
109 // Decode the user file object and fail this request if it is not
113 if (CdDecodeFileObject( IrpContext
,
116 &Ccb
) != UserDirectoryOpen
) {
118 CdCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_PARAMETER
);
119 return STATUS_INVALID_PARAMETER
;
123 // We know this is a directory control so we'll case on the
124 // minor function, and call a internal worker routine to complete
128 switch (IrpSp
->MinorFunction
) {
130 case IRP_MN_QUERY_DIRECTORY
:
132 Status
= CdQueryDirectory( IrpContext
, Irp
, IrpSp
, Fcb
, Ccb
);
135 case IRP_MN_NOTIFY_CHANGE_DIRECTORY
:
137 Status
= CdNotifyChangeDirectory( IrpContext
, Irp
, IrpSp
, Ccb
);
142 CdCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_DEVICE_REQUEST
);
143 Status
= STATUS_INVALID_DEVICE_REQUEST
;
152 // Local support routines
157 IN PIRP_CONTEXT IrpContext
,
159 IN PIO_STACK_LOCATION IrpSp
,
168 This routine performs the query directory operation. It is responsible
169 for either completing of enqueuing the input Irp. We store the state of the
174 Irp - Supplies the Irp to process
176 IrpSp - Stack location for this Irp.
178 Fcb - Fcb for this directory.
180 Ccb - Ccb for this directory open.
184 NTSTATUS - The return status for the operation
189 NTSTATUS Status
= STATUS_SUCCESS
;
190 ULONG Information
= 0;
196 ULONG SeparatorBytes
;
197 ULONG VersionStringBytes
;
199 FILE_ENUM_CONTEXT FileContext
;
201 BOOLEAN InitialQuery
;
202 BOOLEAN ReturnNextEntry
;
203 BOOLEAN ReturnSingleEntry
;
205 BOOLEAN DoCcbUpdate
= FALSE
;
208 ULONG BytesRemainingInBuffer
;
212 PFILE_BOTH_DIR_INFORMATION DirInfo
= NULL
; /* ReactOS Change: GCC Uninit var */
213 PFILE_NAMES_INFORMATION NamesInfo
;
214 PFILE_ID_FULL_DIR_INFORMATION IdFullDirInfo
;
215 PFILE_ID_BOTH_DIR_INFORMATION IdBothDirInfo
;
220 // Check if we support this search mode. Also remember the size of the base part of
221 // each of these structures.
224 switch (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
) {
226 case FileDirectoryInformation
:
228 BaseLength
= FIELD_OFFSET( FILE_DIRECTORY_INFORMATION
,
232 case FileFullDirectoryInformation
:
234 BaseLength
= FIELD_OFFSET( FILE_FULL_DIR_INFORMATION
,
238 case FileIdFullDirectoryInformation
:
240 BaseLength
= FIELD_OFFSET( FILE_ID_FULL_DIR_INFORMATION
,
244 case FileNamesInformation
:
246 BaseLength
= FIELD_OFFSET( FILE_NAMES_INFORMATION
,
250 case FileBothDirectoryInformation
:
252 BaseLength
= FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION
,
256 case FileIdBothDirectoryInformation
:
258 BaseLength
= FIELD_OFFSET( FILE_ID_BOTH_DIR_INFORMATION
,
264 CdCompleteRequest( IrpContext
, Irp
, STATUS_INVALID_INFO_CLASS
);
265 return STATUS_INVALID_INFO_CLASS
;
269 // Get the user buffer.
272 CdMapUserBuffer( IrpContext
, &UserBuffer
);
275 // Initialize our search context.
278 CdInitializeFileContext( IrpContext
, &FileContext
);
281 // Acquire the directory.
284 CdAcquireFileShared( IrpContext
, Fcb
);
287 // Use a try-finally to facilitate cleanup.
293 // Verify the Fcb is still good.
296 CdVerifyFcbOperation( IrpContext
, Fcb
);
299 // Start by getting the initial state for the enumeration. This will set up the Ccb with
300 // the initial search parameters and let us know the starting offset in the directory
304 CdInitializeEnumeration( IrpContext
,
314 // The current dirent is stored in the InitialDirent field. We capture
315 // this here so that we have a valid restart point even if we don't
316 // find a single entry.
319 ThisDirent
= &FileContext
.InitialDirent
->Dirent
;
322 // At this point we are about to enter our query loop. We have
323 // determined the index into the directory file to begin the
324 // search. LastEntry and NextEntry are used to index into the user
325 // buffer. LastEntry is the last entry we've added, NextEntry is
326 // current one we're working on. If NextEntry is non-zero, then
327 // at least one entry was added.
333 // If the user had requested only a single match and we have
334 // returned that, then we stop at this point. We update the Ccb with
335 // the status based on the last entry returned.
338 if ((NextEntry
!= 0) && ReturnSingleEntry
) {
345 // We try to locate the next matching dirent. Our search if based on a starting
346 // dirent offset, whether we should return the current or next entry, whether
347 // we should be doing a short name search and finally whether we should be
348 // checking for a version match.
351 Found
= CdEnumerateIndex( IrpContext
, Ccb
, &FileContext
, ReturnNextEntry
);
354 // Initialize the value for the next search.
357 ReturnNextEntry
= TRUE
;
360 // If we didn't receive a dirent, then we are at the end of the
361 // directory. If we have returned any files, we exit with
362 // success, otherwise we return STATUS_NO_MORE_FILES.
367 if (NextEntry
== 0) {
369 Status
= STATUS_NO_MORE_FILES
;
373 Status
= STATUS_NO_SUCH_FILE
;
382 // Remember the dirent for the file we just found.
385 ThisDirent
= &FileContext
.InitialDirent
->Dirent
;
388 // Here are the rules concerning filling up the buffer:
390 // 1. The Io system guarantees that there will always be
391 // enough room for at least one base record.
393 // 2. If the full first record (including file name) cannot
394 // fit, as much of the name as possible is copied and
395 // STATUS_BUFFER_OVERFLOW is returned.
397 // 3. If a subsequent record cannot completely fit into the
398 // buffer, none of it (as in 0 bytes) is copied, and
399 // STATUS_SUCCESS is returned. A subsequent query will
400 // pick up with this record.
404 // Let's compute the number of bytes we need to transfer the current entry.
408 VersionStringBytes
= 0;
411 // We can look directly at the dirent that we found.
414 FileNameBytes
= ThisDirent
->CdFileName
.FileName
.Length
;
417 // Compute the number of bytes for the version string if
418 // we will return this. Allow directories with illegal ";".
421 if (((Ccb
->SearchExpression
.VersionString
.Length
!= 0) ||
422 (FlagOn(ThisDirent
->DirentFlags
, CD_ATTRIBUTE_DIRECTORY
))) &&
423 (ThisDirent
->CdFileName
.VersionString
.Length
!= 0)) {
427 VersionStringBytes
= ThisDirent
->CdFileName
.VersionString
.Length
;
431 // If the slot for the next entry would be beyond the length of the
432 // user's buffer just exit (we know we've returned at least one entry
433 // already). This will happen when we align the pointer past the end.
436 if (NextEntry
> IrpSp
->Parameters
.QueryDirectory
.Length
) {
438 ReturnNextEntry
= FALSE
;
440 try_leave( Status
= STATUS_SUCCESS
);
444 // Compute the number of bytes remaining in the buffer. Round this
445 // down to a WCHAR boundary so we can copy full characters.
448 BytesRemainingInBuffer
= IrpSp
->Parameters
.QueryDirectory
.Length
- NextEntry
;
449 ClearFlag( BytesRemainingInBuffer
, 1 );
452 // If this won't fit and we have returned a previous entry then just
453 // return STATUS_SUCCESS.
456 if ((BaseLength
+ FileNameBytes
+ SeparatorBytes
+ VersionStringBytes
) > BytesRemainingInBuffer
) {
459 // If we already found an entry then just exit.
462 if (NextEntry
!= 0) {
464 ReturnNextEntry
= FALSE
;
466 try_leave( Status
= STATUS_SUCCESS
);
470 // Don't even try to return the version string if it doesn't all fit.
471 // Reduce the FileNameBytes to just fit in the buffer.
474 if ((BaseLength
+ FileNameBytes
) > BytesRemainingInBuffer
) {
476 FileNameBytes
= BytesRemainingInBuffer
- BaseLength
;
480 // Don't return any version string bytes.
487 // Use a status code of STATUS_BUFFER_OVERFLOW. Also set
488 // ReturnSingleEntry so that we will exit the loop at the top.
491 Status
= STATUS_BUFFER_OVERFLOW
;
492 ReturnSingleEntry
= TRUE
;
496 // Protect access to the user buffer with an exception handler.
497 // Since (at our request) IO doesn't buffer these requests, we have
498 // to guard against a user messing with the page protection and other
505 // Zero and initialize the base part of the current entry.
508 RtlZeroMemory( Add2Ptr( UserBuffer
, NextEntry
, PVOID
),
512 // Now we have an entry to return to our caller.
513 // We'll case on the type of information requested and fill up
514 // the user buffer if everything fits.
517 switch (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
) {
519 case FileBothDirectoryInformation
:
520 case FileFullDirectoryInformation
:
521 case FileIdBothDirectoryInformation
:
522 case FileIdFullDirectoryInformation
:
523 case FileDirectoryInformation
:
525 DirInfo
= Add2Ptr( UserBuffer
, NextEntry
, PFILE_BOTH_DIR_INFORMATION
);
528 // Use the create time for all the time stamps.
531 CdConvertCdTimeToNtTime( IrpContext
,
532 FileContext
.InitialDirent
->Dirent
.CdTime
,
533 &DirInfo
->CreationTime
);
535 DirInfo
->LastWriteTime
= DirInfo
->ChangeTime
= DirInfo
->CreationTime
;
538 // Set the attributes and sizes separately for directories and
542 if (FlagOn( ThisDirent
->DirentFlags
, CD_ATTRIBUTE_DIRECTORY
)) {
544 DirInfo
->EndOfFile
.QuadPart
= DirInfo
->AllocationSize
.QuadPart
= 0;
546 SetFlag( DirInfo
->FileAttributes
, FILE_ATTRIBUTE_DIRECTORY
);
550 DirInfo
->EndOfFile
.QuadPart
= FileContext
.FileSize
;
551 DirInfo
->AllocationSize
.QuadPart
= LlSectorAlign( FileContext
.FileSize
);
555 // All Cdrom files are readonly. We also copy the existence
556 // bit to the hidden attribute.
559 SetFlag( DirInfo
->FileAttributes
, FILE_ATTRIBUTE_READONLY
);
561 if (FlagOn( ThisDirent
->DirentFlags
,
562 CD_ATTRIBUTE_HIDDEN
)) {
564 SetFlag( DirInfo
->FileAttributes
, FILE_ATTRIBUTE_HIDDEN
);
567 DirInfo
->FileIndex
= ThisDirent
->DirentOffset
;
569 DirInfo
->FileNameLength
= FileNameBytes
+ SeparatorBytes
+ VersionStringBytes
;
573 case FileNamesInformation
:
575 NamesInfo
= Add2Ptr( UserBuffer
, NextEntry
, PFILE_NAMES_INFORMATION
);
577 NamesInfo
->FileIndex
= ThisDirent
->DirentOffset
;
579 NamesInfo
->FileNameLength
= FileNameBytes
+ SeparatorBytes
+ VersionStringBytes
;
583 /* ReactOS Change: GCC "enumeration value not handled in switch" */
588 // Fill in the FileId
591 switch (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
) {
593 case FileIdBothDirectoryInformation
:
595 IdBothDirInfo
= Add2Ptr( UserBuffer
, NextEntry
, PFILE_ID_BOTH_DIR_INFORMATION
);
596 CdSetFidFromParentAndDirent( IdBothDirInfo
->FileId
, Fcb
, ThisDirent
);
599 case FileIdFullDirectoryInformation
:
601 IdFullDirInfo
= Add2Ptr( UserBuffer
, NextEntry
, PFILE_ID_FULL_DIR_INFORMATION
);
602 CdSetFidFromParentAndDirent( IdFullDirInfo
->FileId
, Fcb
, ThisDirent
);
610 // Now copy as much of the name as possible. We also may have a version
614 if (FileNameBytes
!= 0) {
617 // This is a Unicode name, we can copy the bytes directly.
620 RtlCopyMemory( Add2Ptr( UserBuffer
, NextEntry
+ BaseLength
, PVOID
),
621 ThisDirent
->CdFileName
.FileName
.Buffer
,
624 if (SeparatorBytes
!= 0) {
626 *(Add2Ptr( UserBuffer
,
627 NextEntry
+ BaseLength
+ FileNameBytes
,
630 if (VersionStringBytes
!= 0) {
632 RtlCopyMemory( Add2Ptr( UserBuffer
,
633 NextEntry
+ BaseLength
+ FileNameBytes
+ sizeof( WCHAR
),
635 ThisDirent
->CdFileName
.VersionString
.Buffer
,
636 VersionStringBytes
);
642 // Fill in the short name if we got STATUS_SUCCESS. The short name
643 // may already be in the file context. Otherwise we will check
644 // whether the long name is 8.3. Special case the self and parent
648 if ((Status
== STATUS_SUCCESS
) &&
649 (IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileBothDirectoryInformation
||
650 IrpSp
->Parameters
.QueryDirectory
.FileInformationClass
== FileIdBothDirectoryInformation
) &&
651 (Ccb
->SearchExpression
.VersionString
.Length
== 0) &&
652 !FlagOn( ThisDirent
->Flags
, DIRENT_FLAG_CONSTANT_ENTRY
)) {
655 // If we already have the short name then copy into the user's buffer.
658 if (FileContext
.ShortName
.FileName
.Length
!= 0) {
660 RtlCopyMemory( DirInfo
->ShortName
,
661 FileContext
.ShortName
.FileName
.Buffer
,
662 FileContext
.ShortName
.FileName
.Length
);
664 DirInfo
->ShortNameLength
= (CCHAR
) FileContext
.ShortName
.FileName
.Length
;
667 // If the short name length is currently zero then check if
668 // the long name is not 8.3. We can copy the short name in
669 // unicode form directly into the caller's buffer.
674 if (!CdIs8dot3Name( IrpContext
,
675 ThisDirent
->CdFileName
.FileName
)) {
677 CdGenerate8dot3Name( IrpContext
,
678 &ThisDirent
->CdCaseFileName
.FileName
,
679 ThisDirent
->DirentOffset
,
681 &FileContext
.ShortName
.FileName
.Length
);
683 DirInfo
->ShortNameLength
= (CCHAR
) FileContext
.ShortName
.FileName
.Length
;
690 // Sum the total number of bytes for the information field.
693 FileNameBytes
+= SeparatorBytes
+ VersionStringBytes
;
696 // Update the information with the number of bytes stored in the
697 // buffer. We quad-align the existing buffer to add any necessary
701 Information
= NextEntry
+ BaseLength
+ FileNameBytes
;
704 // Go back to the previous entry and fill in the update to this entry.
707 *(Add2Ptr( UserBuffer
, LastEntry
, PULONG
)) = NextEntry
- LastEntry
;
710 // Set up our variables for the next dirent.
713 InitialQuery
= FALSE
;
715 LastEntry
= NextEntry
;
716 NextEntry
= QuadAlign( Information
);
718 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
721 // We had a problem filling in the user's buffer, so stop and
722 // fail this request. This is the only reason any exception
723 // would have occured at this level.
727 try_leave( Status
= _SEH2_GetExceptionCode());
736 // Cleanup our search context - *before* acquiring the FCB mutex exclusive,
737 // else can block on threads in cdcreateinternalstream/purge which
738 // hold the FCB but are waiting for all maps in this stream to be released.
741 CdCleanupFileContext( IrpContext
, &FileContext
);
744 // Now we can safely acquire the FCB mutex if we need to.
747 if (DoCcbUpdate
&& !NT_ERROR( Status
)) {
750 // Update the Ccb to show the current state of the enumeration.
753 CdLockFcb( IrpContext
, Fcb
);
755 Ccb
->CurrentDirentOffset
= ThisDirent
->DirentOffset
;
757 ClearFlag( Ccb
->Flags
, CCB_FLAG_ENUM_RETURN_NEXT
);
759 if (ReturnNextEntry
) {
761 SetFlag( Ccb
->Flags
, CCB_FLAG_ENUM_RETURN_NEXT
);
764 CdUnlockFcb( IrpContext
, Fcb
);
771 CdReleaseFile( IrpContext
, Fcb
);
775 // Complete the request here.
778 Irp
->IoStatus
.Information
= Information
;
780 CdCompleteRequest( IrpContext
, Irp
, Status
);
786 // Local support routines
790 CdNotifyChangeDirectory (
791 IN PIRP_CONTEXT IrpContext
,
793 IN PIO_STACK_LOCATION IrpSp
,
801 This routine performs the notify change directory operation. It is
802 responsible for either completing of enqueuing the input Irp. Although there
803 will never be a notify signalled on a CDROM disk we still support this call.
805 We have already checked that this is not an OpenById handle.
809 Irp - Supplies the Irp to process
811 IrpSp - Io stack location for this request.
813 Ccb - Handle to the directory being watched.
817 NTSTATUS - STATUS_PENDING, any other error will raise.
825 // Always set the wait bit in the IrpContext so the initial wait can't fail.
828 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
831 // Acquire the Vcb shared.
834 CdAcquireVcbShared( IrpContext
, IrpContext
->Vcb
, FALSE
);
837 // Use a try-finally to facilitate cleanup.
846 CdVerifyVcb( IrpContext
, IrpContext
->Vcb
);
849 // Call the Fsrtl package to process the request. We cast the
850 // unicode strings to ansi strings as the dir notify package
851 // only deals with memory matching.
854 FsRtlNotifyFullChangeDirectory( IrpContext
->Vcb
->NotifySync
,
855 &IrpContext
->Vcb
->DirNotifyList
,
857 (PSTRING
) &IrpSp
->FileObject
->FileName
,
858 BooleanFlagOn( IrpSp
->Flags
, SL_WATCH_TREE
),
860 IrpSp
->Parameters
.NotifyDirectory
.CompletionFilter
,
871 CdReleaseVcb( IrpContext
, IrpContext
->Vcb
);
875 // Cleanup the IrpContext.
878 CdCompleteRequest( IrpContext
, NULL
, STATUS_SUCCESS
);
880 return STATUS_PENDING
;
885 // Local support routine
889 CdInitializeEnumeration (
890 IN PIRP_CONTEXT IrpContext
,
891 IN PIO_STACK_LOCATION IrpSp
,
894 IN OUT PFILE_ENUM_CONTEXT FileContext
,
895 OUT PBOOLEAN ReturnNextEntry
,
896 OUT PBOOLEAN ReturnSingleEntry
,
897 OUT PBOOLEAN InitialQuery
904 This routine is called to initialize the enumeration variables and structures.
905 We look at the state of a previous enumeration from the Ccb as well as any
906 input values from the user. On exit we will position the FileContext at
907 a file in the directory and let the caller know whether this entry or the
908 next entry should be returned.
912 IrpSp - Irp stack location for this request.
914 Fcb - Fcb for this directory.
916 Ccb - Ccb for the directory handle.
918 FileContext - FileContext to use for this enumeration.
920 ReturnNextEntry - Address to store whether we should return the entry at
921 the FileContext position or the next entry.
923 ReturnSingleEntry - Address to store whether we should only return
926 InitialQuery - Address to store whether this is the first enumeration
927 query on this handle.
938 PUNICODE_STRING FileName
;
939 CD_NAME WildCardName
;
940 CD_NAME SearchExpression
;
945 ULONG LastDirentOffset
;
953 // If this is the initial query then build a search expression from the input
957 if (!FlagOn( Ccb
->Flags
, CCB_FLAG_ENUM_INITIALIZED
)) {
959 FileName
= IrpSp
->Parameters
.QueryDirectory
.FileName
;
964 // If the filename is not specified or is a single '*' then we will
968 if ((FileName
== NULL
) ||
969 (FileName
->Buffer
== NULL
) ||
970 (FileName
->Length
== 0) ||
971 ((FileName
->Length
== sizeof( WCHAR
)) &&
972 (FileName
->Buffer
[0] == L
'*'))) {
974 SetFlag( CcbFlags
, CCB_FLAG_ENUM_MATCH_ALL
);
975 RtlZeroMemory( &SearchExpression
, sizeof( SearchExpression
));
978 // Otherwise build the CdName from the name in the stack location.
979 // This involves building both the name and version portions and
980 // checking for wild card characters. We also upcase the string if
981 // this is a case-insensitive search.
987 // Create a CdName to check for wild cards.
990 WildCardName
.FileName
= *FileName
;
992 CdConvertNameToCdName( IrpContext
, &WildCardName
);
995 // The name better have at least one character.
998 if (WildCardName
.FileName
.Length
== 0) {
1000 CdRaiseStatus( IrpContext
, STATUS_INVALID_PARAMETER
);
1004 // Check for wildcards in the separate components.
1007 if (FsRtlDoesNameContainWildCards( &WildCardName
.FileName
)) {
1009 SetFlag( CcbFlags
, CCB_FLAG_ENUM_NAME_EXP_HAS_WILD
);
1012 if ((WildCardName
.VersionString
.Length
!= 0) &&
1013 (FsRtlDoesNameContainWildCards( &WildCardName
.VersionString
))) {
1015 SetFlag( CcbFlags
, CCB_FLAG_ENUM_VERSION_EXP_HAS_WILD
);
1018 // Check if this is a wild card only and match all version
1022 if ((WildCardName
.VersionString
.Length
== sizeof( WCHAR
)) &&
1023 (WildCardName
.VersionString
.Buffer
[0] == L
'*')) {
1025 SetFlag( CcbFlags
, CCB_FLAG_ENUM_VERSION_MATCH_ALL
);
1030 // Now create the search expression to store in the Ccb.
1033 SearchExpression
.FileName
.Buffer
= FsRtlAllocatePoolWithTag( CdPagedPool
,
1035 TAG_ENUM_EXPRESSION
);
1037 SearchExpression
.FileName
.MaximumLength
= FileName
->Length
;
1040 // Either copy the name directly or perform the upcase.
1043 if (FlagOn( Ccb
->Flags
, CCB_FLAG_IGNORE_CASE
)) {
1045 Status
= RtlUpcaseUnicodeString( (PUNICODE_STRING
) &SearchExpression
.FileName
,
1050 // This should never fail.
1053 ASSERT( Status
== STATUS_SUCCESS
);
1057 RtlCopyMemory( SearchExpression
.FileName
.Buffer
,
1063 // Now split into the separate name and version components.
1066 SearchExpression
.FileName
.Length
= WildCardName
.FileName
.Length
;
1067 SearchExpression
.VersionString
.Length
= WildCardName
.VersionString
.Length
;
1068 SearchExpression
.VersionString
.MaximumLength
= WildCardName
.VersionString
.MaximumLength
;
1070 SearchExpression
.VersionString
.Buffer
= Add2Ptr( SearchExpression
.FileName
.Buffer
,
1071 SearchExpression
.FileName
.Length
+ sizeof( WCHAR
),
1076 // But we do not want to return the constant "." and ".." entries for
1077 // the root directory, for consistency with the rest of Microsoft's
1081 if (Fcb
== Fcb
->Vcb
->RootIndexFcb
) {
1083 SetFlag( CcbFlags
, CCB_FLAG_ENUM_NOMATCH_CONSTANT_ENTRY
);
1087 // Now lock the Fcb in order to update the Ccb with the inital
1088 // enumeration values.
1091 CdLockFcb( IrpContext
, Fcb
);
1094 // Check again that this is the initial search.
1097 if (!FlagOn( Ccb
->Flags
, CCB_FLAG_ENUM_INITIALIZED
)) {
1100 // Update the values in the Ccb.
1103 Ccb
->CurrentDirentOffset
= Fcb
->StreamOffset
;
1104 Ccb
->SearchExpression
= SearchExpression
;
1107 // Set the appropriate flags in the Ccb.
1110 SetFlag( Ccb
->Flags
, CcbFlags
| CCB_FLAG_ENUM_INITIALIZED
);
1113 // Otherwise cleanup any buffer allocated here.
1118 if (!FlagOn( CcbFlags
, CCB_FLAG_ENUM_MATCH_ALL
)) {
1120 CdFreePool( &SearchExpression
.FileName
.Buffer
);
1125 // Otherwise lock the Fcb so we can read the current enumeration values.
1130 CdLockFcb( IrpContext
, Fcb
);
1134 // Capture the current state of the enumeration.
1136 // If the user specified an index then use his offset. We always
1137 // return the next entry in this case.
1140 if (FlagOn( IrpSp
->Flags
, SL_INDEX_SPECIFIED
)) {
1142 KnownOffset
= FALSE
;
1143 DirentOffset
= IrpSp
->Parameters
.QueryDirectory
.FileIndex
;
1144 *ReturnNextEntry
= TRUE
;
1147 // If we are restarting the scan then go from the self entry.
1150 } else if (FlagOn( IrpSp
->Flags
, SL_RESTART_SCAN
)) {
1153 DirentOffset
= Fcb
->StreamOffset
;
1154 *ReturnNextEntry
= FALSE
;
1157 // Otherwise use the values from the Ccb.
1163 DirentOffset
= Ccb
->CurrentDirentOffset
;
1164 *ReturnNextEntry
= BooleanFlagOn( Ccb
->Flags
, CCB_FLAG_ENUM_RETURN_NEXT
);
1171 CdUnlockFcb( IrpContext
, Fcb
);
1174 // We have the starting offset in the directory and whether to return
1175 // that entry or the next. If we are at the beginning of the directory
1176 // and are returning that entry, then tell our caller this is the
1180 *InitialQuery
= FALSE
;
1182 if ((DirentOffset
== Fcb
->StreamOffset
) &&
1183 !(*ReturnNextEntry
)) {
1185 *InitialQuery
= TRUE
;
1189 // If there is no file object then create it now.
1192 if (Fcb
->FileObject
== NULL
) {
1194 CdCreateInternalStream( IrpContext
, Fcb
->Vcb
, Fcb
);
1198 // Determine the offset in the stream to position the FileContext and
1199 // whether this offset is known to be a file offset.
1201 // If this offset is known to be safe then go ahead and position the
1202 // file context. This handles the cases where the offset is the beginning
1203 // of the stream, the offset is from a previous search or this is the
1209 CdLookupInitialFileDirent( IrpContext
, Fcb
, FileContext
, DirentOffset
);
1212 // Otherwise we walk through the directory from the beginning until
1213 // we reach the entry which contains this offset.
1218 LastDirentOffset
= Fcb
->StreamOffset
;
1221 CdLookupInitialFileDirent( IrpContext
, Fcb
, FileContext
, LastDirentOffset
);
1224 // If the requested offset is prior to the beginning offset in the stream
1225 // then don't return the next entry.
1228 if (DirentOffset
< LastDirentOffset
) {
1230 *ReturnNextEntry
= FALSE
;
1233 // Else look for the last entry which ends past the desired index.
1239 // Keep walking through the directory until we run out of
1240 // entries or we find an entry which ends beyond the input
1247 // If we have passed the index value then exit.
1250 if (FileContext
->InitialDirent
->Dirent
.DirentOffset
> DirentOffset
) {
1257 // Remember the current position in case we need to go back.
1260 LastDirentOffset
= FileContext
->InitialDirent
->Dirent
.DirentOffset
;
1263 // Exit if the next entry is beyond the desired index value.
1266 if (LastDirentOffset
+ FileContext
->InitialDirent
->Dirent
.DirentLength
> DirentOffset
) {
1271 Found
= CdLookupNextInitialFileDirent( IrpContext
, Fcb
, FileContext
);
1276 // If we didn't find the entry then go back to the last known entry.
1277 // This can happen if the index lies in the unused range at the
1283 CdCleanupFileContext( IrpContext
, FileContext
);
1284 CdInitializeFileContext( IrpContext
, FileContext
);
1286 CdLookupInitialFileDirent( IrpContext
, Fcb
, FileContext
, LastDirentOffset
);
1292 // Only update the dirent name if we will need it for some reason.
1293 // Don't update this name if we are returning the next entry and
1294 // the search string has a version component.
1297 FileContext
->ShortName
.FileName
.Length
= 0;
1299 if (!(*ReturnNextEntry
) ||
1300 (Ccb
->SearchExpression
.VersionString
.Length
== 0)) {
1303 // Update the name in the dirent into filename and version components.
1306 CdUpdateDirentName( IrpContext
,
1307 &FileContext
->InitialDirent
->Dirent
,
1308 FlagOn( Ccb
->Flags
, CCB_FLAG_IGNORE_CASE
));
1312 // Look at the flag in the IrpSp indicating whether to return just
1316 *ReturnSingleEntry
= FALSE
;
1318 if (FlagOn( IrpSp
->Flags
, SL_RETURN_SINGLE_ENTRY
)) {
1320 *ReturnSingleEntry
= TRUE
;
1328 // Local support routine
1333 IN PIRP_CONTEXT IrpContext
,
1335 IN OUT PFILE_ENUM_CONTEXT FileContext
,
1336 IN BOOLEAN ReturnNextEntry
1341 Routine Description:
1343 This routine is the worker routine for index enumeration. We are positioned
1344 at some dirent in the directory and will either return the first match
1345 at that point or look to the next entry. The Ccb contains details about
1346 the type of matching to do. If the user didn't specify a version in
1347 his search string then we only return the first version of a sequence
1348 of files with versions. We also don't return any associated files.
1352 Ccb - Ccb for this directory handle.
1354 FileContext - File context already positioned at some entry in the directory.
1356 ReturnNextEntry - Indicates if we are returning this entry or should start
1357 with the next entry.
1361 BOOLEAN - TRUE if next entry is found, FALSE otherwise.
1366 PDIRENT PreviousDirent
= NULL
;
1367 PDIRENT ThisDirent
= &FileContext
->InitialDirent
->Dirent
;
1369 BOOLEAN Found
= FALSE
;
1374 // Loop until we find a match or exhaust the directory.
1380 // Move to the next entry unless we want to consider the current
1384 if (ReturnNextEntry
) {
1386 if (!CdLookupNextInitialFileDirent( IrpContext
, Ccb
->Fcb
, FileContext
)) {
1391 PreviousDirent
= ThisDirent
;
1392 ThisDirent
= &FileContext
->InitialDirent
->Dirent
;
1394 CdUpdateDirentName( IrpContext
, ThisDirent
, FlagOn( Ccb
->Flags
, CCB_FLAG_IGNORE_CASE
));
1398 ReturnNextEntry
= TRUE
;
1402 // Don't bother if we have a constant entry and are ignoring them.
1405 if (FlagOn( ThisDirent
->Flags
, DIRENT_FLAG_CONSTANT_ENTRY
) &&
1406 FlagOn( Ccb
->Flags
, CCB_FLAG_ENUM_NOMATCH_CONSTANT_ENTRY
)) {
1412 // Look at the current entry if it is not an associated file
1413 // and the name doesn't match the previous file if the version
1414 // name is not part of the search.
1417 if (!FlagOn( ThisDirent
->DirentFlags
, CD_ATTRIBUTE_ASSOC
)) {
1420 // Check if this entry matches the previous entry except
1421 // for version number and whether we should return the
1422 // entry in that case. Go directly to the name comparison
1425 // There is no previous entry.
1426 // The search expression has a version component.
1427 // The name length doesn't match the length of the previous entry.
1428 // The base name strings don't match.
1431 if ((PreviousDirent
== NULL
) ||
1432 (Ccb
->SearchExpression
.VersionString
.Length
!= 0) ||
1433 (PreviousDirent
->CdCaseFileName
.FileName
.Length
!= ThisDirent
->CdCaseFileName
.FileName
.Length
) ||
1434 FlagOn( PreviousDirent
->DirentFlags
, CD_ATTRIBUTE_ASSOC
) ||
1435 !RtlEqualMemory( PreviousDirent
->CdCaseFileName
.FileName
.Buffer
,
1436 ThisDirent
->CdCaseFileName
.FileName
.Buffer
,
1437 ThisDirent
->CdCaseFileName
.FileName
.Length
)) {
1440 // If we match all names then return to our caller.
1443 if (FlagOn( Ccb
->Flags
, CCB_FLAG_ENUM_MATCH_ALL
)) {
1445 FileContext
->ShortName
.FileName
.Length
= 0;
1451 // Check if the long name matches the search expression.
1454 if (CdIsNameInExpression( IrpContext
,
1455 &ThisDirent
->CdCaseFileName
,
1456 &Ccb
->SearchExpression
,
1461 // Let our caller know we found an entry.
1465 FileContext
->ShortName
.FileName
.Length
= 0;
1470 // The long name didn't match so we need to check for a
1471 // possible short name match. There is no match if the
1472 // long name is 8dot3 or the search expression has a
1473 // version component. Special case the self and parent
1477 if ((Ccb
->SearchExpression
.VersionString
.Length
== 0) &&
1478 !FlagOn( ThisDirent
->Flags
, DIRENT_FLAG_CONSTANT_ENTRY
) &&
1479 !CdIs8dot3Name( IrpContext
,
1480 ThisDirent
->CdFileName
.FileName
)) {
1482 CdGenerate8dot3Name( IrpContext
,
1483 &ThisDirent
->CdCaseFileName
.FileName
,
1484 ThisDirent
->DirentOffset
,
1485 FileContext
->ShortName
.FileName
.Buffer
,
1486 &FileContext
->ShortName
.FileName
.Length
);
1489 // Check if this name matches.
1492 if (CdIsNameInExpression( IrpContext
,
1493 &FileContext
->ShortName
,
1494 &Ccb
->SearchExpression
,
1499 // Let our caller know we found an entry.
1511 // If we found the entry then make sure we walk through all of the
1517 CdLookupLastFileDirent( IrpContext
, Ccb
->Fcb
, FileContext
);