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 IN OUT 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 OUT 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 indeterminate 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
,
395 CdUpdateDirentFromRawDirent (
396 IN PIRP_CONTEXT IrpContext
,
398 IN PDIRENT_ENUM_CONTEXT DirContext
,
399 IN OUT PDIRENT Dirent
406 This routine is called to safely copy the data from the dirent on disk
407 to the in-memory dirent. The fields on disk are unaligned so we
408 need to safely copy them to our structure.
412 Fcb - Fcb for the directory being scanned.
414 DirContext - Enumeration context for the raw disk dirent.
416 Dirent - In-memory dirent to update.
425 PRAW_DIRENT RawDirent
= CdRawDirent( IrpContext
, DirContext
);
430 // Clear all of the current state flags except the flag indicating that
431 // we allocated a name string.
434 ClearFlag( Dirent
->Flags
, DIRENT_FLAG_NOT_PERSISTENT
);
437 // The dirent offset is the sum of the start of the sector and the
441 Dirent
->DirentOffset
= DirContext
->BaseOffset
+ DirContext
->SectorOffset
;
444 // Copy the dirent length from the raw dirent.
447 Dirent
->DirentLength
= RawDirent
->DirLen
;
450 // The starting offset on disk is computed by finding the starting
451 // logical block and stepping over the Xar block.
454 CopyUchar4( &Dirent
->StartingOffset
, RawDirent
->FileLoc
);
456 Dirent
->StartingOffset
+= RawDirent
->XarLen
;
459 // Do a safe copy to get the data length.
462 CopyUchar4( &Dirent
->DataLength
, RawDirent
->DataLen
);
465 // Save a pointer to the time stamps.
468 Dirent
->CdTime
= (PCHAR
)RawDirent
->RecordTime
; /* ReactOS change: GCC "pointer targets in assignment differ in signedness" */
471 // Copy the dirent flags.
474 Dirent
->DirentFlags
= CdRawDirentFlags( IrpContext
, RawDirent
);
477 // For both the file unit and interleave skip we want to take the
478 // logical block count.
481 Dirent
->FileUnitSize
=
482 Dirent
->InterleaveGapSize
= 0;
484 if (RawDirent
->IntLeaveSize
!= 0) {
486 Dirent
->FileUnitSize
= RawDirent
->IntLeaveSize
;
487 Dirent
->InterleaveGapSize
= RawDirent
->IntLeaveSkip
;
491 // Get the name length and remember a pointer to the start of the
492 // name string. We don't do any processing on the name at this
495 // Check that the name length is non-zero.
498 if (RawDirent
->FileIdLen
== 0) {
500 CdRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
503 Dirent
->FileNameLen
= RawDirent
->FileIdLen
;
504 Dirent
->FileName
= (PCHAR
)RawDirent
->FileId
; /* ReactOS change: GCC "pointer targets in assignment differ in signedness" */
507 // If there are any remaining bytes at the end of the dirent then
508 // there may be a system use area. We protect ourselves from
509 // disks which don't pad the dirent entries correctly by using
510 // a fudge factor of one. All system use areas must have a length
511 // greater than one. Don't bother with the system use area
512 // if this is a directory.
515 Dirent
->XAAttributes
= 0;
516 Dirent
->XAFileNumber
= 0;
517 Dirent
->ExtentType
= Form1Data
;
518 Dirent
->SystemUseOffset
= 0;
520 if (!FlagOn( Dirent
->DirentFlags
, CD_ATTRIBUTE_DIRECTORY
) &&
521 (Dirent
->DirentLength
> ((FIELD_OFFSET( RAW_DIRENT
, FileId
) + Dirent
->FileNameLen
) + 1))) {
523 Dirent
->SystemUseOffset
= WordAlign( FIELD_OFFSET( RAW_DIRENT
, FileId
) + Dirent
->FileNameLen
);
532 IN PIRP_CONTEXT IrpContext
,
533 IN OUT PDIRENT Dirent
,
541 This routine is called to update the name in the dirent with the name
542 from the disk. We will look for the special case of the self and
543 parent entries and also construct the Unicode name for the Joliet disk
544 in order to work around the BigEndian on-disk structure.
548 Dirent - Pointer to the in-memory dirent structure.
550 IgnoreCase - TRUE if we should build the upcased version. Otherwise we
551 use the exact case name.
560 UCHAR DirectoryValue
;
568 // Check if this is a self or parent entry. There is no version number
569 // in these cases. We use a fixed string for these.
571 // Self-Entry - Length is 1, value is 0.
572 // Parent-Entry - Length is 1, value is 1.
575 if ((Dirent
->FileNameLen
== 1) &&
576 FlagOn( Dirent
->DirentFlags
, CD_ATTRIBUTE_DIRECTORY
)) {
578 DirectoryValue
= *((PCHAR
) Dirent
->FileName
);
580 if ((DirectoryValue
== 0) || (DirectoryValue
== 1)) {
583 // We should not have allocated a name by the time we see these cases.
584 // If we have, this means that the image is in violation of ISO 9660 7.6.2,
585 // which states that the ./.. entries must be the first two in the directory.
588 if (FlagOn( Dirent
->Flags
, DIRENT_FLAG_ALLOC_BUFFER
)) {
590 CdRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
594 // Now use one of the hard coded directory names.
597 Dirent
->CdFileName
.FileName
= CdUnicodeDirectoryNames
[DirectoryValue
];
600 // Show that there is no version number.
603 Dirent
->CdFileName
.VersionString
.Length
= 0;
606 // The case name is the same as the exact name.
609 Dirent
->CdCaseFileName
= Dirent
->CdFileName
;
612 // Mark this as a constant value entry.
615 SetFlag( Dirent
->Flags
, DIRENT_FLAG_CONSTANT_ENTRY
);
626 // Mark this as a non-constant value entry.
629 ClearFlag( Dirent
->Flags
, DIRENT_FLAG_CONSTANT_ENTRY
);
632 // Compute how large a buffer we will need. If this is an ignore
633 // case operation then we will want a double size buffer. If the disk is not
634 // a Joliet disk then we might need two bytes for each byte in the name.
637 Length
= Dirent
->FileNameLen
;
644 if (!FlagOn( IrpContext
->Vcb
->VcbState
, VCB_STATE_JOLIET
)) {
646 Length
*= sizeof( WCHAR
);
650 // Now decide if we need to allocate a new buffer. We will if
651 // this name won't fit in the embedded name buffer and it is
652 // larger than the current allocated buffer. We always use the
653 // allocated buffer if present.
655 // If we haven't allocated a buffer then use the embedded buffer if the data
656 // will fit. This is the typical case.
659 if (!FlagOn( Dirent
->Flags
, DIRENT_FLAG_ALLOC_BUFFER
) &&
660 (Length
<= sizeof( Dirent
->NameBuffer
))) {
662 Dirent
->CdFileName
.FileName
.MaximumLength
= sizeof( Dirent
->NameBuffer
);
663 Dirent
->CdFileName
.FileName
.Buffer
= Dirent
->NameBuffer
;
668 // We need to use an allocated buffer. Check if the current buffer
672 if (Length
> Dirent
->CdFileName
.FileName
.MaximumLength
) {
675 // Free any allocated buffer.
678 if (FlagOn( Dirent
->Flags
, DIRENT_FLAG_ALLOC_BUFFER
)) {
680 CdFreePool( &Dirent
->CdFileName
.FileName
.Buffer
);
681 ClearFlag( Dirent
->Flags
, DIRENT_FLAG_ALLOC_BUFFER
);
684 Dirent
->CdFileName
.FileName
.Buffer
= FsRtlAllocatePoolWithTag( CdPagedPool
,
688 SetFlag( Dirent
->Flags
, DIRENT_FLAG_ALLOC_BUFFER
);
690 Dirent
->CdFileName
.FileName
.MaximumLength
= (USHORT
) Length
;
695 // We now have a buffer for the name. We need to either convert the on-disk bigendian
696 // to little endian or covert the name to Unicode.
699 if (!FlagOn( IrpContext
->Vcb
->VcbState
, VCB_STATE_JOLIET
)) {
701 Status
= RtlOemToUnicodeN( Dirent
->CdFileName
.FileName
.Buffer
,
702 Dirent
->CdFileName
.FileName
.MaximumLength
,
705 Dirent
->FileNameLen
);
707 ASSERT( Status
== STATUS_SUCCESS
);
708 Dirent
->CdFileName
.FileName
.Length
= (USHORT
) Length
;
713 // Convert this string to little endian.
716 CdConvertBigToLittleEndian( IrpContext
,
719 (PCHAR
) Dirent
->CdFileName
.FileName
.Buffer
);
721 Dirent
->CdFileName
.FileName
.Length
= (USHORT
) Dirent
->FileNameLen
;
725 // Split the name into name and version strings.
728 CdConvertNameToCdName( IrpContext
,
729 &Dirent
->CdFileName
);
732 // The name length better be non-zero.
735 if (Dirent
->CdFileName
.FileName
.Length
== 0) {
737 CdRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
741 // If the filename ends with a period then back up one character.
744 if (Dirent
->CdFileName
.FileName
.Buffer
[(Dirent
->CdFileName
.FileName
.Length
- sizeof( WCHAR
)) / 2] == L
'.') {
747 // Slide the version string down.
750 if (Dirent
->CdFileName
.VersionString
.Length
!= 0) {
755 // Start from the position currently containing the separator.
758 NewVersion
= Add2Ptr( Dirent
->CdFileName
.FileName
.Buffer
,
759 Dirent
->CdFileName
.FileName
.Length
,
763 // Now overwrite the period.
766 RtlMoveMemory( NewVersion
- 1,
768 Dirent
->CdFileName
.VersionString
.Length
+ sizeof( WCHAR
));
771 // Now point to the new version string.
774 Dirent
->CdFileName
.VersionString
.Buffer
= NewVersion
;
778 // Shrink the filename length.
781 Dirent
->CdFileName
.FileName
.Length
-= sizeof( WCHAR
);
785 // If this an exact case operation then use the filename exactly.
790 Dirent
->CdCaseFileName
= Dirent
->CdFileName
;
793 // Otherwise perform our upcase operation. We already have guaranteed the buffers are
799 Dirent
->CdCaseFileName
.FileName
.Buffer
= Add2Ptr( Dirent
->CdFileName
.FileName
.Buffer
,
800 Dirent
->CdFileName
.FileName
.MaximumLength
/ 2,
803 Dirent
->CdCaseFileName
.FileName
.MaximumLength
= Dirent
->CdFileName
.FileName
.MaximumLength
/ 2;
805 CdUpcaseName( IrpContext
,
807 &Dirent
->CdCaseFileName
);
816 IN PIRP_CONTEXT IrpContext
,
819 IN BOOLEAN IgnoreCase
,
820 IN OUT PFILE_ENUM_CONTEXT FileContext
,
821 OUT PCD_NAME
*MatchingName
828 This routine is called to search a directory for a file matching the input
829 name. This name has been upcased at this point if this a case-insensitive
830 search. The name has been separated into separate name and version strings.
831 We look for an exact match in the name and only consider the version if
832 there is a version specified in the search name.
836 Fcb - Fcb for the directory being scanned.
838 Name - Name to search for.
840 IgnoreCase - Indicates the case of the search.
842 FileContext - File context to use for the search. This has already been
845 MatchingName - Pointer to buffer containing matching name. We need this
846 in case we don't match the name in the directory but match the
851 BOOLEAN - TRUE if matching entry is found, FALSE otherwise.
857 ULONG ShortNameDirentOffset
;
859 BOOLEAN Found
= FALSE
;
864 // Make sure there is a stream file for this Fcb.
867 if (Fcb
->FileObject
== NULL
) {
869 CdCreateInternalStream( IrpContext
, Fcb
->Vcb
, Fcb
);
873 // Check to see whether we need to check for a possible short name.
876 ShortNameDirentOffset
= CdShortNameDirentOffset( IrpContext
, &Name
->FileName
);
879 // Position ourselves at the first entry.
882 CdLookupInitialFileDirent( IrpContext
, Fcb
, FileContext
, Fcb
->StreamOffset
);
885 // Loop while there are more entries in this directory.
890 Dirent
= &FileContext
->InitialDirent
->Dirent
;
893 // We only consider files which don't have the associated bit set.
894 // We also only look for files. All directories would already
898 if (!FlagOn( Dirent
->DirentFlags
, CD_ATTRIBUTE_ASSOC
| CD_ATTRIBUTE_DIRECTORY
)) {
901 // Update the name in the current dirent.
904 CdUpdateDirentName( IrpContext
, Dirent
, IgnoreCase
);
907 // Don't bother with constant entries.
910 if (FlagOn( Dirent
->Flags
, DIRENT_FLAG_CONSTANT_ENTRY
)) {
916 // Now check whether we have a name match.
917 // We exit the loop if we have a match.
920 if (CdIsNameInExpression( IrpContext
,
921 &Dirent
->CdCaseFileName
,
926 *MatchingName
= &Dirent
->CdCaseFileName
;
932 // The names didn't match. If the input name is a possible short
933 // name and we are at the correct offset in the directory then
934 // check if the short names match.
937 if (((Dirent
->DirentOffset
>> SHORT_NAME_SHIFT
) == ShortNameDirentOffset
) &&
938 (Name
->VersionString
.Length
== 0) &&
939 !CdIs8dot3Name( IrpContext
,
940 Dirent
->CdFileName
.FileName
)) {
943 // Create the short name and check for a match.
946 CdGenerate8dot3Name( IrpContext
,
947 &Dirent
->CdCaseFileName
.FileName
,
948 Dirent
->DirentOffset
,
949 FileContext
->ShortName
.FileName
.Buffer
,
950 &FileContext
->ShortName
.FileName
.Length
);
953 // Now check whether we have a name match.
954 // We exit the loop if we have a match.
957 if (CdIsNameInExpression( IrpContext
,
958 &FileContext
->ShortName
,
963 *MatchingName
= &FileContext
->ShortName
,
971 // Go to the next initial dirent for a file.
974 } while (CdLookupNextInitialFileDirent( IrpContext
, Fcb
, FileContext
));
977 // If we find the file then collect all of the dirents.
982 CdLookupLastFileDirent( IrpContext
, Fcb
, FileContext
);
992 IN PIRP_CONTEXT IrpContext
,
995 IN BOOLEAN IgnoreCase
,
996 IN OUT PFILE_ENUM_CONTEXT FileContext
1001 Routine Description:
1003 This routine is called to search a directory for a directory matching the input
1004 name. This name has been upcased at this point if this a case-insensitive
1005 search. We look for an exact match in the name and do not look for shortname
1010 Fcb - Fcb for the directory being scanned.
1012 Name - Name to search for.
1014 IgnoreCase - Indicates the case of the search.
1016 FileContext - File context to use for the search. This has already been
1021 BOOLEAN - TRUE if matching entry is found, FALSE otherwise.
1028 BOOLEAN Found
= FALSE
;
1033 // Make sure there is a stream file for this Fcb.
1036 if (Fcb
->FileObject
== NULL
) {
1038 CdCreateInternalStream( IrpContext
, Fcb
->Vcb
, Fcb
);
1042 // Position ourselves at the first entry.
1045 CdLookupInitialFileDirent( IrpContext
, Fcb
, FileContext
, Fcb
->StreamOffset
);
1048 // Loop while there are more entries in this directory.
1053 Dirent
= &FileContext
->InitialDirent
->Dirent
;
1056 // We only look for directories. Directories cannot have the
1057 // associated bit set.
1060 if (FlagOn( Dirent
->DirentFlags
, CD_ATTRIBUTE_DIRECTORY
)) {
1063 // Update the name in the current dirent.
1066 CdUpdateDirentName( IrpContext
, Dirent
, IgnoreCase
);
1069 // Don't bother with constant entries.
1072 if (FlagOn( Dirent
->Flags
, DIRENT_FLAG_CONSTANT_ENTRY
)) {
1078 // Now check whether we have a name match.
1079 // We exit the loop if we have a match.
1082 if (CdIsNameInExpression( IrpContext
,
1083 &Dirent
->CdCaseFileName
,
1094 // Go to the next initial dirent.
1097 } while (CdLookupNextInitialFileDirent( IrpContext
, Fcb
, FileContext
));
1104 CdFindFileByShortName (
1105 IN PIRP_CONTEXT IrpContext
,
1108 IN BOOLEAN IgnoreCase
,
1109 IN ULONG ShortNameDirentOffset
,
1110 IN OUT PFILE_ENUM_CONTEXT FileContext
1115 Routine Description:
1117 This routine is called to find the file name entry whose short name
1118 is defined by the input DirentOffset. The dirent offset here is
1119 multiplied by 32 and we look for the dirent begins in this 32 byte offset in
1120 directory. The minimum dirent length is 34 so we are guaranteed that only
1121 one dirent can begin in each 32 byte block in the directory.
1125 Fcb - Fcb for the directory being scanned.
1127 Name - Name we are trying to match. We know this contains the tilde
1128 character followed by decimal characters.
1130 IgnoreCase - Indicates whether we need to upcase the long name and
1131 generated short name.
1133 ShortNameDirentOffset - This is the shifted value for the offset of the
1134 name in the directory.
1136 FileContext - This is the initialized file context to use for the search.
1140 BOOLEAN - TRUE if a matching name was found, FALSE otherwise.
1145 BOOLEAN Found
= FALSE
;
1148 ULONG ThisShortNameDirentOffset
;
1153 // Make sure there is a stream file for this Fcb.
1156 if (Fcb
->FileObject
== NULL
) {
1158 CdCreateInternalStream( IrpContext
, Fcb
->Vcb
, Fcb
);
1162 // Position ourselves at the start of the directory and update
1166 CdLookupInitialFileDirent( IrpContext
, Fcb
, FileContext
, Fcb
->StreamOffset
);
1169 // Loop until we have found the entry or are beyond this dirent.
1175 // Compute the short name dirent offset for the current dirent.
1178 Dirent
= &FileContext
->InitialDirent
->Dirent
;
1179 ThisShortNameDirentOffset
= Dirent
->DirentOffset
>> SHORT_NAME_SHIFT
;
1182 // If beyond the target then exit.
1185 if (ThisShortNameDirentOffset
> ShortNameDirentOffset
) {
1191 // If equal to the target then check if we have a name match.
1192 // We will either match or fail here.
1195 if (ThisShortNameDirentOffset
== ShortNameDirentOffset
) {
1198 // If this is an associated file then get out.
1201 if (FlagOn( Dirent
->DirentFlags
, CD_ATTRIBUTE_ASSOC
)) {
1207 // Update the name in the dirent and check if it is not
1211 CdUpdateDirentName( IrpContext
, Dirent
, IgnoreCase
);
1213 if (CdIs8dot3Name( IrpContext
,
1214 Dirent
->CdFileName
.FileName
)) {
1220 // Generate the 8.3 name see if it matches our input name.
1223 CdGenerate8dot3Name( IrpContext
,
1224 &Dirent
->CdCaseFileName
.FileName
,
1225 Dirent
->DirentOffset
,
1226 FileContext
->ShortName
.FileName
.Buffer
,
1227 &FileContext
->ShortName
.FileName
.Length
);
1230 // Check if this name matches.
1233 if (CdIsNameInExpression( IrpContext
,
1235 &FileContext
->ShortName
,
1240 // Let our caller know we found an entry.
1247 // Break out of the loop.
1254 // Continue until there are no more entries.
1257 } while (CdLookupNextInitialFileDirent( IrpContext
, Fcb
, FileContext
));
1260 // If we find the file then collect all of the dirents.
1265 CdLookupLastFileDirent( IrpContext
, Fcb
, FileContext
);
1274 CdLookupNextInitialFileDirent (
1275 IN PIRP_CONTEXT IrpContext
,
1277 IN OUT PFILE_ENUM_CONTEXT FileContext
1282 Routine Description:
1284 This routine is called to walk through the directory until we find the
1285 first possible dirent for file. We are positioned at some point described
1286 by the FileContext. We will walk through any remaining dirents for the
1287 current file until we find the first dirent for some subsequent file.
1289 We can be called when we have found just one dirent for a file or all
1290 of them. We first check the CurrentDirContext. In the typical
1291 single-extent case this is unused. Then we look to the InitialDirContext
1292 which must be initialized.
1294 This routine will save the initial DirContext to the PriorDirContext and
1295 clean up any existing DirContext for the Prior or Current positions in
1296 the enumeration context.
1300 Fcb - This is the directory to scan.
1302 FileContext - This is the file enumeration context. It is currently pointing
1303 at some file in the directory.
1310 PRAW_DIRENT RawDirent
;
1312 PDIRENT_ENUM_CONTEXT CurrentDirContext
;
1313 PDIRENT_ENUM_CONTEXT TargetDirContext
;
1314 PCOMPOUND_DIRENT TempDirent
;
1316 BOOLEAN FoundDirent
= FALSE
;
1317 BOOLEAN FoundLastDirent
;
1322 // Start by saving the initial dirent of the current file as the
1326 TempDirent
= FileContext
->PriorDirent
;
1327 FileContext
->PriorDirent
= FileContext
->InitialDirent
;
1328 FileContext
->InitialDirent
= TempDirent
;
1331 // We will use the initial dirent of the prior file unless the
1332 // previous search returned multiple extents.
1335 CurrentDirContext
= &FileContext
->PriorDirent
->DirContext
;
1337 if (FlagOn( FileContext
->Flags
, FILE_CONTEXT_MULTIPLE_DIRENTS
)) {
1339 CurrentDirContext
= &FileContext
->CurrentDirent
->DirContext
;
1343 // Clear all of the flags and file size for the next file.
1346 FileContext
->Flags
= 0;
1347 FileContext
->FileSize
= 0;
1349 FileContext
->ShortName
.FileName
.Length
= 0;
1352 // We always want to store the result into the updated initial dirent
1356 TargetDirContext
= &FileContext
->InitialDirent
->DirContext
;
1359 // Loop until we find the first dirent after the last dirent of the
1360 // current file. We may not be at the last dirent for the current file yet
1361 // so we may walk forward looking for the last and then find the
1362 // initial dirent for the next file after that.
1368 // Remember if the last dirent we visited was the last dirent for
1372 RawDirent
= CdRawDirent( IrpContext
, CurrentDirContext
);
1374 FoundLastDirent
= !FlagOn( CdRawDirentFlags( IrpContext
, RawDirent
), CD_ATTRIBUTE_MULTI
);
1377 // Try to find another dirent.
1380 FoundDirent
= CdLookupNextDirent( IrpContext
,
1386 // Exit the loop if no entry found.
1396 // Update the in-memory dirent.
1399 CdUpdateDirentFromRawDirent( IrpContext
,
1402 &FileContext
->InitialDirent
->Dirent
);
1405 // Exit the loop if we had the end for the previous file.
1408 if (FoundLastDirent
) {
1414 // Always use a single dirent from this point on.
1417 CurrentDirContext
= TargetDirContext
;
1425 CdLookupLastFileDirent (
1426 IN PIRP_CONTEXT IrpContext
,
1428 IN PFILE_ENUM_CONTEXT FileContext
1433 Routine Description:
1435 This routine is called when we've found the matching initial dirent for
1436 a file. Now we want to find all of the dirents for a file as well as
1437 compute the running total for the file size.
1439 We also go out to the system use area and check whether this is an
1440 XA sector. In that case we will compute the real file size.
1442 The dirent in the initial compound dirent has been updated from the
1443 raw dirent when this routine is called.
1447 Fcb - Directory containing the entries for the file.
1449 FileContext - Enumeration context for this search. It currently points
1450 to the first dirent of the file and the in-memory dirent has been
1455 None. This routine may raise STATUS_FILE_CORRUPT.
1460 XA_EXTENT_TYPE ExtentType
= 0; /* ReactOS Change: GCC Uninit var */
1461 PCOMPOUND_DIRENT CurrentCompoundDirent
;
1462 PDIRENT CurrentDirent
;
1464 BOOLEAN FirstPass
= TRUE
;
1465 BOOLEAN FoundDirent
;
1470 // The current dirent to look at is the initial dirent for the file.
1473 CurrentCompoundDirent
= FileContext
->InitialDirent
;
1476 // Loop until we reach the last dirent for the file.
1481 CurrentDirent
= &CurrentCompoundDirent
->Dirent
;
1484 // Check if this extent has XA sectors.
1487 if ((CurrentDirent
->SystemUseOffset
!= 0) &&
1488 FlagOn( Fcb
->Vcb
->VcbState
, VCB_STATE_CDXA
) &&
1489 CdCheckForXAExtent( IrpContext
,
1490 CdRawDirent( IrpContext
, &CurrentCompoundDirent
->DirContext
),
1494 // Any previous dirent must describe XA sectors as well.
1497 if (!FirstPass
&& (ExtentType
!= CurrentDirent
->ExtentType
)) {
1499 CdRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
1503 // If there are XA sectors then the data on the disk must
1504 // be correctly aligned on sectors and be an integral number of
1505 // sectors. Only an issue if the logical block size is not
1509 if (Fcb
->Vcb
->BlockSize
!= SECTOR_SIZE
) {
1512 // We will do the following checks.
1514 // Data must start on a sector boundary.
1515 // Data length must be integral number of sectors.
1518 if ((SectorBlockOffset( Fcb
->Vcb
, CurrentDirent
->StartingOffset
) != 0) ||
1519 (SectorBlockOffset( Fcb
->Vcb
, CurrentDirent
->DataLength
) != 0)) {
1521 CdRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
1525 // If interleaved then both the file unit and interleave
1526 // gap must be integral number of sectors.
1529 if ((CurrentDirent
->FileUnitSize
!= 0) &&
1530 ((SectorBlockOffset( Fcb
->Vcb
, CurrentDirent
->FileUnitSize
) != 0) ||
1531 (SectorBlockOffset( Fcb
->Vcb
, CurrentDirent
->InterleaveGapSize
) != 0))) {
1533 CdRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
1538 // If this is the first dirent then add the bytes for the RIFF
1544 FileContext
->FileSize
= sizeof( RIFF_HEADER
);
1548 // Add the size of the mode2-form2 sector for each sector
1552 FileContext
->FileSize
+= Int32x32To64( CurrentDirent
->DataLength
>> SECTOR_SHIFT
,
1558 // This extent does not have XA sectors. Any previous dirent
1559 // better not have XA sectors.
1562 if (!FirstPass
&& (ExtentType
!= CurrentDirent
->ExtentType
)) {
1564 CdRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
1568 // Add these bytes to the file size.
1571 FileContext
->FileSize
+= CurrentDirent
->DataLength
;
1575 // If we are at the last dirent then exit.
1578 if (!FlagOn( CurrentDirent
->DirentFlags
, CD_ATTRIBUTE_MULTI
)) {
1584 // Remember the extent type of the current extent.
1587 ExtentType
= CurrentDirent
->ExtentType
;
1590 // Look for the next dirent of the file.
1593 FoundDirent
= CdLookupNextDirent( IrpContext
,
1595 &CurrentCompoundDirent
->DirContext
,
1596 &FileContext
->CurrentDirent
->DirContext
);
1599 // If we didn't find the entry then this is a corrupt directory.
1604 CdRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
1608 // Remember the dirent we just found.
1611 CurrentCompoundDirent
= FileContext
->CurrentDirent
;
1615 // Look up all of the dirent information for the given dirent.
1618 CdUpdateDirentFromRawDirent( IrpContext
,
1620 &CurrentCompoundDirent
->DirContext
,
1621 &CurrentCompoundDirent
->Dirent
);
1624 // Set flag to show there were multiple extents.
1627 SetFlag( FileContext
->Flags
, FILE_CONTEXT_MULTIPLE_DIRENTS
);
1635 CdCleanupFileContext (
1636 IN PIRP_CONTEXT IrpContext
,
1637 IN PFILE_ENUM_CONTEXT FileContext
1642 Routine Description:
1644 This routine is called to cleanup the enumeration context for a file
1645 search in a directory. We will unpin any remaining Bcbs and free
1646 any allocated buffers.
1650 FileContext - Enumeration context for the file search.
1659 PCOMPOUND_DIRENT CurrentCompoundDirent
;
1665 // Cleanup the individual compound dirents.
1670 CurrentCompoundDirent
= &FileContext
->Dirents
[ Count
];
1671 CdCleanupDirContext( IrpContext
, &CurrentCompoundDirent
->DirContext
);
1672 CdCleanupDirent( IrpContext
, &CurrentCompoundDirent
->Dirent
);
1681 // Local support routine
1685 CdCheckRawDirentBounds (
1686 IN PIRP_CONTEXT IrpContext
,
1687 IN PDIRENT_ENUM_CONTEXT DirContext
1692 Routine Description:
1694 This routine takes a Dirent enumeration context and computes the offset
1695 to the next dirent. A non-zero value indicates the offset within this
1696 sector. A zero value indicates to move to the next sector. If the
1697 current dirent does not fit within the sector then we will raise
1702 DirContext - Enumeration context indicating the current position in
1707 ULONG - Offset to the next dirent in this sector or zero if the
1708 next dirent is in the next sector.
1710 This routine will raise on a dirent which does not fit into the
1711 described data buffer.
1716 ULONG NextDirentOffset
;
1717 PRAW_DIRENT RawDirent
;
1722 // We should always have at least a byte still available in the
1726 ASSERT( (DirContext
->DataLength
- DirContext
->SectorOffset
) >= 1 );
1729 // Get a pointer to the current dirent.
1732 RawDirent
= CdRawDirent( IrpContext
, DirContext
);
1735 // If the dirent length is non-zero then look at the current dirent.
1738 if (RawDirent
->DirLen
!= 0) {
1741 // Check the following bound for the dirent length.
1743 // - Fits in the available bytes in the sector.
1744 // - Is at least the minimal dirent size.
1745 // - Is large enough to hold the file name.
1748 if ((RawDirent
->DirLen
> (DirContext
->DataLength
- DirContext
->SectorOffset
)) ||
1749 (RawDirent
->DirLen
< MIN_RAW_DIRENT_LEN
) ||
1750 (RawDirent
->DirLen
< (MIN_RAW_DIRENT_LEN
- 1 + RawDirent
->FileIdLen
))) {
1752 CdRaiseStatus( IrpContext
, STATUS_FILE_CORRUPT_ERROR
);
1756 // Copy the dirent length field.
1759 NextDirentOffset
= RawDirent
->DirLen
;
1762 // If we are exactly at the next sector then tell our caller by
1766 if (NextDirentOffset
== (DirContext
->DataLength
- DirContext
->SectorOffset
)) {
1768 NextDirentOffset
= 0;
1773 NextDirentOffset
= 0;
1776 return NextDirentOffset
;
1781 // Local support routine
1785 CdCheckForXAExtent (
1786 IN PIRP_CONTEXT IrpContext
,
1787 IN PRAW_DIRENT RawDirent
,
1788 IN OUT PDIRENT Dirent
1793 Routine Description:
1795 This routine is called to scan through the system use area to test if
1796 the current dirent has the XA bit set. The bit in the in-memory
1797 dirent will be set as appropriate.
1801 RawDirent - Pointer to the on-disk dirent.
1803 Dirent - Pointer to the in-memory dirent. We will update this with the
1804 appropriate XA flag.
1808 XA_EXTENT_TYPE - Type of physical extent for this on disk dirent.
1813 XA_EXTENT_TYPE ExtentType
= Form1Data
;
1814 PSYSTEM_USE_XA SystemUseArea
;
1819 // Check if there is enough space for the XA system use area.
1822 if (Dirent
->DirentLength
- Dirent
->SystemUseOffset
>= sizeof( SYSTEM_USE_XA
)) {
1824 SystemUseArea
= Add2Ptr( RawDirent
, Dirent
->SystemUseOffset
, PSYSTEM_USE_XA
);
1827 // Check for a valid signature.
1830 if (SystemUseArea
->Signature
== SYSTEM_XA_SIGNATURE
) {
1833 // Check for an audio track.
1836 if (FlagOn( SystemUseArea
->Attributes
, SYSTEM_USE_XA_DA
)) {
1838 ExtentType
= CDAudio
;
1840 } else if (FlagOn( SystemUseArea
->Attributes
, SYSTEM_USE_XA_FORM2
)) {
1843 // Check for XA data. Note that a number of discs (video CDs)
1844 // have files marked as type XA Mode 2 Form 1 (2048 bytes of
1845 // user data), but actually record these sectors as Mode2 Form 2
1846 // (2352). We will fail to read these files, since for M2F1,
1847 // a normal read CD command is issued (as per SCSI specs).
1850 ExtentType
= Mode2Form2Data
;
1853 Dirent
->XAAttributes
= SystemUseArea
->Attributes
;
1854 Dirent
->XAFileNumber
= SystemUseArea
->FileNumber
;
1858 Dirent
->ExtentType
= ExtentType
;