mirror of
https://github.com/reactos/reactos.git
synced 2024-09-02 08:38:28 +00:00
1871 lines
49 KiB
C
1871 lines
49 KiB
C
/*++
|
||
|
||
Copyright (c) 1989-2000 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.
|
||
|
||
|
||
--*/
|
||
|
||
#include "cdprocs.h"
|
||
|
||
//
|
||
// The Bug check file id for this module
|
||
//
|
||
|
||
#define BugCheckFileId (CDFS_BUG_CHECK_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,
|
||
_Inout_ 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,
|
||
_Inout_ 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;
|
||
}
|
||
|
||
|
||
_At_(Dirent->CdTime, _Post_notnull_)
|
||
VOID
|
||
CdUpdateDirentFromRawDirent (
|
||
_In_ PIRP_CONTEXT IrpContext,
|
||
_In_ PFCB Fcb,
|
||
_In_ PDIRENT_ENUM_CONTEXT DirContext,
|
||
_Inout_ 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();
|
||
|
||
UNREFERENCED_PARAMETER( Fcb );
|
||
|
||
//
|
||
// 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 = (PCHAR)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 = (PCHAR)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,
|
||
_Inout_ 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 by the time we see these cases.
|
||
// If we have, this means that the image is in violation of ISO 9660 7.6.2,
|
||
// which states that the ./.. entries must be the first two in the directory.
|
||
//
|
||
|
||
if (FlagOn( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER )) {
|
||
|
||
CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
|
||
}
|
||
|
||
//
|
||
// 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;
|
||
|
||
//
|
||
// Mark this as a constant value entry.
|
||
//
|
||
|
||
SetFlag( Dirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY );
|
||
|
||
//
|
||
// Return now.
|
||
//
|
||
|
||
return;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Mark this as a non-constant value entry.
|
||
//
|
||
|
||
ClearFlag( Dirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY );
|
||
|
||
//
|
||
// 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 )) {
|
||
|
||
CdFreePool( &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 );
|
||
|
||
__analysis_assert( Status == STATUS_SUCCESS );
|
||
NT_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 (!CdIsLegalName( IrpContext, &Dirent->CdFileName.FileName )) {
|
||
|
||
CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
|
||
}
|
||
|
||
//
|
||
// 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;
|
||
}
|
||
|
||
|
||
_Success_(return != FALSE) BOOLEAN
|
||
CdFindFile (
|
||
_In_ PIRP_CONTEXT IrpContext,
|
||
_In_ PFCB Fcb,
|
||
_In_ PCD_NAME Name,
|
||
_In_ BOOLEAN IgnoreCase,
|
||
_Inout_ 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.
|
||
//
|
||
|
||
CdVerifyOrCreateDirStreamFile( IrpContext, 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 );
|
||
|
||
//
|
||
// Don't bother with constant entries.
|
||
//
|
||
|
||
if (FlagOn( Dirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY )) {
|
||
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// 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,
|
||
_Inout_ 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.
|
||
//
|
||
|
||
CdVerifyOrCreateDirStreamFile( IrpContext, 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 );
|
||
|
||
//
|
||
// Don't bother with constant entries.
|
||
//
|
||
|
||
if (FlagOn( Dirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY )) {
|
||
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// 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;
|
||
}
|
||
|
||
|
||
_At_(FileContext->ShortName.FileName.MaximumLength, _In_range_(>=, BYTE_COUNT_8_DOT_3))
|
||
BOOLEAN
|
||
CdFindFileByShortName (
|
||
_In_ PIRP_CONTEXT IrpContext,
|
||
_In_ PFCB Fcb,
|
||
_In_ PCD_NAME Name,
|
||
_In_ BOOLEAN IgnoreCase,
|
||
_In_ ULONG ShortNameDirentOffset,
|
||
_Inout_ 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.
|
||
//
|
||
|
||
CdVerifyOrCreateDirStreamFile( IrpContext, 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,
|
||
_Inout_ 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 = FALSE;
|
||
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 = Form1Data;
|
||
PCOMPOUND_DIRENT CurrentCompoundDirent;
|
||
PDIRENT CurrentDirent = NULL;
|
||
|
||
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;
|
||
|
||
//
|
||
// 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();
|
||
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
|
||
//
|
||
// 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();
|
||
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
|
||
//
|
||
// We should always have at least a byte still available in the
|
||
// current buffer.
|
||
//
|
||
|
||
NT_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,
|
||
_Inout_ 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();
|
||
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
|
||
//
|
||
// 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 )) {
|
||
|
||
//
|
||
// Check for XA data. Note that a number of discs (video CDs)
|
||
// have files marked as type XA Mode 2 Form 1 (2048 bytes of
|
||
// user data), but actually record these sectors as Mode2 Form 2
|
||
// (2352). We will fail to read these files, since for M2F1,
|
||
// a normal read CD command is issued (as per SCSI specs).
|
||
//
|
||
|
||
ExtentType = Mode2Form2Data;
|
||
}
|
||
|
||
Dirent->XAAttributes = SystemUseArea->Attributes;
|
||
Dirent->XAFileNumber = SystemUseArea->FileNumber;
|
||
}
|
||
}
|
||
|
||
Dirent->ExtentType = ExtentType;
|
||
return ExtentType;
|
||
}
|
||
|
||
|
||
|