mirror of
https://github.com/reactos/reactos.git
synced 2024-12-29 10:35:28 +00:00
1870 lines
49 KiB
C
1870 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;
|
|
}
|
|
|
|
|
|
|