diff options
author | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
---|---|---|
committer | Adam <you@example.com> | 2020-05-17 05:51:50 +0200 |
commit | e611b132f9b8abe35b362e5870b74bce94a1e58e (patch) | |
tree | a5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/cdfs/dirsup.c | |
download | NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2 NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip |
Diffstat (limited to 'private/ntos/cdfs/dirsup.c')
-rw-r--r-- | private/ntos/cdfs/dirsup.c | 1835 |
1 files changed, 1835 insertions, 0 deletions
diff --git a/private/ntos/cdfs/dirsup.c b/private/ntos/cdfs/dirsup.c new file mode 100644 index 000000000..b10f61ca8 --- /dev/null +++ b/private/ntos/cdfs/dirsup.c @@ -0,0 +1,1835 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + DirSup.c + +Abstract: + + This module implements the dirent support routines for Cdfs. + + Directories on a CD consist of a number of contiguous sectors on + the disk. File descriptors consist of one or more directory entries + (dirents) within a directory. Files may contain version numbers. If + present all like-named files will be ordered contiguously in the + directory by decreasing version numbers. We will only return the + first of these on a directory query unless the user explicitly + asks for version numbers. Finally dirents will not span sector + boundaries. Unused bytes at the end of a sector will be zero + filled. + + Directory sector: Offset + 2048 + +---------------------------------------------------------------+ + | | | | | | | + | foo;4 | foo;4 | foo;3 | hat | zebra | Zero| + | | | | | | Fill| + | | final | single | | | | + | | extent | extent | | | | + +---------------------------------------------------------------+ + + Dirent operations: + + - Position scan at known offset in directory. Dirent at this + offset must exist and is valid. Used when scanning a directory + from the beginning when the self entry is known to be valid. + Used when positioning at the first dirent for an open + file to scan the allocation information. Used when resuming + a directory enumeration from a valid directory entry. + + - Position scan at known offset in directory. Dirent is known to + start at this position but must be checked for validity. + Used to read the self-directory entry. + + - Move to the next dirent within a directory. + + - Given a known starting dirent, collect all the dirents for + that file. Scan will finish positioned at the last dirent + for the file. We will accumulate the extent lengths to + find the size of the file. + + - Given a known starting dirent, position the scan for the first + dirent of the following file. Used when not interested in + all of the details for the current file and are looking for + the next file. + + - Update a common dirent structure with the details of the on-disk + structure. This is used to smooth out the differences + + - Build the filename (name and version strings) out of the stream + of bytes in the file name on disk. For Joliet disks we will have + to convert to little endian. + +Author: + + Brian Andrew [BrianAn] 01-July-1995 + +Revision History: + +--*/ + +#include "CdProcs.h" + +// +// The Bug check file id for this module +// + +#define BugCheckFileId (CDFS_BUG_CHECK_DIRSUP) + +// +// Local debug trace level +// + +#define Dbg (DEBUG_TRACE_DIRSUP) + +// +// Local macros +// + +// +// PRAW_DIRENT +// CdRawDirent ( +// IN PIRP_CONTEXT IrpContext, +// IN PDIR_ENUM_CONTEXT DirContext +// ); +// + +#define CdRawDirent(IC,DC) \ + Add2Ptr( (DC)->Sector, (DC)->SectorOffset, PRAW_DIRENT ) + +// +// Local support routines +// + +ULONG +CdCheckRawDirentBounds ( + IN PIRP_CONTEXT IrpContext, + IN PDIRENT_ENUM_CONTEXT DirContext + ); + +XA_EXTENT_TYPE +CdCheckForXAExtent ( + IN PIRP_CONTEXT IrpContext, + IN PRAW_DIRENT RawDirent, + IN OUT PDIRENT Dirent + ); + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, CdCheckForXAExtent) +#pragma alloc_text(PAGE, CdCheckRawDirentBounds) +#pragma alloc_text(PAGE, CdCleanupFileContext) +#pragma alloc_text(PAGE, CdFindFile) +#pragma alloc_text(PAGE, CdFindDirectory) +#pragma alloc_text(PAGE, CdFindFileByShortName) +#pragma alloc_text(PAGE, CdLookupDirent) +#pragma alloc_text(PAGE, CdLookupLastFileDirent) +#pragma alloc_text(PAGE, CdLookupNextDirent) +#pragma alloc_text(PAGE, CdLookupNextInitialFileDirent) +#pragma alloc_text(PAGE, CdUpdateDirentFromRawDirent) +#pragma alloc_text(PAGE, CdUpdateDirentName) +#endif + + +VOID +CdLookupDirent ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN ULONG DirentOffset, + OUT PDIRENT_ENUM_CONTEXT DirContext + ) + +/*++ + +Routine Description: + + This routine is called to initiate a walk through a directory. We will + position ourselves in the directory at offset DirentOffset. We know that + a dirent begins at this boundary but may have to verify the dirent bounds. + We will call this routine when looking up the first entry of a known + file or verifying the self entry of a directory. + +Arguments: + + Fcb - Fcb for the directory being traversed. + + DirentOffset - This is our target point in the directory. We will map the + page containing this entry and possibly verify the dirent bounds at + this location. + + DirContext - This is the dirent context for this scan. We update it with + the location of the dirent we found. This structure has been initialized + outside of this call. + +Return Value: + + None. + +--*/ + +{ + LONGLONG BaseOffset; + + PAGED_CODE(); + + // + // Initialize the offset of the first dirent we want to map. + // + + DirContext->BaseOffset = SectorTruncate( DirentOffset ); + BaseOffset = DirContext->BaseOffset; + + DirContext->DataLength = SECTOR_SIZE; + + DirContext->SectorOffset = SectorOffset( DirentOffset ); + + // + // Truncate the data length if we are at the end of the file. + // + + if (DirContext->DataLength > (Fcb->FileSize.QuadPart - BaseOffset)) { + + DirContext->DataLength = (ULONG) (Fcb->FileSize.QuadPart - BaseOffset); + } + + // + // Now map the data at this offset. + // + + CcMapData( Fcb->FileObject, + (PLARGE_INTEGER) &BaseOffset, + DirContext->DataLength, + TRUE, + &DirContext->Bcb, + &DirContext->Sector ); + + // + // Verify the dirent bounds. + // + + DirContext->NextDirentOffset = CdCheckRawDirentBounds( IrpContext, + DirContext ); + + return; +} + + +BOOLEAN +CdLookupNextDirent ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PDIRENT_ENUM_CONTEXT CurrentDirContext, + OUT PDIRENT_ENUM_CONTEXT NextDirContext + ) + +/*++ + +Routine Description: + + This routine is called to find the next dirent in the directory. The + current position is given and we look for the next. We leave the context + for the starting position untouched and update the context for the + dirent we found. The target context may already be initialized so we + may already have the sector in memory. + + This routine will position the enumeration context for the next dirent and + verify the dirent bounds. + + NOTE - This routine can be called with CurrentDirContext and NextDirContext + pointing to the same enumeration context. + +Arguments: + + Fcb - Fcb for the directory being traversed. + + CurrentDirContext - This is the dirent context for this scan. We update + it with the location of the dirent we found. This is currently + pointing to a dirent location. The dirent bounds at this location + have already been verified. + + NextDirContext - This is the dirent context to update with the dirent we + find. This may already point to a dirent so we need to check if + we are in the same sector and unmap any buffer as necessary. + + This dirent is left in an indeterminant state if we don't find a dirent. + +Return Value: + + BOOLEAN - TRUE if we find a location for the next dirent, FALSE otherwise. + This routine can cause a raise if the directory is corrupt. + +--*/ + +{ + LONGLONG CurrentBaseOffset = CurrentDirContext->BaseOffset; + ULONG TempUlong; + + BOOLEAN FoundDirent = FALSE; + + PAGED_CODE(); + + // + // Check if a different sector is mapped. If so then move our target + // enumeration context to the same sector. + // + + if ((CurrentDirContext->BaseOffset != NextDirContext->BaseOffset) || + (NextDirContext->Bcb == NULL)) { + + // + // Unpin the current target Bcb and map the next sector. + // + + CdUnpinData( IrpContext, &NextDirContext->Bcb ); + + CcMapData( Fcb->FileObject, + (PLARGE_INTEGER) &CurrentBaseOffset, + CurrentDirContext->DataLength, + TRUE, + &NextDirContext->Bcb, + &NextDirContext->Sector ); + + // + // Copy the data length and sector offset. + // + + NextDirContext->DataLength = CurrentDirContext->DataLength; + NextDirContext->BaseOffset = CurrentDirContext->BaseOffset; + } + + // + // Now move to the same offset in the sector. + // + + NextDirContext->SectorOffset = CurrentDirContext->SectorOffset; + + // + // If the value is zero then unmap the current sector and set up + // the base offset to the beginning of the next sector. + // + + if (CurrentDirContext->NextDirentOffset == 0) { + + CurrentBaseOffset = NextDirContext->BaseOffset + NextDirContext->DataLength; + + // + // Unmap the current sector. We test the value of the Bcb in the + // loop below to see if we need to read in another sector. + // + + CdUnpinData( IrpContext, &NextDirContext->Bcb ); + + // + // There is another possible dirent in the current sector. Update the + // enumeration context to reflect this. + // + + } else { + + NextDirContext->SectorOffset += CurrentDirContext->NextDirentOffset; + } + + // + // Now loop until we find the next possible dirent or walk off the directory. + // + + while (TRUE) { + + // + // If we don't currently have a sector mapped then map the + // directory at the current offset. + // + + if (NextDirContext->Bcb == NULL) { + + TempUlong = SECTOR_SIZE; + + if (TempUlong > (ULONG) (Fcb->FileSize.QuadPart - CurrentBaseOffset)) { + + TempUlong = (ULONG) (Fcb->FileSize.QuadPart - CurrentBaseOffset); + + // + // If the length is zero then there is no dirent. + // + + if (TempUlong == 0) { + + break; + } + } + + CcMapData( Fcb->FileObject, + (PLARGE_INTEGER) &CurrentBaseOffset, + TempUlong, + TRUE, + &NextDirContext->Bcb, + &NextDirContext->Sector ); + + NextDirContext->BaseOffset = (ULONG) CurrentBaseOffset; + NextDirContext->SectorOffset = 0; + NextDirContext->DataLength = TempUlong; + } + + // + // The CDFS spec allows for sectors in a directory to contain all zeroes. + // In this case we need to move to the next sector. So look at the + // current potential dirent for a zero length. Move to the next + // dirent if length is zero. + // + + if (*((PCHAR) CdRawDirent( IrpContext, NextDirContext )) != 0) { + + FoundDirent = TRUE; + break; + } + + CurrentBaseOffset = NextDirContext->BaseOffset + NextDirContext->DataLength; + CdUnpinData( IrpContext, &NextDirContext->Bcb ); + } + + // + // Check the dirent bounds if we found a dirent. + // + + if (FoundDirent) { + + NextDirContext->NextDirentOffset = CdCheckRawDirentBounds( IrpContext, + NextDirContext ); + } + + return FoundDirent; +} + + +VOID +CdUpdateDirentFromRawDirent ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PDIRENT_ENUM_CONTEXT DirContext, + IN OUT PDIRENT Dirent + ) + +/*++ + +Routine Description: + + This routine is called to safely copy the data from the dirent on disk + to the in-memory dirent. The fields on disk are unaligned so we + need to safely copy them to our structure. + +Arguments: + + Fcb - Fcb for the directory being scanned. + + DirContext - Enumeration context for the raw disk dirent. + + Dirent - In-memory dirent to update. + +Return Value: + + None. + +--*/ + +{ + PRAW_DIRENT RawDirent = CdRawDirent( IrpContext, DirContext ); + + PAGED_CODE(); + + // + // Clear all of the current state flags except the flag indicating that + // we allocated a name string. + // + + ClearFlag( Dirent->Flags, DIRENT_FLAG_NOT_PERSISTENT ); + + // + // The dirent offset is the sum of the start of the sector and the + // sector offset. + // + + Dirent->DirentOffset = DirContext->BaseOffset + DirContext->SectorOffset; + + // + // Copy the dirent length from the raw dirent. + // + + Dirent->DirentLength = RawDirent->DirLen; + + // + // The starting offset on disk is computed by finding the starting + // logical block and stepping over the Xar block. + // + + CopyUchar4( &Dirent->StartingOffset, RawDirent->FileLoc ); + + Dirent->StartingOffset += RawDirent->XarLen; + + // + // Do a safe copy to get the data length. + // + + CopyUchar4( &Dirent->DataLength, RawDirent->DataLen ); + + // + // Save a pointer to the time stamps. + // + + Dirent->CdTime = RawDirent->RecordTime; + + // + // Copy the dirent flags. + // + + Dirent->DirentFlags = CdRawDirentFlags( IrpContext, RawDirent ); + + // + // For both the file unit and interleave skip we want to take the + // logical block count. + // + + Dirent->FileUnitSize = + Dirent->InterleaveGapSize = 0; + + if (RawDirent->IntLeaveSize != 0) { + + Dirent->FileUnitSize = RawDirent->IntLeaveSize; + Dirent->InterleaveGapSize = RawDirent->IntLeaveSkip; + } + + // + // Get the name length and remember a pointer to the start of the + // name string. We don't do any processing on the name at this + // point. + // + // Check that the name length is non-zero. + // + + if (RawDirent->FileIdLen == 0) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + Dirent->FileNameLen = RawDirent->FileIdLen; + Dirent->FileName = RawDirent->FileId; + + // + // If there are any remaining bytes at the end of the dirent then + // there may be a system use area. We protect ourselves from + // disks which don't pad the dirent entries correctly by using + // a fudge factor of one. All system use areas must have a length + // greater than one. Don't bother with the system use area + // if this is a directory. + // + + Dirent->XAAttributes = 0; + Dirent->XAFileNumber = 0; + Dirent->ExtentType = Form1Data; + Dirent->SystemUseOffset = 0; + + if (!FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY ) && + (Dirent->DirentLength > ((FIELD_OFFSET( RAW_DIRENT, FileId ) + Dirent->FileNameLen) + 1))) { + + Dirent->SystemUseOffset = WordAlign( FIELD_OFFSET( RAW_DIRENT, FileId ) + Dirent->FileNameLen ); + } + + return; +} + + +VOID +CdUpdateDirentName ( + IN PIRP_CONTEXT IrpContext, + IN OUT PDIRENT Dirent, + IN ULONG IgnoreCase + ) + +/*++ + +Routine Description: + + This routine is called to update the name in the dirent with the name + from the disk. We will look for the special case of the self and + parent entries and also construct the Unicode name for the Joliet disk + in order to work around the BigEndian on-disk structure. + +Arguments: + + Dirent - Pointer to the in-memory dirent structure. + + IgnoreCase - TRUE if we should build the upcased version. Otherwise we + use the exact case name. + +Return Value: + + None. + +--*/ + +{ + UCHAR DirectoryValue; + ULONG Length; + + NTSTATUS Status; + + PAGED_CODE(); + + // + // Check if this is a self or parent entry. There is no version number + // in these cases. We use a fixed string for these. + // + // Self-Entry - Length is 1, value is 0. + // Parent-Entry - Length is 1, value is 1. + // + + if ((Dirent->FileNameLen == 1) && + FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY )) { + + DirectoryValue = *((PCHAR) Dirent->FileName); + + if ((DirectoryValue == 0) || (DirectoryValue == 1)) { + + // + // We should not have allocated a name for these cases. + // + + ASSERT( !FlagOn( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER )); + + // + // Now use one of the hard coded directory names. + // + + Dirent->CdFileName.FileName = CdUnicodeDirectoryNames[DirectoryValue]; + + // + // Show that there is no version number. + // + + Dirent->CdFileName.VersionString.Length = 0; + + // + // The case name is the same as the exact name. + // + + Dirent->CdCaseFileName = Dirent->CdFileName; + + // + // Return now. + // + + return; + } + } + + // + // Compute how large a buffer we will need. If this is an ignore + // case operation then we will want a double size buffer. If the disk is not + // a Joliet disk then we might need two bytes for each byte in the name. + // + + Length = Dirent->FileNameLen; + + if (IgnoreCase) { + + Length *= 2; + } + + if (!FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_JOLIET )) { + + Length *= sizeof( WCHAR ); + } + + // + // Now decide if we need to allocate a new buffer. We will if + // this name won't fit in the embedded name buffer and it is + // larger than the current allocated buffer. We always use the + // allocated buffer if present. + // + // If we haven't allocated a buffer then use the embedded buffer if the data + // will fit. This is the typical case. + // + + if (!FlagOn( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER ) && + (Length <= sizeof( Dirent->NameBuffer ))) { + + Dirent->CdFileName.FileName.MaximumLength = sizeof( Dirent->NameBuffer ); + Dirent->CdFileName.FileName.Buffer = Dirent->NameBuffer; + + } else { + + // + // We need to use an allocated buffer. Check if the current buffer + // is large enough. + // + + if (Length > Dirent->CdFileName.FileName.MaximumLength) { + + // + // Free any allocated buffer. + // + + if (FlagOn( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER )) { + + ExFreePool( Dirent->CdFileName.FileName.Buffer ); + ClearFlag( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER ); + } + + Dirent->CdFileName.FileName.Buffer = FsRtlAllocatePoolWithTag( CdPagedPool, + Length, + TAG_DIRENT_NAME ); + + SetFlag( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER ); + + Dirent->CdFileName.FileName.MaximumLength = (USHORT) Length; + } + } + + // + // We now have a buffer for the name. We need to either convert the on-disk bigendian + // to little endian or covert the name to Unicode. + // + + if (!FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_JOLIET )) { + + Status = RtlOemToUnicodeN( Dirent->CdFileName.FileName.Buffer, + Dirent->CdFileName.FileName.MaximumLength, + &Length, + Dirent->FileName, + Dirent->FileNameLen ); + + ASSERT( Status == STATUS_SUCCESS ); + Dirent->CdFileName.FileName.Length = (USHORT) Length; + + } else { + + // + // Convert this string to little endian. + // + + CdConvertBigToLittleEndian( IrpContext, + Dirent->FileName, + Dirent->FileNameLen, + (PCHAR) Dirent->CdFileName.FileName.Buffer ); + + Dirent->CdFileName.FileName.Length = (USHORT) Dirent->FileNameLen; + } + + // + // Split the name into name and version strings. + // + + CdConvertNameToCdName( IrpContext, + &Dirent->CdFileName ); + + // + // The name length better be non-zero. + // + + if (Dirent->CdFileName.FileName.Length == 0) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // If the filename ends with a period then back up one character. + // + + if (Dirent->CdFileName.FileName.Buffer[(Dirent->CdFileName.FileName.Length - sizeof( WCHAR )) / 2] == L'.') { + + // + // Slide the version string down. + // + + if (Dirent->CdFileName.VersionString.Length != 0) { + + PWCHAR NewVersion; + + // + // Start from the position currently containing the separator. + // + + NewVersion = Add2Ptr( Dirent->CdFileName.FileName.Buffer, + Dirent->CdFileName.FileName.Length, + PWCHAR ); + + // + // Now overwrite the period. + // + + RtlMoveMemory( NewVersion - 1, + NewVersion, + Dirent->CdFileName.VersionString.Length + sizeof( WCHAR )); + + // + // Now point to the new version string. + // + + Dirent->CdFileName.VersionString.Buffer = NewVersion; + } + + // + // Shrink the filename length. + // + + Dirent->CdFileName.FileName.Length -= sizeof( WCHAR ); + } + + // + // If this an exact case operation then use the filename exactly. + // + + if (!IgnoreCase) { + + Dirent->CdCaseFileName = Dirent->CdFileName; + + // + // Otherwise perform our upcase operation. We already have guaranteed the buffers are + // there. + // + + } else { + + Dirent->CdCaseFileName.FileName.Buffer = Add2Ptr( Dirent->CdFileName.FileName.Buffer, + Dirent->CdFileName.FileName.MaximumLength / 2, + PWCHAR); + + Dirent->CdCaseFileName.FileName.MaximumLength = Dirent->CdFileName.FileName.MaximumLength / 2; + + CdUpcaseName( IrpContext, + &Dirent->CdFileName, + &Dirent->CdCaseFileName ); + } + + return; +} + + +BOOLEAN +CdFindFile ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PCD_NAME Name, + IN BOOLEAN IgnoreCase, + IN OUT PFILE_ENUM_CONTEXT FileContext, + OUT PCD_NAME *MatchingName + ) + +/*++ + +Routine Description: + + This routine is called to search a dirctory for a file matching the input + name. This name has been upcased at this point if this a case-insensitive + search. The name has been separated into separate name and version strings. + We look for an exact match in the name and only consider the version if + there is a version specified in the search name. + +Arguments: + + Fcb - Fcb for the directory being scanned. + + Name - Name to search for. + + IgnoreCase - Indicates the case of the search. + + FileContext - File context to use for the search. This has already been + initialized. + + MatchingName - Pointer to buffer containing matching name. We need this + in case we don't match the name in the directory but match the + short name instead. + +Return Value: + + BOOLEAN - TRUE if matching entry is found, FALSE otherwise. + +--*/ + +{ + PDIRENT Dirent; + ULONG ShortNameDirentOffset; + + BOOLEAN Found = FALSE; + + PAGED_CODE(); + + // + // Make sure there is a stream file for this Fcb. + // + + if (Fcb->FileObject == NULL) { + + CdCreateInternalStream( IrpContext, Fcb->Vcb, Fcb ); + } + + // + // Check to see whether we need to check for a possible short name. + // + + ShortNameDirentOffset = CdShortNameDirentOffset( IrpContext, &Name->FileName ); + + // + // Position ourselves at the first entry. + // + + CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, Fcb->StreamOffset ); + + // + // Loop while there are more entries in this directory. + // + + do { + + Dirent = &FileContext->InitialDirent->Dirent; + + // + // We only consider files which don't have the associated bit set. + // We also only look for files. All directories would already + // have been found. + // + + if (!FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_ASSOC | CD_ATTRIBUTE_DIRECTORY )) { + + // + // Update the name in the current dirent. + // + + CdUpdateDirentName( IrpContext, Dirent, IgnoreCase ); + + // + // Now check whether we have a name match. + // We exit the loop if we have a match. + // + + if (CdIsNameInExpression( IrpContext, + &Dirent->CdCaseFileName, + Name, + 0, + TRUE )) { + + *MatchingName = &Dirent->CdCaseFileName; + Found = TRUE; + break; + } + + // + // The names didn't match. If the input name is a possible short + // name and we are at the correct offset in the directory then + // check if the short names match. + // + + if (((Dirent->DirentOffset >> SHORT_NAME_SHIFT) == ShortNameDirentOffset) && + (Name->VersionString.Length == 0) && + !CdIs8dot3Name( IrpContext, + Dirent->CdFileName.FileName )) { + + // + // Create the short name and check for a match. + // + + CdGenerate8dot3Name( IrpContext, + &Dirent->CdCaseFileName.FileName, + Dirent->DirentOffset, + FileContext->ShortName.FileName.Buffer, + &FileContext->ShortName.FileName.Length ); + + // + // Now check whether we have a name match. + // We exit the loop if we have a match. + // + + if (CdIsNameInExpression( IrpContext, + &FileContext->ShortName, + Name, + 0, + FALSE )) { + + *MatchingName = &FileContext->ShortName, + Found = TRUE; + break; + } + } + } + + // + // Go to the next initial dirent for a file. + // + + } while (CdLookupNextInitialFileDirent( IrpContext, Fcb, FileContext )); + + // + // If we find the file then collect all of the dirents. + // + + if (Found) { + + CdLookupLastFileDirent( IrpContext, Fcb, FileContext ); + + } + + return Found; +} + + +BOOLEAN +CdFindDirectory ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PCD_NAME Name, + IN BOOLEAN IgnoreCase, + IN OUT PFILE_ENUM_CONTEXT FileContext + ) + +/*++ + +Routine Description: + + This routine is called to search a dirctory for a directory matching the input + name. This name has been upcased at this point if this a case-insensitive + search. We look for an exact match in the name and do not look for shortname + equivalents. + +Arguments: + + Fcb - Fcb for the directory being scanned. + + Name - Name to search for. + + IgnoreCase - Indicates the case of the search. + + FileContext - File context to use for the search. This has already been + initialized. + +Return Value: + + BOOLEAN - TRUE if matching entry is found, FALSE otherwise. + +--*/ + +{ + PDIRENT Dirent; + + BOOLEAN Found = FALSE; + + PAGED_CODE(); + + // + // Make sure there is a stream file for this Fcb. + // + + if (Fcb->FileObject == NULL) { + + CdCreateInternalStream( IrpContext, Fcb->Vcb, Fcb ); + } + + // + // Position ourselves at the first entry. + // + + CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, Fcb->StreamOffset ); + + // + // Loop while there are more entries in this directory. + // + + do { + + Dirent = &FileContext->InitialDirent->Dirent; + + // + // We only look for directories. Directories cannot have the + // associated bit set. + // + + if (FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY )) { + + // + // Update the name in the current dirent. + // + + CdUpdateDirentName( IrpContext, Dirent, IgnoreCase ); + + // + // Now check whether we have a name match. + // We exit the loop if we have a match. + // + + if (CdIsNameInExpression( IrpContext, + &Dirent->CdCaseFileName, + Name, + 0, + TRUE )) { + + Found = TRUE; + break; + } + } + + // + // Go to the next initial dirent. + // + + } while (CdLookupNextInitialFileDirent( IrpContext, Fcb, FileContext )); + + return Found; +} + + +BOOLEAN +CdFindFileByShortName ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PCD_NAME Name, + IN BOOLEAN IgnoreCase, + IN ULONG ShortNameDirentOffset, + IN OUT PFILE_ENUM_CONTEXT FileContext + ) + +/*++ + +Routine Description: + + This routine is called to find the file name entry whose short name + is defined by the input DirentOffset. The dirent offset here is + multiplied by 32 and we look for the dirent begins in this 32 byte offset in + directory. The minimum dirent length is 34 so we are guaranteed that only + one dirent can begin in each 32 byte block in the directory. + +Arguments: + + Fcb - Fcb for the directory being scanned. + + Name - Name we are trying to match. We know this contains the tilde + character followed by decimal characters. + + IgnoreCase - Indicates whether we need to upcase the long name and + generated short name. + + ShortNameDirentOffset - This is the shifted value for the offset of the + name in the directory. + + FileContext - This is the initialized file context to use for the search. + +Return Value: + + BOOLEAN - TRUE if a matching name was found, FALSE otherwise. + +--*/ + +{ + BOOLEAN Found = FALSE; + PDIRENT Dirent; + + ULONG ThisShortNameDirentOffset; + + PAGED_CODE(); + + // + // Make sure there is a stream file for this Fcb. + // + + if (Fcb->FileObject == NULL) { + + CdCreateInternalStream( IrpContext, Fcb->Vcb, Fcb ); + } + + // + // Position ourselves at the start of the directory and update + // + // + + CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, Fcb->StreamOffset ); + + // + // Loop until we have found the entry or are beyond this dirent. + // + + do { + + // + // Compute the short name dirent offset for the current dirent. + // + + Dirent = &FileContext->InitialDirent->Dirent; + ThisShortNameDirentOffset = Dirent->DirentOffset >> SHORT_NAME_SHIFT; + + // + // If beyond the target then exit. + // + + if (ThisShortNameDirentOffset > ShortNameDirentOffset) { + + break; + } + + // + // If equal to the target then check if we have a name match. + // We will either match or fail here. + // + + if (ThisShortNameDirentOffset == ShortNameDirentOffset) { + + // + // If this is an associated file then get out. + // + + if (FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_ASSOC )) { + + break; + } + + // + // Update the name in the dirent and check if it is not + // an 8.3 name. + // + + CdUpdateDirentName( IrpContext, Dirent, IgnoreCase ); + + if (CdIs8dot3Name( IrpContext, + Dirent->CdFileName.FileName )) { + + break; + } + + // + // Generate the 8.3 name see if it matches our input name. + // + + CdGenerate8dot3Name( IrpContext, + &Dirent->CdCaseFileName.FileName, + Dirent->DirentOffset, + FileContext->ShortName.FileName.Buffer, + &FileContext->ShortName.FileName.Length ); + + // + // Check if this name matches. + // + + if (CdIsNameInExpression( IrpContext, + Name, + &FileContext->ShortName, + 0, + FALSE )) { + + // + // Let our caller know we found an entry. + // + + Found = TRUE; + } + + // + // Break out of the loop. + // + + break; + } + + // + // Continue until there are no more entries. + // + + } while (CdLookupNextInitialFileDirent( IrpContext, Fcb, FileContext )); + + // + // If we find the file then collect all of the dirents. + // + + if (Found) { + + CdLookupLastFileDirent( IrpContext, Fcb, FileContext ); + + } + + return Found; +} + + +BOOLEAN +CdLookupNextInitialFileDirent ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN OUT PFILE_ENUM_CONTEXT FileContext + ) + +/*++ + +Routine Description: + + This routine is called to walk through the directory until we find the + first possible dirent for file. We are positioned at some point described + by the FileContext. We will walk through any remaing dirents for the + current file until we find the first dirent for some subsequent file. + + We can be called when we have found just one dirent for a file or all + of them. We first check the CurrentDirContext. In the typical + single-extent case this is unused. Then we look to the InitialDirContext + which must be initialized. + + This routine will save the initial DirContext to the PriorDirContext and + clean up any existing DirContext for the Prior or Current positions in + the enumeration context. + +Arguments: + + Fcb - This is the directory to scan. + + FileContext - This is the file enumeration context. It is currently pointing + at some file in the directory. + +Return Value: + +--*/ + +{ + PRAW_DIRENT RawDirent; + + PDIRENT_ENUM_CONTEXT CurrentDirContext; + PDIRENT_ENUM_CONTEXT TargetDirContext; + PCOMPOUND_DIRENT TempDirent; + + BOOLEAN FoundDirent; + BOOLEAN FoundLastDirent; + + PAGED_CODE(); + + // + // Start by saving the initial dirent of the current file as the + // previous file. + // + + TempDirent = FileContext->PriorDirent; + FileContext->PriorDirent = FileContext->InitialDirent; + FileContext->InitialDirent = TempDirent; + + // + // We will use the initial dirent of the prior file unless the + // previous search returned multiple extents. + // + + CurrentDirContext = &FileContext->PriorDirent->DirContext; + + if (FlagOn( FileContext->Flags, FILE_CONTEXT_MULTIPLE_DIRENTS )) { + + CurrentDirContext = &FileContext->CurrentDirent->DirContext; + } + + // + // Clear all of the flags and file size for the next file. + // + + FileContext->Flags = 0; + FileContext->FileSize = 0; + + FileContext->ShortName.FileName.Length = 0; + + // + // We always want to store the result into the updated initial dirent + // context. + // + + TargetDirContext = &FileContext->InitialDirent->DirContext; + + // + // Loop until we find the first dirent after the last dirent of the + // current file. We may not be at the last dirent for the current file yet + // so we may walk forward looking for the last and then find the + // initial dirent for the next file after that. + // + + while (TRUE) { + + // + // Remember if the last dirent we visited was the last dirent for + // a file. + // + + RawDirent = CdRawDirent( IrpContext, CurrentDirContext ); + + FoundLastDirent = !FlagOn( CdRawDirentFlags( IrpContext, RawDirent ), CD_ATTRIBUTE_MULTI ); + + // + // Try to find another dirent. + // + + FoundDirent = CdLookupNextDirent( IrpContext, + Fcb, + CurrentDirContext, + TargetDirContext ); + + // + // Exit the loop if no entry found. + // + + if (!FoundDirent) { + + break; + + } + + // + // Update the in-memory dirent. + // + + CdUpdateDirentFromRawDirent( IrpContext, + Fcb, + TargetDirContext, + &FileContext->InitialDirent->Dirent ); + + // + // Exit the loop if we had the end for the previous file. + // + + if (FoundLastDirent) { + + break; + } + + // + // Always use a single dirent from this point on. + // + + CurrentDirContext = TargetDirContext; + } + + return FoundDirent; +} + + +VOID +CdLookupLastFileDirent ( + IN PIRP_CONTEXT IrpContext, + IN PFCB Fcb, + IN PFILE_ENUM_CONTEXT FileContext + ) + +/*++ + +Routine Description: + + This routine is called when we've found the matching initial dirent for + a file. Now we want to find all of the dirents for a file as well as + compute the running total for the file size. + + We also go out to the system use area and check whether this is an + XA sector. In that case we will compute the real file size. + + The dirent in the initial compound dirent has been updated from the + raw dirent when this routine is called. + +Arguments: + + Fcb - Directory containing the entries for the file. + + FileContext - Enumeration context for this search. It currently points + to the first dirent of the file and the in-memory dirent has been + updated. + +Return Value: + + None. This routine may raise STATUS_FILE_CORRUPT. + +--*/ + +{ + XA_EXTENT_TYPE ExtentType; + PCOMPOUND_DIRENT CurrentCompoundDirent; + PDIRENT CurrentDirent; + + BOOLEAN FirstPass = TRUE; + BOOLEAN FoundDirent; + + PAGED_CODE(); + + // + // The current dirent to look at is the initial dirent for the file. + // + + CurrentCompoundDirent = FileContext->InitialDirent; + + // + // Loop until we reach the last dirent for the file. + // + + while (TRUE) { + + CurrentDirent = &CurrentCompoundDirent->Dirent; + + // + // Check if this extent has XA sectors. + // + + if ((CurrentDirent->SystemUseOffset != 0) && + FlagOn( Fcb->Vcb->VcbState, VCB_STATE_CDXA ) && + CdCheckForXAExtent( IrpContext, + CdRawDirent( IrpContext, &CurrentCompoundDirent->DirContext ), + CurrentDirent )) { + + // + // Any previous dirent must describe XA sectors as well. + // + + if (!FirstPass && (ExtentType != CurrentDirent->ExtentType)) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // If there are XA sectors then the data on the disk must + // be correctly aligned on sectors and be an integral number of + // sectors. Only an issue if the logical block size is not + // 2048. + // + + if (Fcb->Vcb->BlockSize != SECTOR_SIZE) { + + // + // We will do the following checks. + // + // Data must start on a sector boundary. + // Data length must be integral number of sectors. + // + + if ((SectorBlockOffset( Fcb->Vcb, CurrentDirent->StartingOffset ) != 0) || + (SectorBlockOffset( Fcb->Vcb, CurrentDirent->DataLength ) != 0)) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // If interleaved then both the file unit and interleave + // gap must be integral number of sectors. + // + + if ((CurrentDirent->FileUnitSize != 0) && + ((SectorBlockOffset( Fcb->Vcb, CurrentDirent->FileUnitSize ) != 0) || + (SectorBlockOffset( Fcb->Vcb, CurrentDirent->InterleaveGapSize ) != 0))) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + } + + // + // If this is the first dirent then add the bytes for the RIFF + // header. + // + + if (FirstPass) { + + FileContext->FileSize = sizeof( RIFF_HEADER ); + } + + // + // Add the size of the mode2-form2 sector for each sector + // we have here. + // + + FileContext->FileSize += Int32x32To64( CurrentDirent->DataLength >> SECTOR_SHIFT, + XA_SECTOR_SIZE); + + } else { + + // + // This extent does not have XA sectors. Any previous dirent + // better not have XA sectors. + // + + if (!FirstPass && (ExtentType != CurrentDirent->ExtentType)) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // Add these bytes to the file size. + // + + FileContext->FileSize += CurrentDirent->DataLength; + } + + // + // If we are at the last dirent then exit. + // + + if (!FlagOn( CurrentDirent->DirentFlags, CD_ATTRIBUTE_MULTI )) { + + break; + } + + // + // Remember the extent type of the current extent. + // + + ExtentType = CurrentDirent->ExtentType; + + // + // Look for the next dirent of the file. + // + + FoundDirent = CdLookupNextDirent( IrpContext, + Fcb, + &CurrentCompoundDirent->DirContext, + &FileContext->CurrentDirent->DirContext ); + + // + // If we didn't find the entry then this is a corrupt directory. + // + + if (!FoundDirent) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // Remember the dirent we just found. + // + + CurrentCompoundDirent = FileContext->CurrentDirent; + FirstPass = FALSE; + + CdUpdateDirentFromRawDirent( IrpContext, + Fcb, + &CurrentCompoundDirent->DirContext, + &CurrentCompoundDirent->Dirent ); + + // + // Look up all of the dirent information for the given dirent. + // + + CdUpdateDirentFromRawDirent( IrpContext, + Fcb, + &CurrentCompoundDirent->DirContext, + &CurrentCompoundDirent->Dirent ); + + // + // Set flag to show there were multiple extents. + // + + SetFlag( FileContext->Flags, FILE_CONTEXT_MULTIPLE_DIRENTS ); + } + + return; +} + + +VOID +CdCleanupFileContext ( + IN PIRP_CONTEXT IrpContext, + IN PFILE_ENUM_CONTEXT FileContext + ) + +/*++ + +Routine Description: + + This routine is called to cleanup the enumeration context for a file + search in a directory. We will unpin any remaining Bcbs and free + any allocated buffers. + +Arguments: + + FileContext - Enumeration context for the file search. + +Return Value: + + None. + +--*/ + +{ + PCOMPOUND_DIRENT CurrentCompoundDirent; + ULONG Count = 2; + + PAGED_CODE(); + + // + // Cleanup the individual compound dirents. + // + + do { + + CurrentCompoundDirent = &FileContext->Dirents[ Count ]; + CdCleanupDirContext( IrpContext, &CurrentCompoundDirent->DirContext ); + CdCleanupDirent( IrpContext, &CurrentCompoundDirent->Dirent ); + + } while (Count--); + + return; +} + + +// +// Local support routine +// + +ULONG +CdCheckRawDirentBounds ( + IN PIRP_CONTEXT IrpContext, + IN PDIRENT_ENUM_CONTEXT DirContext + ) + +/*++ + +Routine Description: + + This routine takes a Dirent enumeration context and computes the offset + to the next dirent. A non-zero value indicates the offset within this + sector. A zero value indicates to move to the next sector. If the + current dirent does not fit within the sector then we will raise + STATUS_CORRUPT. + +Arguments: + + DirContext - Enumeration context indicating the current position in + the sector. + +Return Value: + + ULONG - Offset to the next dirent in this sector or zero if the + next dirent is in the next sector. + + This routine will raise on a dirent which does not fit into the + described data buffer. + +--*/ + +{ + ULONG NextDirentOffset; + PRAW_DIRENT RawDirent; + + PAGED_CODE(); + + // + // We should always have at least a byte still available in the + // current buffer. + // + + ASSERT( (DirContext->DataLength - DirContext->SectorOffset) >= 1 ); + + // + // Get a pointer to the current dirent. + // + + RawDirent = CdRawDirent( IrpContext, DirContext ); + + // + // If the dirent length is non-zero then look at the current dirent. + // + + if (RawDirent->DirLen != 0) { + + // + // Check the following bound for the dirent length. + // + // - Fits in the available bytes in the sector. + // - Is at least the minimal dirent size. + // - Is large enough to hold the file name. + // + + if ((RawDirent->DirLen > (DirContext->DataLength - DirContext->SectorOffset)) || + (RawDirent->DirLen < MIN_RAW_DIRENT_LEN) || + (RawDirent->DirLen < (MIN_RAW_DIRENT_LEN - 1 + RawDirent->FileIdLen))) { + + CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR ); + } + + // + // Copy the dirent length field. + // + + NextDirentOffset = RawDirent->DirLen; + + // + // If we are exactly at the next sector then tell our caller by + // returning zero. + // + + if (NextDirentOffset == (DirContext->DataLength - DirContext->SectorOffset)) { + + NextDirentOffset = 0; + } + + } else { + + NextDirentOffset = 0; + } + + return NextDirentOffset; +} + + +// +// Local support routine +// + +XA_EXTENT_TYPE +CdCheckForXAExtent ( + IN PIRP_CONTEXT IrpContext, + IN PRAW_DIRENT RawDirent, + IN OUT PDIRENT Dirent + ) + +/*++ + +Routine Description: + + This routine is called to scan through the system use area to test if + the current dirent has the XA bit set. The bit in the in-memory + dirent will be set as appropriate. + +Arguments: + + RawDirent - Pointer to the on-disk dirent. + + Dirent - Pointer to the in-memory dirent. We will update this with the + appropriate XA flag. + +Return Value: + + XA_EXTENT_TYPE - Type of physical extent for this on disk dirent. + +--*/ + +{ + XA_EXTENT_TYPE ExtentType = Form1Data; + PSYSTEM_USE_XA SystemUseArea; + + PAGED_CODE(); + + // + // Check if there is enough space for the XA system use area. + // + + if (Dirent->DirentLength - Dirent->SystemUseOffset >= sizeof( SYSTEM_USE_XA )) { + + SystemUseArea = Add2Ptr( RawDirent, Dirent->SystemUseOffset, PSYSTEM_USE_XA ); + + // + // Check for a valid signature. + // + + if (SystemUseArea->Signature == SYSTEM_XA_SIGNATURE) { + + // + // Check for an audio track. + // + + if (FlagOn( SystemUseArea->Attributes, SYSTEM_USE_XA_DA )) { + + ExtentType = CDAudio; + + } else if (FlagOn( SystemUseArea->Attributes, SYSTEM_USE_XA_FORM2 )) { + + ExtentType = Mode2Form2Data; + } + + Dirent->XAAttributes = SystemUseArea->Attributes; + Dirent->XAFileNumber = SystemUseArea->FileNumber; + } + } + + Dirent->ExtentType = ExtentType; + return ExtentType; +} + + |