3 Copyright (c) 1989-2000 Microsoft Corporation
11 This module implements the dirent support routines for Cdfs.
13 Directories on a CD consist of a number of contiguous sectors on
14 the disk. File descriptors consist of one or more directory entries
15 (dirents) within a directory. Files may contain version numbers. If
16 present all like-named files will be ordered contiguously in the
17 directory by decreasing version numbers. We will only return the
18 first of these on a directory query unless the user explicitly
19 asks for version numbers. Finally dirents will not span sector
20 boundaries. Unused bytes at the end of a sector will be zero
23 Directory sector: Offset
25 +---------------------------------------------------------------+
27 | foo;4 | foo;4 | foo;3 | hat | zebra | Zero|
29 | | final | single | | | |
30 | | extent | extent | | | |
31 +---------------------------------------------------------------+
35 - Position scan at known offset in directory. Dirent at this
36 offset must exist and is valid. Used when scanning a directory
37 from the beginning when the self entry is known to be valid.
38 Used when positioning at the first dirent for an open
39 file to scan the allocation information. Used when resuming
40 a directory enumeration from a valid directory entry.
42 - Position scan at known offset in directory. Dirent is known to
43 start at this position but must be checked for validity.
44 Used to read the self-directory entry.
46 - Move to the next dirent within a directory.
48 - Given a known starting dirent, collect all the dirents for
49 that file. Scan will finish positioned at the last dirent
50 for the file. We will accumulate the extent lengths to
51 find the size of the file.
53 - Given a known starting dirent, position the scan for the first
54 dirent of the following file. Used when not interested in
55 all of the details for the current file and are looking for
58 - Update a common dirent structure with the details of the on-disk
59 structure. This is used to smooth out the differences
61 - Build the filename (name and version strings) out of the stream
62 of bytes in the file name on disk. For Joliet disks we will have
63 to convert to little endian.
71 // The Bug check file id for this module
74 #define BugCheckFileId (CDFS_BUG_CHECK_DIRSUP)
83 // _In_ PIRP_CONTEXT IrpContext,
84 // _In_ PDIR_ENUM_CONTEXT DirContext
88 #define CdRawDirent(IC,DC) \
89 Add2Ptr( (DC)->Sector, (DC)->SectorOffset, PRAW_DIRENT )
92 // Local support routines
96 CdCheckRawDirentBounds (
97 _In_ PIRP_CONTEXT IrpContext
,
98 _In_ PDIRENT_ENUM_CONTEXT DirContext
103 _In_ PIRP_CONTEXT IrpContext
,
104 _In_ PRAW_DIRENT RawDirent
,
105 _Inout_ PDIRENT Dirent
109 #pragma alloc_text(PAGE, CdCheckForXAExtent)
110 #pragma alloc_text(PAGE, CdCheckRawDirentBounds)
111 #pragma alloc_text(PAGE, CdCleanupFileContext)
112 #pragma alloc_text(PAGE, CdFindFile)
113 #pragma alloc_text(PAGE, CdFindDirectory)
114 #pragma alloc_text(PAGE, CdFindFileByShortName)
115 #pragma alloc_text(PAGE, CdLookupDirent)
116 #pragma alloc_text(PAGE, CdLookupLastFileDirent)
117 #pragma alloc_text(PAGE, CdLookupNextDirent)
118 #pragma alloc_text(PAGE, CdLookupNextInitialFileDirent)
119 #pragma alloc_text(PAGE, CdUpdateDirentFromRawDirent)
120 #pragma alloc_text(PAGE, CdUpdateDirentName)
126 _In_ PIRP_CONTEXT IrpContext
,
128 _In_ ULONG DirentOffset
,
129 _Out_ PDIRENT_ENUM_CONTEXT DirContext
136 This routine is called to initiate a walk through a directory. We will
137 position ourselves in the directory at offset DirentOffset. We know that
138 a dirent begins at this boundary but may have to verify the dirent bounds.
139 We will call this routine when looking up the first entry of a known
140 file or verifying the self entry of a directory.
144 Fcb - Fcb for the directory being traversed.
146 DirentOffset - This is our target point in the directory. We will map the
147 page containing this entry and possibly verify the dirent bounds at
150 DirContext - This is the dirent context for this scan. We update it with
151 the location of the dirent we found. This structure has been initialized
152 outside of this call.
166 // Initialize the offset of the first dirent we want to map.
169 DirContext
->BaseOffset
= SectorTruncate( DirentOffset
);
170 BaseOffset
= DirContext
->BaseOffset
;
172 DirContext
->DataLength
= SECTOR_SIZE
;
174 DirContext
->SectorOffset
= SectorOffset( DirentOffset
);
177 // Truncate the data length if we are at the end of the file.
180 if (DirContext
->DataLength
> (Fcb
->FileSize
.QuadPart
- BaseOffset
)) {
182 DirContext
->DataLength
= (ULONG
) (Fcb
->FileSize
.QuadPart
- BaseOffset
);
186 // Now map the data at this offset.
189 CcMapData( Fcb
->FileObject
,
190 (PLARGE_INTEGER
) &BaseOffset
,
191 DirContext
->DataLength
,
194 &DirContext
->Sector
);
197 // Verify the dirent bounds.
200 DirContext
->NextDirentOffset
= CdCheckRawDirentBounds( IrpContext
,
209 _In_ PIRP_CONTEXT IrpContext
,
211 _In_ PDIRENT_ENUM_CONTEXT CurrentDirContext
,
212 _Inout_ PDIRENT_ENUM_CONTEXT NextDirContext
219 This routine is called to find the next dirent in the directory. The
220 current position is given and we look for the next. We leave the context
221 for the starting position untouched and update the context for the
222 dirent we found. The target context may already be initialized so we
223 may already have the sector in memory.
225 This routine will position the enumeration context for the next dirent and
226 verify the dirent bounds.
228 NOTE - This routine can be called with CurrentDirContext and NextDirContext
229 pointing to the same enumeration context.
233 Fcb - Fcb for the directory being traversed.
235 CurrentDirContext - This is the dirent context for this scan. We update
236 it with the location of the dirent we found. This is currently
237 pointing to a dirent location. The dirent bounds at this location
238 have already been verified.
240 NextDirContext - This is the dirent context to update with the dirent we
241 find. This may already point to a dirent so we need to check if
242 we are in the same sector and unmap any buffer as necessary.
244 This dirent is left in an indeterminant state if we don't find a dirent.
248 BOOLEAN - TRUE if we find a location for the next dirent, FALSE otherwise.
249 This routine can cause a raise if the directory is corrupt.
254 LONGLONG CurrentBaseOffset
= CurrentDirContext
->BaseOffset
;
257 BOOLEAN FoundDirent
= FALSE
;
262 // Check if a different sector is mapped. If so then move our target
263 // enumeration context to the same sector.
266 if ((CurrentDirContext
->BaseOffset
!= NextDirContext
->BaseOffset
) ||
267 (NextDirContext
->Bcb
== NULL
)) {
270 // Unpin the current target Bcb and map the next sector.
273 CdUnpinData( IrpContext
, &NextDirContext
->Bcb
);
275 CcMapData( Fcb
->FileObject
,
276 (PLARGE_INTEGER
) &CurrentBaseOffset
,
277 CurrentDirContext
->DataLength
,
279 &NextDirContext
->Bcb
,
280 &NextDirContext
->Sector
);
283 // Copy the data length and sector offset.
286 NextDirContext
->DataLength
= CurrentDirContext
->DataLength
;
287 NextDirContext
->BaseOffset
= CurrentDirContext
->BaseOffset
;
291 // Now move to the same offset in the sector.
294 NextDirContext
->SectorOffset
= CurrentDirContext
->SectorOffset
;
297 // If the value is zero then unmap the current sector and set up
298 // the base offset to the beginning of the next sector.
301 if (CurrentDirContext
->NextDirentOffset
== 0) {
303 CurrentBaseOffset
= NextDirContext
->BaseOffset
+ NextDirContext
->DataLength
;
306 // Unmap the current sector. We test the value of the Bcb in the
307 // loop below to see if we need to read in another sector.
310 CdUnpinData( IrpContext
, &NextDirContext
->Bcb
);
313 // There is another possible dirent in the current sector. Update the
314 // enumeration context to reflect this.
319 NextDirContext
->SectorOffset
+= CurrentDirContext
->NextDirentOffset
;
323 // Now loop until we find the next possible dirent or walk off the directory.
329 // If we don't currently have a sector mapped then map the
330 // directory at the current offset.
333 if (NextDirContext
->Bcb
== NULL
) {
335 TempUlong
= SECTOR_SIZE
;
337 if (TempUlong
> (ULONG
) (Fcb
->FileSize
.QuadPart
- CurrentBaseOffset
)) {
339 TempUlong
= (ULONG
) (Fcb
->FileSize
.QuadPart
- CurrentBaseOffset
);
342 // If the length is zero then there is no dirent.
345 if (TempUlong
== 0) {
351 CcMapData( Fcb
->FileObject
,
352 (PLARGE_INTEGER
) &CurrentBaseOffset
,
355 &NextDirContext
->Bcb
,
356 &NextDirContext
->Sector
);
358 NextDirContext
->BaseOffset
= (ULONG
) CurrentBaseOffset
;
359 NextDirContext
->SectorOffset
= 0;
360 NextDirContext
->DataLength
= TempUlong
;
364 // The CDFS spec allows for sectors in a directory to contain all zeroes.
365 // In this case we need to move to the next sector. So look at the
366 // current potential dirent for a zero length. Move to the next
367 // dirent if length is zero.
370 if (*((PCHAR
) CdRawDirent( IrpContext
, NextDirContext
)) != 0) {
376 CurrentBaseOffset
= NextDirContext
->BaseOffset
+ NextDirContext
->DataLength
;
377 CdUnpinData( IrpContext
, &NextDirContext
->Bcb
);
381 // Check the dirent bounds if we found a dirent.
386 NextDirContext
->NextDirentOffset
= CdCheckRawDirentBounds( IrpContext
,
394 _At_(Dirent
->CdTime
, _Post_notnull_
)\f
396 CdUpdateDirentFromRawDirent (
397 _In_ PIRP_CONTEXT IrpContext
,
399 _In_ PDIRENT_ENUM_CONTEXT DirContext
,
400 _Inout_ PDIRENT Dirent
407 This routine is called to safely copy the data from the dirent on disk
408 to the in-memory dirent. The fields on disk are unaligned so we
409 need to safely copy them to our structure.
413 Fcb - Fcb for the directory being scanned.
415 DirContext - Enumeration context for the raw disk dirent.
417 Dirent - In-memory dirent to update.
426 PRAW_DIRENT RawDirent
= CdRawDirent( IrpContext
, DirContext
);
430 UNREFERENCED_PARAMETER( Fcb
);
433 // Clear all of the current state flags except the flag indicating that
434 // we allocated a name string.
437 ClearFlag( Dirent
->Flags
, DIRENT_FLAG_NOT_PERSISTENT
);
440 // The dirent offset is the sum of the start of the sector and the
444 Dirent
->DirentOffset
= DirContext
->BaseOffset
+ DirContext
->SectorOffset
;
447 // Copy the dirent length from the raw dirent.
450 Dirent
->DirentLength
= RawDirent
->DirLen
;
453 // The starting offset on disk is computed by finding the starting
454 // logical block and stepping over the Xar block.
457 CopyUchar4( &Dirent
->StartingOffset
, RawDirent
->FileLoc
);
459 Dirent
->StartingOffset
+= RawDirent
->XarLen
;
462 // Do a safe copy to get the data length.
465 CopyUchar4( &Dirent
->DataLength
, RawDirent
->DataLen
);
468 // Save a pointer to the time stamps.
471 Dirent
->CdTime
= (PCHAR
)RawDirent
->RecordTime
;
474 // Copy the dirent flags.
477 Dirent
->DirentFlags
= CdRawDirentFlags( IrpContext
, RawDirent
);
480 // For both the file unit and interleave skip we want to take the
481 // logical block count.
484 Dirent
->FileUnitSize
=
485 Dirent
->InterleaveGapSize
= 0;
487 if (RawDirent
->IntLeaveSize
!= 0) {
489 Dirent
->FileUnitSize
= RawDirent
->IntLeaveSize
;
490 Dirent
->InterleaveGapSize
= RawDirent
->IntLeaveSkip
;
494 // Get the name length and remember a pointer to the start of the
495 // name string. We don't do any processing on the name at this
498 // Check that the name length is non-zero.
501 if (RawDirent
->FileIdLen
== 0) {
503 CdRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
506 Dirent
->FileNameLen
= RawDirent
->FileIdLen
;
507 Dirent
->FileName
= (PCHAR
)RawDirent
->FileId
;
510 // If there are any remaining bytes at the end of the dirent then
511 // there may be a system use area. We protect ourselves from
512 // disks which don't pad the dirent entries correctly by using
513 // a fudge factor of one. All system use areas must have a length
514 // greater than one. Don't bother with the system use area
515 // if this is a directory.
518 Dirent
->XAAttributes
= 0;
519 Dirent
->XAFileNumber
= 0;
520 Dirent
->ExtentType
= Form1Data
;
521 Dirent
->SystemUseOffset
= 0;
523 if (!FlagOn( Dirent
->DirentFlags
, CD_ATTRIBUTE_DIRECTORY
) &&
524 (Dirent
->DirentLength
> ((FIELD_OFFSET( RAW_DIRENT
, FileId
) + Dirent
->FileNameLen
) + 1))) {
526 Dirent
->SystemUseOffset
= WordAlign( FIELD_OFFSET( RAW_DIRENT
, FileId
) + Dirent
->FileNameLen
);
535 _In_ PIRP_CONTEXT IrpContext
,
536 _Inout_ PDIRENT Dirent
,
537 _In_ ULONG IgnoreCase
544 This routine is called to update the name in the dirent with the name
545 from the disk. We will look for the special case of the self and
546 parent entries and also construct the Unicode name for the Joliet disk
547 in order to work around the BigEndian on-disk structure.
551 Dirent - Pointer to the in-memory dirent structure.
553 IgnoreCase - TRUE if we should build the upcased version. Otherwise we
554 use the exact case name.
563 UCHAR DirectoryValue
;
571 // Check if this is a self or parent entry. There is no version number
572 // in these cases. We use a fixed string for these.
574 // Self-Entry - Length is 1, value is 0.
575 // Parent-Entry - Length is 1, value is 1.
578 if ((Dirent
->FileNameLen
== 1) &&
579 FlagOn( Dirent
->DirentFlags
, CD_ATTRIBUTE_DIRECTORY
)) {
581 DirectoryValue
= *((PCHAR
) Dirent
->FileName
);
583 if ((DirectoryValue
== 0) || (DirectoryValue
== 1)) {
586 // We should not have allocated a name by the time we see these cases.
587 // If we have, this means that the image is in violation of ISO 9660 7.6.2,
588 // which states that the ./.. entries must be the first two in the directory.
591 if (FlagOn( Dirent
->Flags
, DIRENT_FLAG_ALLOC_BUFFER
)) {
593 CdRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
597 // Now use one of the hard coded directory names.
600 Dirent
->CdFileName
.FileName
= CdUnicodeDirectoryNames
[DirectoryValue
];
603 // Show that there is no version number.
606 Dirent
->CdFileName
.VersionString
.Length
= 0;
609 // The case name is the same as the exact name.
612 Dirent
->CdCaseFileName
= Dirent
->CdFileName
;
615 // Mark this as a constant value entry.
618 SetFlag( Dirent
->Flags
, DIRENT_FLAG_CONSTANT_ENTRY
);
629 // Mark this as a non-constant value entry.
632 ClearFlag( Dirent
->Flags
, DIRENT_FLAG_CONSTANT_ENTRY
);
635 // Compute how large a buffer we will need. If this is an ignore
636 // case operation then we will want a double size buffer. If the disk is not
637 // a Joliet disk then we might need two bytes for each byte in the name.
640 Length
= Dirent
->FileNameLen
;
647 if (!FlagOn( IrpContext
->Vcb
->VcbState
, VCB_STATE_JOLIET
)) {
649 Length
*= sizeof( WCHAR
);
653 // Now decide if we need to allocate a new buffer. We will if
654 // this name won't fit in the embedded name buffer and it is
655 // larger than the current allocated buffer. We always use the
656 // allocated buffer if present.
658 // If we haven't allocated a buffer then use the embedded buffer if the data
659 // will fit. This is the typical case.
662 if (!FlagOn( Dirent
->Flags
, DIRENT_FLAG_ALLOC_BUFFER
) &&
663 (Length
<= sizeof( Dirent
->NameBuffer
))) {
665 Dirent
->CdFileName
.FileName
.MaximumLength
= sizeof( Dirent
->NameBuffer
);
666 Dirent
->CdFileName
.FileName
.Buffer
= Dirent
->NameBuffer
;
671 // We need to use an allocated buffer. Check if the current buffer
675 if (Length
> Dirent
->CdFileName
.FileName
.MaximumLength
) {
678 // Free any allocated buffer.
681 if (FlagOn( Dirent
->Flags
, DIRENT_FLAG_ALLOC_BUFFER
)) {
683 CdFreePool( &Dirent
->CdFileName
.FileName
.Buffer
);
684 ClearFlag( Dirent
->Flags
, DIRENT_FLAG_ALLOC_BUFFER
);
687 Dirent
->CdFileName
.FileName
.Buffer
= FsRtlAllocatePoolWithTag( CdPagedPool
,
691 SetFlag( Dirent
->Flags
, DIRENT_FLAG_ALLOC_BUFFER
);
693 Dirent
->CdFileName
.FileName
.MaximumLength
= (USHORT
) Length
;
698 // We now have a buffer for the name. We need to either convert the on-disk bigendian
699 // to little endian or covert the name to Unicode.
702 if (!FlagOn( IrpContext
->Vcb
->VcbState
, VCB_STATE_JOLIET
)) {
704 Status
= RtlOemToUnicodeN( Dirent
->CdFileName
.FileName
.Buffer
,
705 Dirent
->CdFileName
.FileName
.MaximumLength
,
708 Dirent
->FileNameLen
);
710 __analysis_assert( Status
== STATUS_SUCCESS
);
711 NT_ASSERT( Status
== STATUS_SUCCESS
);
712 Dirent
->CdFileName
.FileName
.Length
= (USHORT
) Length
;
717 // Convert this string to little endian.
720 CdConvertBigToLittleEndian( IrpContext
,
723 (PCHAR
) Dirent
->CdFileName
.FileName
.Buffer
);
725 Dirent
->CdFileName
.FileName
.Length
= (USHORT
) Dirent
->FileNameLen
;
729 // Split the name into name and version strings.
732 CdConvertNameToCdName( IrpContext
,
733 &Dirent
->CdFileName
);
736 // The name length better be non-zero.
739 if (Dirent
->CdFileName
.FileName
.Length
== 0) {
741 CdRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
745 // If the filename ends with a period then back up one character.
748 if (Dirent
->CdFileName
.FileName
.Buffer
[(Dirent
->CdFileName
.FileName
.Length
- sizeof( WCHAR
)) / 2] == L
'.') {
751 // Slide the version string down.
754 if (Dirent
->CdFileName
.VersionString
.Length
!= 0) {
759 // Start from the position currently containing the separator.
762 NewVersion
= Add2Ptr( Dirent
->CdFileName
.FileName
.Buffer
,
763 Dirent
->CdFileName
.FileName
.Length
,
767 // Now overwrite the period.
770 RtlMoveMemory( NewVersion
- 1,
772 Dirent
->CdFileName
.VersionString
.Length
+ sizeof( WCHAR
));
775 // Now point to the new version string.
778 Dirent
->CdFileName
.VersionString
.Buffer
= NewVersion
;
782 // Shrink the filename length.
785 Dirent
->CdFileName
.FileName
.Length
-= sizeof( WCHAR
);
788 if (!CdIsLegalName( IrpContext
, &Dirent
->CdFileName
.FileName
)) {
790 CdRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
794 // If this an exact case operation then use the filename exactly.
799 Dirent
->CdCaseFileName
= Dirent
->CdFileName
;
802 // Otherwise perform our upcase operation. We already have guaranteed the buffers are
808 Dirent
->CdCaseFileName
.FileName
.Buffer
= Add2Ptr( Dirent
->CdFileName
.FileName
.Buffer
,
809 Dirent
->CdFileName
.FileName
.MaximumLength
/ 2,
812 Dirent
->CdCaseFileName
.FileName
.MaximumLength
= Dirent
->CdFileName
.FileName
.MaximumLength
/ 2;
814 CdUpcaseName( IrpContext
,
816 &Dirent
->CdCaseFileName
);
823 _Success_(return != FALSE
) BOOLEAN
825 _In_ PIRP_CONTEXT IrpContext
,
828 _In_ BOOLEAN IgnoreCase
,
829 _Inout_ PFILE_ENUM_CONTEXT FileContext
,
830 _Out_ PCD_NAME
*MatchingName
837 This routine is called to search a dirctory for a file matching the input
838 name. This name has been upcased at this point if this a case-insensitive
839 search. The name has been separated into separate name and version strings.
840 We look for an exact match in the name and only consider the version if
841 there is a version specified in the search name.
845 Fcb - Fcb for the directory being scanned.
847 Name - Name to search for.
849 IgnoreCase - Indicates the case of the search.
851 FileContext - File context to use for the search. This has already been
854 MatchingName - Pointer to buffer containing matching name. We need this
855 in case we don't match the name in the directory but match the
860 BOOLEAN - TRUE if matching entry is found, FALSE otherwise.
866 ULONG ShortNameDirentOffset
;
868 BOOLEAN Found
= FALSE
;
873 // Make sure there is a stream file for this Fcb.
876 CdVerifyOrCreateDirStreamFile( IrpContext
, Fcb
);
879 // Check to see whether we need to check for a possible short name.
882 ShortNameDirentOffset
= CdShortNameDirentOffset( IrpContext
, &Name
->FileName
);
885 // Position ourselves at the first entry.
888 CdLookupInitialFileDirent( IrpContext
, Fcb
, FileContext
, Fcb
->StreamOffset
);
891 // Loop while there are more entries in this directory.
896 Dirent
= &FileContext
->InitialDirent
->Dirent
;
899 // We only consider files which don't have the associated bit set.
900 // We also only look for files. All directories would already
904 if (!FlagOn( Dirent
->DirentFlags
, CD_ATTRIBUTE_ASSOC
| CD_ATTRIBUTE_DIRECTORY
)) {
907 // Update the name in the current dirent.
910 CdUpdateDirentName( IrpContext
, Dirent
, IgnoreCase
);
913 // Don't bother with constant entries.
916 if (FlagOn( Dirent
->Flags
, DIRENT_FLAG_CONSTANT_ENTRY
)) {
922 // Now check whether we have a name match.
923 // We exit the loop if we have a match.
926 if (CdIsNameInExpression( IrpContext
,
927 &Dirent
->CdCaseFileName
,
932 *MatchingName
= &Dirent
->CdCaseFileName
;
938 // The names didn't match. If the input name is a possible short
939 // name and we are at the correct offset in the directory then
940 // check if the short names match.
943 if (((Dirent
->DirentOffset
>> SHORT_NAME_SHIFT
) == ShortNameDirentOffset
) &&
944 (Name
->VersionString
.Length
== 0) &&
945 !CdIs8dot3Name( IrpContext
,
946 Dirent
->CdFileName
.FileName
)) {
949 // Create the short name and check for a match.
952 CdGenerate8dot3Name( IrpContext
,
953 &Dirent
->CdCaseFileName
.FileName
,
954 Dirent
->DirentOffset
,
955 FileContext
->ShortName
.FileName
.Buffer
,
956 &FileContext
->ShortName
.FileName
.Length
);
959 // Now check whether we have a name match.
960 // We exit the loop if we have a match.
963 if (CdIsNameInExpression( IrpContext
,
964 &FileContext
->ShortName
,
969 *MatchingName
= &FileContext
->ShortName
,
977 // Go to the next initial dirent for a file.
980 } while (CdLookupNextInitialFileDirent( IrpContext
, Fcb
, FileContext
));
983 // If we find the file then collect all of the dirents.
988 CdLookupLastFileDirent( IrpContext
, Fcb
, FileContext
);
998 _In_ PIRP_CONTEXT IrpContext
,
1001 _In_ BOOLEAN IgnoreCase
,
1002 _Inout_ PFILE_ENUM_CONTEXT FileContext
1007 Routine Description:
1009 This routine is called to search a dirctory for a directory matching the input
1010 name. This name has been upcased at this point if this a case-insensitive
1011 search. We look for an exact match in the name and do not look for shortname
1016 Fcb - Fcb for the directory being scanned.
1018 Name - Name to search for.
1020 IgnoreCase - Indicates the case of the search.
1022 FileContext - File context to use for the search. This has already been
1027 BOOLEAN - TRUE if matching entry is found, FALSE otherwise.
1034 BOOLEAN Found
= FALSE
;
1039 // Make sure there is a stream file for this Fcb.
1042 CdVerifyOrCreateDirStreamFile( IrpContext
, Fcb
);
1045 // Position ourselves at the first entry.
1048 CdLookupInitialFileDirent( IrpContext
, Fcb
, FileContext
, Fcb
->StreamOffset
);
1051 // Loop while there are more entries in this directory.
1056 Dirent
= &FileContext
->InitialDirent
->Dirent
;
1059 // We only look for directories. Directories cannot have the
1060 // associated bit set.
1063 if (FlagOn( Dirent
->DirentFlags
, CD_ATTRIBUTE_DIRECTORY
)) {
1066 // Update the name in the current dirent.
1069 CdUpdateDirentName( IrpContext
, Dirent
, IgnoreCase
);
1072 // Don't bother with constant entries.
1075 if (FlagOn( Dirent
->Flags
, DIRENT_FLAG_CONSTANT_ENTRY
)) {
1081 // Now check whether we have a name match.
1082 // We exit the loop if we have a match.
1085 if (CdIsNameInExpression( IrpContext
,
1086 &Dirent
->CdCaseFileName
,
1097 // Go to the next initial dirent.
1100 } while (CdLookupNextInitialFileDirent( IrpContext
, Fcb
, FileContext
));
1106 _At_(FileContext
->ShortName
.FileName
.MaximumLength
, _In_range_(>=, BYTE_COUNT_8_DOT_3
))
1108 CdFindFileByShortName (
1109 _In_ PIRP_CONTEXT IrpContext
,
1112 _In_ BOOLEAN IgnoreCase
,
1113 _In_ ULONG ShortNameDirentOffset
,
1114 _Inout_ PFILE_ENUM_CONTEXT FileContext
1119 Routine Description:
1121 This routine is called to find the file name entry whose short name
1122 is defined by the input DirentOffset. The dirent offset here is
1123 multiplied by 32 and we look for the dirent begins in this 32 byte offset in
1124 directory. The minimum dirent length is 34 so we are guaranteed that only
1125 one dirent can begin in each 32 byte block in the directory.
1129 Fcb - Fcb for the directory being scanned.
1131 Name - Name we are trying to match. We know this contains the tilde
1132 character followed by decimal characters.
1134 IgnoreCase - Indicates whether we need to upcase the long name and
1135 generated short name.
1137 ShortNameDirentOffset - This is the shifted value for the offset of the
1138 name in the directory.
1140 FileContext - This is the initialized file context to use for the search.
1144 BOOLEAN - TRUE if a matching name was found, FALSE otherwise.
1149 BOOLEAN Found
= FALSE
;
1152 ULONG ThisShortNameDirentOffset
;
1157 // Make sure there is a stream file for this Fcb.
1160 CdVerifyOrCreateDirStreamFile( IrpContext
, Fcb
);
1163 // Position ourselves at the start of the directory and update
1167 CdLookupInitialFileDirent( IrpContext
, Fcb
, FileContext
, Fcb
->StreamOffset
);
1170 // Loop until we have found the entry or are beyond this dirent.
1176 // Compute the short name dirent offset for the current dirent.
1179 Dirent
= &FileContext
->InitialDirent
->Dirent
;
1180 ThisShortNameDirentOffset
= Dirent
->DirentOffset
>> SHORT_NAME_SHIFT
;
1183 // If beyond the target then exit.
1186 if (ThisShortNameDirentOffset
> ShortNameDirentOffset
) {
1192 // If equal to the target then check if we have a name match.
1193 // We will either match or fail here.
1196 if (ThisShortNameDirentOffset
== ShortNameDirentOffset
) {
1199 // If this is an associated file then get out.
1202 if (FlagOn( Dirent
->DirentFlags
, CD_ATTRIBUTE_ASSOC
)) {
1208 // Update the name in the dirent and check if it is not
1212 CdUpdateDirentName( IrpContext
, Dirent
, IgnoreCase
);
1214 if (CdIs8dot3Name( IrpContext
,
1215 Dirent
->CdFileName
.FileName
)) {
1221 // Generate the 8.3 name see if it matches our input name.
1224 CdGenerate8dot3Name( IrpContext
,
1225 &Dirent
->CdCaseFileName
.FileName
,
1226 Dirent
->DirentOffset
,
1227 FileContext
->ShortName
.FileName
.Buffer
,
1228 &FileContext
->ShortName
.FileName
.Length
);
1231 // Check if this name matches.
1234 if (CdIsNameInExpression( IrpContext
,
1236 &FileContext
->ShortName
,
1241 // Let our caller know we found an entry.
1248 // Break out of the loop.
1255 // Continue until there are no more entries.
1258 } while (CdLookupNextInitialFileDirent( IrpContext
, Fcb
, FileContext
));
1261 // If we find the file then collect all of the dirents.
1266 CdLookupLastFileDirent( IrpContext
, Fcb
, FileContext
);
1275 CdLookupNextInitialFileDirent (
1276 _In_ PIRP_CONTEXT IrpContext
,
1278 _Inout_ PFILE_ENUM_CONTEXT FileContext
1283 Routine Description:
1285 This routine is called to walk through the directory until we find the
1286 first possible dirent for file. We are positioned at some point described
1287 by the FileContext. We will walk through any remaing dirents for the
1288 current file until we find the first dirent for some subsequent file.
1290 We can be called when we have found just one dirent for a file or all
1291 of them. We first check the CurrentDirContext. In the typical
1292 single-extent case this is unused. Then we look to the InitialDirContext
1293 which must be initialized.
1295 This routine will save the initial DirContext to the PriorDirContext and
1296 clean up any existing DirContext for the Prior or Current positions in
1297 the enumeration context.
1301 Fcb - This is the directory to scan.
1303 FileContext - This is the file enumeration context. It is currently pointing
1304 at some file in the directory.
1311 PRAW_DIRENT RawDirent
;
1313 PDIRENT_ENUM_CONTEXT CurrentDirContext
;
1314 PDIRENT_ENUM_CONTEXT TargetDirContext
;
1315 PCOMPOUND_DIRENT TempDirent
;
1317 BOOLEAN FoundDirent
= FALSE
;
1318 BOOLEAN FoundLastDirent
;
1323 // Start by saving the initial dirent of the current file as the
1327 TempDirent
= FileContext
->PriorDirent
;
1328 FileContext
->PriorDirent
= FileContext
->InitialDirent
;
1329 FileContext
->InitialDirent
= TempDirent
;
1332 // We will use the initial dirent of the prior file unless the
1333 // previous search returned multiple extents.
1336 CurrentDirContext
= &FileContext
->PriorDirent
->DirContext
;
1338 if (FlagOn( FileContext
->Flags
, FILE_CONTEXT_MULTIPLE_DIRENTS
)) {
1340 CurrentDirContext
= &FileContext
->CurrentDirent
->DirContext
;
1344 // Clear all of the flags and file size for the next file.
1347 FileContext
->Flags
= 0;
1348 FileContext
->FileSize
= 0;
1350 FileContext
->ShortName
.FileName
.Length
= 0;
1353 // We always want to store the result into the updated initial dirent
1357 TargetDirContext
= &FileContext
->InitialDirent
->DirContext
;
1360 // Loop until we find the first dirent after the last dirent of the
1361 // current file. We may not be at the last dirent for the current file yet
1362 // so we may walk forward looking for the last and then find the
1363 // initial dirent for the next file after that.
1369 // Remember if the last dirent we visited was the last dirent for
1373 RawDirent
= CdRawDirent( IrpContext
, CurrentDirContext
);
1375 FoundLastDirent
= !FlagOn( CdRawDirentFlags( IrpContext
, RawDirent
), CD_ATTRIBUTE_MULTI
);
1378 // Try to find another dirent.
1381 FoundDirent
= CdLookupNextDirent( IrpContext
,
1387 // Exit the loop if no entry found.
1397 // Update the in-memory dirent.
1400 CdUpdateDirentFromRawDirent( IrpContext
,
1403 &FileContext
->InitialDirent
->Dirent
);
1406 // Exit the loop if we had the end for the previous file.
1409 if (FoundLastDirent
) {
1415 // Always use a single dirent from this point on.
1418 CurrentDirContext
= TargetDirContext
;
1426 CdLookupLastFileDirent (
1427 _In_ PIRP_CONTEXT IrpContext
,
1429 _In_ PFILE_ENUM_CONTEXT FileContext
1434 Routine Description:
1436 This routine is called when we've found the matching initial dirent for
1437 a file. Now we want to find all of the dirents for a file as well as
1438 compute the running total for the file size.
1440 We also go out to the system use area and check whether this is an
1441 XA sector. In that case we will compute the real file size.
1443 The dirent in the initial compound dirent has been updated from the
1444 raw dirent when this routine is called.
1448 Fcb - Directory containing the entries for the file.
1450 FileContext - Enumeration context for this search. It currently points
1451 to the first dirent of the file and the in-memory dirent has been
1456 None. This routine may raise STATUS_FILE_CORRUPT.
1461 XA_EXTENT_TYPE ExtentType
= Form1Data
;
1462 PCOMPOUND_DIRENT CurrentCompoundDirent
;
1463 PDIRENT CurrentDirent
= NULL
;
1465 BOOLEAN FirstPass
= TRUE
;
1466 BOOLEAN FoundDirent
;
1471 // The current dirent to look at is the initial dirent for the file.
1474 CurrentCompoundDirent
= FileContext
->InitialDirent
;
1477 // Loop until we reach the last dirent for the file.
1482 CurrentDirent
= &CurrentCompoundDirent
->Dirent
;
1485 // Check if this extent has XA sectors.
1488 if ((CurrentDirent
->SystemUseOffset
!= 0) &&
1489 FlagOn( Fcb
->Vcb
->VcbState
, VCB_STATE_CDXA
) &&
1490 CdCheckForXAExtent( IrpContext
,
1491 CdRawDirent( IrpContext
, &CurrentCompoundDirent
->DirContext
),
1495 // Any previous dirent must describe XA sectors as well.
1498 if (!FirstPass
&& (ExtentType
!= CurrentDirent
->ExtentType
)) {
1500 CdRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
1504 // If there are XA sectors then the data on the disk must
1505 // be correctly aligned on sectors and be an integral number of
1506 // sectors. Only an issue if the logical block size is not
1510 if (Fcb
->Vcb
->BlockSize
!= SECTOR_SIZE
) {
1513 // We will do the following checks.
1515 // Data must start on a sector boundary.
1516 // Data length must be integral number of sectors.
1519 if ((SectorBlockOffset( Fcb
->Vcb
, CurrentDirent
->StartingOffset
) != 0) ||
1520 (SectorBlockOffset( Fcb
->Vcb
, CurrentDirent
->DataLength
) != 0)) {
1522 CdRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
1526 // If interleaved then both the file unit and interleave
1527 // gap must be integral number of sectors.
1530 if ((CurrentDirent
->FileUnitSize
!= 0) &&
1531 ((SectorBlockOffset( Fcb
->Vcb
, CurrentDirent
->FileUnitSize
) != 0) ||
1532 (SectorBlockOffset( Fcb
->Vcb
, CurrentDirent
->InterleaveGapSize
) != 0))) {
1534 CdRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
1539 // If this is the first dirent then add the bytes for the RIFF
1545 FileContext
->FileSize
= sizeof( RIFF_HEADER
);
1549 // Add the size of the mode2-form2 sector for each sector
1553 FileContext
->FileSize
+= Int32x32To64( CurrentDirent
->DataLength
>> SECTOR_SHIFT
,
1559 // This extent does not have XA sectors. Any previous dirent
1560 // better not have XA sectors.
1563 if (!FirstPass
&& (ExtentType
!= CurrentDirent
->ExtentType
)) {
1565 CdRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
1569 // Add these bytes to the file size.
1572 FileContext
->FileSize
+= CurrentDirent
->DataLength
;
1576 // If we are at the last dirent then exit.
1579 if (!FlagOn( CurrentDirent
->DirentFlags
, CD_ATTRIBUTE_MULTI
)) {
1585 // Remember the extent type of the current extent.
1588 ExtentType
= CurrentDirent
->ExtentType
;
1591 // Look for the next dirent of the file.
1594 FoundDirent
= CdLookupNextDirent( IrpContext
,
1596 &CurrentCompoundDirent
->DirContext
,
1597 &FileContext
->CurrentDirent
->DirContext
);
1600 // If we didn't find the entry then this is a corrupt directory.
1605 CdRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
1609 // Remember the dirent we just found.
1612 CurrentCompoundDirent
= FileContext
->CurrentDirent
;
1616 // Look up all of the dirent information for the given dirent.
1619 CdUpdateDirentFromRawDirent( IrpContext
,
1621 &CurrentCompoundDirent
->DirContext
,
1622 &CurrentCompoundDirent
->Dirent
);
1625 // Set flag to show there were multiple extents.
1628 SetFlag( FileContext
->Flags
, FILE_CONTEXT_MULTIPLE_DIRENTS
);
1636 CdCleanupFileContext (
1637 _In_ PIRP_CONTEXT IrpContext
,
1638 _In_ PFILE_ENUM_CONTEXT FileContext
1643 Routine Description:
1645 This routine is called to cleanup the enumeration context for a file
1646 search in a directory. We will unpin any remaining Bcbs and free
1647 any allocated buffers.
1651 FileContext - Enumeration context for the file search.
1660 PCOMPOUND_DIRENT CurrentCompoundDirent
;
1665 UNREFERENCED_PARAMETER( IrpContext
);
1668 // Cleanup the individual compound dirents.
1673 CurrentCompoundDirent
= &FileContext
->Dirents
[ Count
];
1674 CdCleanupDirContext( IrpContext
, &CurrentCompoundDirent
->DirContext
);
1675 CdCleanupDirent( IrpContext
, &CurrentCompoundDirent
->Dirent
);
1684 // Local support routine
1688 CdCheckRawDirentBounds (
1689 _In_ PIRP_CONTEXT IrpContext
,
1690 _In_ PDIRENT_ENUM_CONTEXT DirContext
1695 Routine Description:
1697 This routine takes a Dirent enumeration context and computes the offset
1698 to the next dirent. A non-zero value indicates the offset within this
1699 sector. A zero value indicates to move to the next sector. If the
1700 current dirent does not fit within the sector then we will raise
1705 DirContext - Enumeration context indicating the current position in
1710 ULONG - Offset to the next dirent in this sector or zero if the
1711 next dirent is in the next sector.
1713 This routine will raise on a dirent which does not fit into the
1714 described data buffer.
1719 ULONG NextDirentOffset
;
1720 PRAW_DIRENT RawDirent
;
1724 UNREFERENCED_PARAMETER( IrpContext
);
1727 // We should always have at least a byte still available in the
1731 NT_ASSERT( (DirContext
->DataLength
- DirContext
->SectorOffset
) >= 1 );
1734 // Get a pointer to the current dirent.
1737 RawDirent
= CdRawDirent( IrpContext
, DirContext
);
1740 // If the dirent length is non-zero then look at the current dirent.
1743 if (RawDirent
->DirLen
!= 0) {
1746 // Check the following bound for the dirent length.
1748 // - Fits in the available bytes in the sector.
1749 // - Is at least the minimal dirent size.
1750 // - Is large enough to hold the file name.
1753 if ((RawDirent
->DirLen
> (DirContext
->DataLength
- DirContext
->SectorOffset
)) ||
1754 (RawDirent
->DirLen
< MIN_RAW_DIRENT_LEN
) ||
1755 (RawDirent
->DirLen
< (MIN_RAW_DIRENT_LEN
- 1 + RawDirent
->FileIdLen
))) {
1757 CdRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
1761 // Copy the dirent length field.
1764 NextDirentOffset
= RawDirent
->DirLen
;
1767 // If we are exactly at the next sector then tell our caller by
1771 if (NextDirentOffset
== (DirContext
->DataLength
- DirContext
->SectorOffset
)) {
1773 NextDirentOffset
= 0;
1778 NextDirentOffset
= 0;
1781 return NextDirentOffset
;
1786 // Local support routine
1790 CdCheckForXAExtent (
1791 _In_ PIRP_CONTEXT IrpContext
,
1792 _In_ PRAW_DIRENT RawDirent
,
1793 _Inout_ PDIRENT Dirent
1798 Routine Description:
1800 This routine is called to scan through the system use area to test if
1801 the current dirent has the XA bit set. The bit in the in-memory
1802 dirent will be set as appropriate.
1806 RawDirent - Pointer to the on-disk dirent.
1808 Dirent - Pointer to the in-memory dirent. We will update this with the
1809 appropriate XA flag.
1813 XA_EXTENT_TYPE - Type of physical extent for this on disk dirent.
1818 XA_EXTENT_TYPE ExtentType
= Form1Data
;
1819 PSYSTEM_USE_XA SystemUseArea
;
1823 UNREFERENCED_PARAMETER( IrpContext
);
1826 // Check if there is enough space for the XA system use area.
1829 if (Dirent
->DirentLength
- Dirent
->SystemUseOffset
>= sizeof( SYSTEM_USE_XA
)) {
1831 SystemUseArea
= Add2Ptr( RawDirent
, Dirent
->SystemUseOffset
, PSYSTEM_USE_XA
);
1834 // Check for a valid signature.
1837 if (SystemUseArea
->Signature
== SYSTEM_XA_SIGNATURE
) {
1840 // Check for an audio track.
1843 if (FlagOn( SystemUseArea
->Attributes
, SYSTEM_USE_XA_DA
)) {
1845 ExtentType
= CDAudio
;
1847 } else if (FlagOn( SystemUseArea
->Attributes
, SYSTEM_USE_XA_FORM2
)) {
1850 // Check for XA data. Note that a number of discs (video CDs)
1851 // have files marked as type XA Mode 2 Form 1 (2048 bytes of
1852 // user data), but actually record these sectors as Mode2 Form 2
1853 // (2352). We will fail to read these files, since for M2F1,
1854 // a normal read CD command is issued (as per SCSI specs).
1857 ExtentType
= Mode2Form2Data
;
1860 Dirent
->XAAttributes
= SystemUseArea
->Attributes
;
1861 Dirent
->XAFileNumber
= SystemUseArea
->FileNumber
;
1865 Dirent
->ExtentType
= ExtentType
;