|
|
/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
EtfsBoot.c
Abstract:
This module implements the El Torito CD boot file system used by the operating system loader.
Author:
Steve Collins [stevec] 25-Nov-1995
Revision History:
--*/
#if defined(ELTORITO)
#include "bootlib.h"
#include "cd.h"
#include "blcache.h"
BOOTFS_INFO EtfsBootFsInfo = {L"etfs"};
//
// Local procedure prototypes.
//
ARC_STATUS EtfsReadDisk( IN ULONG DeviceId, IN ULONG Lbo, IN ULONG ByteCount, IN OUT PVOID Buffer, IN BOOLEAN CacheNewData );
VOID EtfsFirstComponent( IN OUT PSTRING String, OUT PSTRING FirstComponent );
typedef enum _COMPARISON_RESULTS { LessThan = -1, EqualTo = 0, GreaterThan = 1 } COMPARISON_RESULTS;
COMPARISON_RESULTS EtfsCompareNames( IN PSTRING Name1, IN PSTRING Name2 );
ARC_STATUS EtfsSearchDirectory( IN PSTRING Name, OUT PBOOLEAN IsDirectory );
VOID EtfsGetDirectoryInfo( IN PRAW_DIR_REC DirEntry, IN BOOLEAN IsoVol, OUT PULONG SectorOffset, OUT PULONG DiskOffset, OUT PULONG Length );
COMPARISON_RESULTS EtfsFileMatch( IN PRAW_DIR_REC DirEntry, IN PSTRING FileName );
typedef union _USHORT2 { USHORT Ushort[2]; ULONG ForceAlignment; } USHORT2, *PUSHORT2;
//
// This macro copies an unaligned src longword to an aligned dsr longword
// accessing the source on a word boundary.
//
#define CopyUshort2(Dst,Src) { \
((PUSHORT2)(Dst))->Ushort[0] = ((UNALIGNED USHORT2 *)(Src))->Ushort[0]; \ ((PUSHORT2)(Dst))->Ushort[1] = ((UNALIGNED USHORT2 *)(Src))->Ushort[1]; \ }
//
// The following macro upcases a single ascii character
//
#define ToUpper(C) ((((C) >= 'a') && ((C) <= 'z')) ? (C) - 'a' + 'A' : (C))
#define SetFlag(Flags,SingleFlag) { (Flags) |= (SingleFlag); }
//
// The following macro indicate if the flag is on or off
//
#define FlagOn(Flags,SingleFlag) ((BOOLEAN)( \
(((Flags) & (SingleFlag)) != 0 ? TRUE : FALSE) \ ) \ )
//
// Define global data.
//
// Context Pointer - This is a pointer to the context for the current file
// operation that is active.
//
PETFS_STRUCTURE_CONTEXT EtfsStructureContext;
//
// File Descriptor - This is a pointer to the file descriptor for the current
// file operation that is active.
//
PBL_FILE_TABLE EtfsFileTableEntry;
//
// File entry table - This is a structure that provides entry to the Etfs
// file system procedures. It is exported when an Etfs file structure
// is recognized.
//
BL_DEVICE_ENTRY_TABLE EtfsDeviceEntryTable;
PBL_DEVICE_ENTRY_TABLE IsEtfsFileStructure ( IN ULONG DeviceId, IN PVOID StructureContext )
/*++
Routine Description:
This routine determines if the partition on the specified channel contains an Etfs file system volume.
Arguments:
DeviceId - Supplies the file table index for the device on which read operations are to be performed.
StructureContext - Supplies a pointer to a Etfs file structure context.
Return Value:
A pointer to the Etfs entry table is returned if the partition is recognized as containing an Etfs volume. Otherwise, NULL is returned.
--*/
{ UCHAR UnalignedSector[CD_SECTOR_SIZE + 256];
PRAW_ISO_VD RawVd; PRAW_DIR_REC RootDe; PRAW_ET_BRVD RawBrvd;
UCHAR DescType; UCHAR Version;
UCHAR BrInd; UCHAR BrVersion;
BOOLEAN EtBootRec; BOOLEAN IsoVol;
STRING IsoVolId; STRING EtSysId; STRING DiskId;
ULONG DiskOffset;
//
// Capture in our global variable the Etfs Structure context record
//
EtfsStructureContext = (PETFS_STRUCTURE_CONTEXT)StructureContext; RtlZeroMemory((PVOID)EtfsStructureContext, sizeof(ETFS_STRUCTURE_CONTEXT));
//
// First check the Boot Record Volume Descriptor at sector 17
//
DiskOffset = ELTORITO_BRVD_SECTOR * CD_SECTOR_SIZE;
//
// Compute the properly aligned buffer for reading in cdrom
// sectors.
//
RawBrvd = ALIGN_BUFFER( UnalignedSector );
if (EtfsReadDisk( DeviceId, DiskOffset, CD_SECTOR_SIZE, RawBrvd, CACHE_NEW_DATA ) != ESUCCESS) {
return NULL; }
//
// Initialize the string Id to match.
//
RtlInitString( &IsoVolId, ISO_VOL_ID );
DiskId.Length = 5; DiskId.MaximumLength = 5;
//
// Compare the standard identifier string in the boot record volume descriptor with the Iso value.
//
DiskId.Buffer = RBRVD_STD_ID( RawBrvd );
IsoVol = (BOOLEAN)(EtfsCompareNames( &DiskId, &IsoVolId ) == EqualTo);
if (!IsoVol) {
return NULL; }
//
// Get the boot record indicator and volume descriptor version number.
//
BrInd = RBRVD_BR_IND( RawBrvd ); BrVersion = RBRVD_VERSION( RawBrvd );
//
// Return NULL, if the version is incorrect or this isn't a boot record
// volume descriptor.
//
if (BrVersion != BRVD_VERSION_1 || BrInd != VD_BOOTREC) {
return NULL; }
//
// Initialize the string Id to match.
//
RtlInitString( &EtSysId, ET_SYS_ID );
DiskId.Length = 23; DiskId.MaximumLength = 23;
//
// Compare the boot system identifier in the boot record volume descriptor with the El Torito value.
//
DiskId.Buffer = RBRVD_SYS_ID( RawBrvd );
EtBootRec = (BOOLEAN)(EtfsCompareNames( &DiskId, &EtSysId ) == EqualTo);
if (!EtBootRec) {
return NULL; }
//
// Now check the Primary Volume Descriptor
// We do this second because if it's valid we want to store values from this sector
// (we only allocate a single buffer for reading in a sector at a time)
//
RawVd = ALIGN_BUFFER( UnalignedSector );
//
// For El Torito the Primary Volume Descriptor must be at sector 16
//
DiskOffset = ELTORITO_VD_SECTOR * CD_SECTOR_SIZE;
//
// Check if this is a valid Primary Volume Descriptor
//
if (EtfsReadDisk( DeviceId, DiskOffset, CD_SECTOR_SIZE, RawVd, CACHE_NEW_DATA ) != ESUCCESS) {
return NULL; }
//
// Initialize the string Id to match.
//
RtlInitString( &IsoVolId, ISO_VOL_ID );
DiskId.Length = 5; DiskId.MaximumLength = 5;
//
// Compare the standard identifier string in the volume descriptor with the Iso value.
//
DiskId.Buffer = RVD_STD_ID( RawVd, TRUE );
IsoVol = (BOOLEAN)(EtfsCompareNames( &DiskId, &IsoVolId ) == EqualTo);
if (!IsoVol) {
return NULL; }
//
// Get the volume descriptor type and volume descriptor version number.
//
DescType = RVD_DESC_TYPE( RawVd, IsoVol ); Version = RVD_VERSION( RawVd, IsoVol );
//
// Return NULL, if the version is incorrect or this isn't a primary
// volume descriptor.
//
if (Version != VERSION_1 || DescType != VD_PRIMARY) {
return NULL; }
//
// Update the fields of the Etfs context structure that apply
// to the volume.
//
EtfsStructureContext->IsIsoVol = IsoVol; EtfsStructureContext->LbnBlockSize = RVD_LB_SIZE( RawVd, IsoVol ); EtfsStructureContext->LogicalBlockCount = RVD_VOL_SIZE( RawVd, IsoVol );
//
// Get the information on the root directory and save it in
// the context structure.
//
RootDe = (PRAW_DIR_REC) (RVD_ROOT_DE( RawVd, IsoVol ));
EtfsGetDirectoryInfo( RootDe, IsoVol, &EtfsStructureContext->RootDirSectorOffset, &EtfsStructureContext->RootDirDiskOffset, &EtfsStructureContext->RootDirSize );
//
// Initialize the file entry table.
//
EtfsDeviceEntryTable.Open = EtfsOpen; EtfsDeviceEntryTable.Close = EtfsClose; EtfsDeviceEntryTable.Read = EtfsRead; EtfsDeviceEntryTable.Seek = EtfsSeek; EtfsDeviceEntryTable.Write = EtfsWrite; EtfsDeviceEntryTable.GetFileInformation = EtfsGetFileInformation; EtfsDeviceEntryTable.SetFileInformation = EtfsSetFileInformation; EtfsDeviceEntryTable.BootFsInfo = &EtfsBootFsInfo;
//
// And return the address of the table to our caller.
//
return &EtfsDeviceEntryTable; }
ARC_STATUS EtfsClose ( IN ULONG FileId )
/*++
Routine Description:
This routine closes the file specified by the file id.
Arguments:
FileId - Supplies the file table index.
Return Value:
ESUCCESS if returned as the function value.
--*/
{ //
// Indicate that the file isn't open any longer
//
BlFileTable[FileId].Flags.Open = 0;
//
// And return to our caller
//
return ESUCCESS; }
ARC_STATUS EtfsOpen ( IN PCHAR FileName, IN OPEN_MODE OpenMode, IN PULONG FileId )
/*++
Routine Description:
This routine searches the root directory for a file matching FileName. If a match is found the dirent for the file is saved and the file is opened.
Arguments:
FileName - Supplies a pointer to a zero terminated file name.
OpenMode - Supplies the mode of the open.
FileId - Supplies a pointer to a variable that specifies the file table entry that is to be filled in if the open is successful.
Return Value:
ESUCCESS is returned if the open operation is successful. Otherwise, an unsuccessful status is returned that describes the reason for failure.
--*/
{ ARC_STATUS Status;
ULONG DeviceId;
STRING PathName;
STRING Name; BOOLEAN IsDirectory; BOOLEAN SearchSucceeded;
//
// Save the address of the file table entry, context area, and the device
// id in use.
//
EtfsFileTableEntry = &BlFileTable[*FileId]; EtfsStructureContext = (PETFS_STRUCTURE_CONTEXT)EtfsFileTableEntry->StructureContext;
DeviceId = EtfsFileTableEntry->DeviceId;
//
// Construct a file name descriptor from the input file name.
//
RtlInitString( &PathName, FileName );
//
// Set the starting directory to be the root directory.
//
EtfsStructureContext->DirSectorOffset = EtfsStructureContext->RootDirSectorOffset; EtfsStructureContext->DirDiskOffset = EtfsStructureContext->RootDirDiskOffset; EtfsStructureContext->DirSize = EtfsStructureContext->RootDirSize;
//
// While the path name has some characters in it we'll go through our
// loop which extracts the first part of the path name and searches
// the current fnode (which must be a directory) for an the entry.
// If what we find is a directory then we have a new directory fnode
// and simply continue back to the top of the loop.
//
IsDirectory = TRUE; SearchSucceeded = TRUE;
while (PathName.Length > 0 && IsDirectory) {
//
// Extract the first component.
//
EtfsFirstComponent( &PathName, &Name );
//
// Copy the name into the filename buffer.
//
EtfsFileTableEntry->FileNameLength = (UCHAR) Name.Length; RtlMoveMemory( EtfsFileTableEntry->FileName, Name.Buffer, Name.Length );
//
// Look to see if the file exists.
//
Status = EtfsSearchDirectory( &Name, &IsDirectory );
if (Status == ENOENT) {
SearchSucceeded = FALSE; break; }
if (Status != ESUCCESS) {
return Status; }
}
//
// If the path name length is not zero then we were trying to crack a path
// with an nonexistent (or non directory) name in it. For example, we tried
// to crack a\b\c\d and b is not a directory or does not exist (then the path
// name will still contain c\d).
//
if (PathName.Length != 0) {
return ENOTDIR; }
//
// At this point we've cracked the name up to (an maybe including the last
// component). We located the last component if the SearchSucceeded flag is
// true, otherwise the last component does not exist. If we located the last
// component then this is like an open or a supersede, but not a create.
//
if (SearchSucceeded) {
//
// Check if the last component is a directory
//
if (IsDirectory) {
//
// For an existing directory the only valid open mode is OpenDirectory
// all other modes return an error
//
switch (OpenMode) {
case ArcOpenReadOnly: case ArcOpenWriteOnly: case ArcOpenReadWrite: case ArcCreateWriteOnly: case ArcCreateReadWrite: case ArcSupersedeWriteOnly: case ArcSupersedeReadWrite:
//
// If we reach here then the caller got a directory but didn't
// want to open a directory
//
return EISDIR;
case ArcOpenDirectory:
//
// If we reach here then the caller got a directory and wanted
// to open a directory.
//
EtfsFileTableEntry->u.EtfsFileContext.FileSize = EtfsStructureContext->DirSize; EtfsFileTableEntry->u.EtfsFileContext.DiskOffset = EtfsStructureContext->DirDiskOffset; EtfsFileTableEntry->u.EtfsFileContext.IsDirectory = TRUE;
EtfsFileTableEntry->Flags.Open = 1; EtfsFileTableEntry->Flags.Read = 1; EtfsFileTableEntry->Position.LowPart = 0; EtfsFileTableEntry->Position.HighPart = 0;
return ESUCCESS;
case ArcCreateDirectory:
//
// If we reach here then the caller got a directory and wanted
// to create a new directory
//
return EACCES; } }
//
// If we get there then we have an existing file that is being opened.
// We can open existing files only read only.
//
switch (OpenMode) {
case ArcOpenReadOnly:
//
// If we reach here then the user got a file and wanted to open the
// file read only
//
EtfsFileTableEntry->u.EtfsFileContext.FileSize = EtfsStructureContext->DirSize; EtfsFileTableEntry->u.EtfsFileContext.DiskOffset = EtfsStructureContext->DirDiskOffset; EtfsFileTableEntry->u.EtfsFileContext.IsDirectory = FALSE;
EtfsFileTableEntry->Flags.Open = 1; EtfsFileTableEntry->Flags.Read = 1; EtfsFileTableEntry->Position.LowPart = 0; EtfsFileTableEntry->Position.HighPart = 0;
return ESUCCESS;
case ArcOpenWriteOnly: case ArcOpenReadWrite: case ArcCreateWriteOnly: case ArcCreateReadWrite: case ArcSupersedeWriteOnly: case ArcSupersedeReadWrite:
//
// If we reach here then we are trying to open a read only
// device for write.
//
return EROFS;
case ArcOpenDirectory: case ArcCreateDirectory:
//
// If we reach here then the user got a file and wanted a directory
//
return ENOTDIR; } }
//
// If we get here the last component does not exist so we are trying to create
// either a new file or a directory.
//
switch (OpenMode) {
case ArcOpenReadOnly: case ArcOpenWriteOnly: case ArcOpenReadWrite: case ArcOpenDirectory:
//
// If we reach here then the user did not get a file but wanted a file
//
return ENOENT;
case ArcCreateWriteOnly: case ArcSupersedeWriteOnly: case ArcCreateReadWrite: case ArcSupersedeReadWrite: case ArcCreateDirectory:
//
// If we get hre the user wants to create something.
//
return EROFS; }
//
// If we reach here then the path name is exhausted and we didn't
// reach a file so return an error to our caller
//
return ENOENT; }
ARC_STATUS EtfsRead ( IN ULONG FileId, OUT PVOID Buffer, IN ULONG Length, OUT PULONG Transfer )
/*++
Routine Description:
This routine reads data from the specified file.
Arguments:
FileId - Supplies the file table index.
Buffer - Supplies a pointer to the buffer that receives the data read.
Length - Supplies the number of bytes that are to be read.
Transfer - Supplies a pointer to a variable that receives the number of bytes actually transfered.
Return Value:
ESUCCESS is returned if the read operation is successful. Otherwise, an unsuccessful status is returned that describes the reason for failure.
--*/
{ ARC_STATUS Status;
ULONG DeviceId; ULONG DiskOffset;
//
// Save the address of the file table entry, context area, and the device
// id in use.
//
EtfsFileTableEntry = &BlFileTable[FileId]; EtfsStructureContext = (PETFS_STRUCTURE_CONTEXT)EtfsFileTableEntry->StructureContext;
DeviceId = EtfsFileTableEntry->DeviceId;
//
// Clear the transfer count and set the initial disk offset.
//
*Transfer = 0;
//
// Check for end of file.
//
//
// If the file position is currently at the end of file, then return
// a success status with no bytes read from the file. If the file
// plus the length of the transfer is beyond the end of file, then
// read only the remaining part of the file. Otherwise, read the
// requested number of bytes.
//
if (EtfsFileTableEntry->Position.LowPart == EtfsFileTableEntry->u.EtfsFileContext.FileSize) { return ESUCCESS;
} else { if ((EtfsFileTableEntry->Position.LowPart + Length) >= EtfsFileTableEntry->u.EtfsFileContext.FileSize) { Length = EtfsFileTableEntry->u.EtfsFileContext.FileSize - EtfsFileTableEntry->Position.LowPart; } }
DiskOffset = EtfsFileTableEntry->Position.LowPart + EtfsFileTableEntry->u.EtfsFileContext.DiskOffset;
//
// Read in runs (i.e., sectors) until the byte count goes to zero
//
while (Length > 0) {
ULONG CurrentRunByteCount;
//
// Compute the current read byte count.
//
if (Length > MAX_CDROM_READ) {
CurrentRunByteCount = MAX_CDROM_READ;
} else {
CurrentRunByteCount = Length; }
//
// Read from the disk.
//
if ((Status = EtfsReadDisk( DeviceId, DiskOffset, CurrentRunByteCount, Buffer, DONT_CACHE_NEW_DATA )) != ESUCCESS) {
return Status; }
//
// Update the remaining length.
//
Length -= CurrentRunByteCount;
//
// Update the current position and the number of bytes transfered
//
EtfsFileTableEntry->Position.LowPart += CurrentRunByteCount; DiskOffset += CurrentRunByteCount;
*Transfer += CurrentRunByteCount;
//
// Update buffer to point to the next byte location to fill in
//
Buffer = (PCHAR)Buffer + CurrentRunByteCount; }
//
// If we get here then remaining sector count is zero so we can
// return success to our caller
//
return ESUCCESS; }
ARC_STATUS EtfsSeek ( IN ULONG FileId, IN PLARGE_INTEGER Offset, IN SEEK_MODE SeekMode )
/*++
Routine Description:
This routine seeks to the specified position for the file specified by the file id.
Arguments:
FileId - Supplies the file table index.
Offset - Supplies the offset in the file to position to.
SeekMode - Supplies the mode of the seek operation.
Return Value:
ESUCCESS if returned as the function value.
--*/
{ ULONG NewPosition;
//
// Compute the new position
//
if (SeekMode == SeekAbsolute) {
NewPosition = Offset->LowPart;
} else {
NewPosition = BlFileTable[FileId].Position.LowPart + Offset->LowPart; }
//
// If the new position is greater than the file size then return
// an error
//
if (NewPosition > BlFileTable[FileId].u.EtfsFileContext.FileSize) {
return EINVAL; }
//
// Otherwise set the new position and return to our caller
//
BlFileTable[FileId].Position.LowPart = NewPosition;
return ESUCCESS; }
ARC_STATUS EtfsWrite ( IN ULONG FileId, IN PVOID Buffer, IN ULONG Length, OUT PULONG Transfer )
/*++
Routine Description:
This routine writes data to the specified file.
Arguments:
FileId - Supplies the file table index.
Buffer - Supplies a pointer to the buffer that contains the data written.
Length - Supplies the number of bytes that are to be written.
Transfer - Supplies a pointer to a variable that receives the number of bytes actually transfered.
Return Value:
ESUCCESS is returned if the write operation is successful. Otherwise, an unsuccessful status is returned that describes the reason for failure.
--*/
{ return EROFS;
UNREFERENCED_PARAMETER( FileId ); UNREFERENCED_PARAMETER( Buffer ); UNREFERENCED_PARAMETER( Length ); UNREFERENCED_PARAMETER( Transfer ); }
ARC_STATUS EtfsGetFileInformation ( IN ULONG FileId, OUT PFILE_INFORMATION Buffer )
/*++
Routine Description:
This procedure returns to the user a buffer filled with file information
Arguments:
FileId - Supplies the File id for the operation
Buffer - Supplies the buffer to receive the file information. Note that it must be large enough to hold the full file name
Return Value:
ESUCCESS is returned for all get information requests.
--*/
{ PBL_FILE_TABLE FileTableEntry; ULONG i;
//
// Load our local variables
//
FileTableEntry = &BlFileTable[FileId];
//
// Zero out the buffer, and fill in its non-zero values
//
RtlZeroMemory(Buffer, sizeof(FILE_INFORMATION));
Buffer->EndingAddress.LowPart = FileTableEntry->u.EtfsFileContext.FileSize;
Buffer->CurrentPosition.LowPart = FileTableEntry->Position.LowPart; Buffer->CurrentPosition.HighPart = 0;
SetFlag(Buffer->Attributes, ArcReadOnlyFile);
if (FileTableEntry->u.EtfsFileContext.IsDirectory) {
SetFlag( Buffer->Attributes, ArcDirectoryFile ); }
Buffer->FileNameLength = FileTableEntry->FileNameLength;
for (i = 0; i < FileTableEntry->FileNameLength; i += 1) {
Buffer->FileName[i] = FileTableEntry->FileName[i]; }
return ESUCCESS; }
ARC_STATUS EtfsSetFileInformation ( IN ULONG FileId, IN ULONG AttributeFlags, IN ULONG AttributeMask )
/*++
Routine Description:
This routine sets the file attributes of the indicated file
Arguments:
FileId - Supplies the File Id for the operation
AttributeFlags - Supplies the value (on or off) for each attribute being modified
AttributeMask - Supplies a mask of the attributes being altered. All other file attributes are left alone.
Return Value:
EROFS is always returned in this case.
--*/
{ return EROFS;
UNREFERENCED_PARAMETER( FileId ); UNREFERENCED_PARAMETER( AttributeFlags ); UNREFERENCED_PARAMETER( AttributeMask ); }
ARC_STATUS EtfsInitialize ( VOID )
/*++
Routine Description:
This routine initializes the etfs boot filesystem. Currently this is a no-op.
Arguments:
None.
Return Value:
ESUCCESS.
--*/
{ return ESUCCESS; }
//
// Internal support routine
//
ARC_STATUS EtfsReadDisk( IN ULONG DeviceId, IN ULONG Lbo, IN ULONG ByteCount, IN OUT PVOID Buffer, IN BOOLEAN CacheNewData )
/*++
Routine Description:
This routine reads in zero or more sectors from the specified device.
Arguments:
DeviceId - Supplies the device id to use in the arc calls.
Lbo - Supplies the LBO (logical byte offset) to start reading from.
ByteCount - Supplies the number of bytes to read.
Buffer - Supplies a pointer to the buffer to read the bytes into.
CacheNewData - Whether to cache new data read from the disk.
Return Value:
ESUCCESS is returned if the read operation is successful. Otherwise, an unsuccessful status is returned that describes the reason for failure.
--*/
{ LARGE_INTEGER LargeLbo; ARC_STATUS Status; ULONG i;
//
// Special case the zero byte read request
//
if (ByteCount == 0) {
return ESUCCESS; }
//
// Issue the read through the cache.
//
LargeLbo.QuadPart = Lbo; Status = BlDiskCacheRead(DeviceId, &LargeLbo, Buffer, ByteCount, &i, CacheNewData);
if (Status != ESUCCESS) {
return Status; }
//
// Make sure we got back the amount requested
//
if (ByteCount != i) {
return EIO; }
//
// Everything is fine so return success to our caller
//
return ESUCCESS; }
//
// Internal support routine
//
VOID EtfsFirstComponent( IN OUT PSTRING String, OUT PSTRING FirstComponent )
/*++
Routine Description:
This routine takes an input path name and separates it into its first file name component and the remaining part.
Arguments:
String - Supplies the original string being dissected. On return this string will now point to the remaining part.
FirstComponent - Returns the string representing the first file name in the input string.
Return Value:
None.
--*/
{ ULONG Index;
//
// Copy over the string variable into the first component variable
//
*FirstComponent = *String;
//
// Now if the first character in the name is a backslash then
// simply skip over the backslash.
//
if (FirstComponent->Buffer[0] == '\\') {
FirstComponent->Buffer += 1; FirstComponent->Length -= 1; }
//
// Now search the name for a backslash
//
for (Index = 0; Index < FirstComponent->Length; Index += 1) {
if (FirstComponent->Buffer[Index] == '\\') {
break; } }
//
// At this point Index denotes a backslash or is equal to the length
// of the string. So update string to be the remaining part.
// Decrement the length of the first component by the approprate
// amount
//
String->Buffer = &FirstComponent->Buffer[Index]; String->Length = (SHORT)(FirstComponent->Length - Index);
FirstComponent->Length = (SHORT)Index;
//
// And return to our caller.
//
return; }
//
// Internal support routine
//
COMPARISON_RESULTS EtfsCompareNames( IN PSTRING Name1, IN PSTRING Name2 )
/*++
Routine Description:
This routine takes two names and compare them ignoring case. This routine does not do implied dot or dbcs processing.
Arguments:
Name1 - Supplies the first name to compare
Name2 - Supplies the second name to compare
Return Value:
LessThan if Name1 is lexically less than Name2 EqualTo if Name1 is lexically equal to Name2 GreaterThan if Name1 is lexically greater than Name2
--*/
{ ULONG i; ULONG MinimumLength;
//
// Compute the smallest of the two name lengths
//
MinimumLength = (Name1->Length < Name2->Length ? Name1->Length : Name2->Length);
//
// Now compare each character in the names.
//
for (i = 0; i < MinimumLength; i += 1) {
if (ToUpper(Name1->Buffer[i]) < ToUpper(Name2->Buffer[i])) {
return LessThan; }
if (ToUpper(Name1->Buffer[i]) > ToUpper(Name2->Buffer[i])) {
return GreaterThan; } }
//
// The names compared equal up to the smallest name length so
// now check the name lengths
//
if (Name1->Length < Name2->Length) {
return LessThan; }
if (Name1->Length > Name2->Length) {
return GreaterThan; }
return EqualTo; }
//
// Internal support routine.
//
ARC_STATUS EtfsSearchDirectory( IN PSTRING Name, OUT PBOOLEAN IsDirectory )
/*++
Routine Description:
This routine walks through the current directory in the Etfs context structure, looking for a match for 'Name'. We will find the first non-multi-extent, non-interleave file. We will ignore any version number for the file. The details about the file, if found, are stored in the Etfs context structure.
Arguments:
Name - This is the name of the file to search for.
IsDirectory - Supplies the address of a boolean where we store whether this is or is not a directory.
Return Value:
ESUCCESS is returned if the operation is successful. Otherwise, an unsuccessful status is returned that describes the reason for failure.
--*/
{ ARC_STATUS Status;
ULONG SectorOffset; ULONG SectorDiskOffset; ULONG DirentOffset; ULONG RemainingBytes;
BOOLEAN ReadSector; BOOLEAN SearchForMultiEnd;
UCHAR UnalignedBuffer[CD_SECTOR_SIZE + 256];
PUCHAR RawSector;
PRAW_DIR_REC RawDe;
COMPARISON_RESULTS ComparisonResult;
//
// Initialize the local variables.
//
RawSector = ALIGN_BUFFER( UnalignedBuffer );
SearchForMultiEnd = FALSE;
//
// Remember where we are within the disk, sector and directory file.
//
SectorOffset = EtfsStructureContext->DirSectorOffset; SectorDiskOffset = EtfsStructureContext->DirDiskOffset - SectorOffset; DirentOffset = 0;
ReadSector = FALSE;
//
// If this is the root directory, then we can return immediately.
//
if (Name->Length == 1 && *Name->Buffer == '\\') {
*IsDirectory = TRUE;
//
// The structure context is already filled in.
//
return ESUCCESS; }
//
// Compute the remaining bytes in this sector.
//
RemainingBytes = CD_SECTOR_SIZE - SectorOffset;
//
// Loop until the directory is exhausted or a matching dirent for the
// target name is found.
//
while (TRUE) {
//
// If the current offset is beyond the end of the directory,
// raise an appropriate status.
//
if (DirentOffset >= EtfsStructureContext->DirSize) {
return ENOENT; }
//
// If the remaining bytes in this sector is less than the
// minimum needed for a dirent, then move to the next sector.
//
if (RemainingBytes < MIN_DIR_REC_SIZE) {
SectorDiskOffset += CD_SECTOR_SIZE; DirentOffset += RemainingBytes; SectorOffset = 0; RemainingBytes = CD_SECTOR_SIZE; ReadSector = FALSE;
continue; }
//
// If we have not read in the sector, do so now.
//
if (!ReadSector) {
Status = EtfsReadDisk( EtfsFileTableEntry->DeviceId, SectorDiskOffset, CD_SECTOR_SIZE, RawSector, CACHE_NEW_DATA );
if (Status != ESUCCESS) {
return Status; }
ReadSector = TRUE; }
//
// If the first byte of the next dirent is '\0', then we move to
// the next sector.
//
if (*(RawSector + SectorOffset) == '\0') {
SectorDiskOffset += CD_SECTOR_SIZE; DirentOffset += RemainingBytes; SectorOffset = 0; RemainingBytes = CD_SECTOR_SIZE; ReadSector = FALSE;
continue; }
RawDe = (PRAW_DIR_REC) ((PUCHAR) RawSector + SectorOffset);
//
// If the size of this dirent extends beyond the end of this sector
// we abort the search.
//
if ((ULONG)RawDe->DirLen > RemainingBytes) {
return EINVAL; }
//
// We have correctly found the next dirent. We first check whether
// we are looking for the last dirent for a multi-extent.
//
if (SearchForMultiEnd) {
//
// If this is the last of a multi-extent we change our search
// state.
//
if (!FlagOn( DE_FILE_FLAGS( EtfsStructureContext->IsIsoVol, RawDe ), ISO_ATTR_MULTI )) {
SearchForMultiEnd = TRUE; }
//
// If this is a multi-extent dirent, we change our search state.
//
} else if (FlagOn( DE_FILE_FLAGS( EtfsStructureContext->IsIsoVol, RawDe ), ISO_ATTR_MULTI )) {
SearchForMultiEnd = TRUE;
//
// If this is a file match, we update the Etfs context structure
// and the 'IsDirectory' flag.
//
} else {
ComparisonResult = EtfsFileMatch( RawDe, Name );
if (ComparisonResult == EqualTo) {
EtfsGetDirectoryInfo( RawDe, EtfsStructureContext->IsIsoVol, &EtfsStructureContext->DirSectorOffset, &EtfsStructureContext->DirDiskOffset, &EtfsStructureContext->DirSize );
*IsDirectory = FlagOn( DE_FILE_FLAGS( EtfsStructureContext->IsIsoVol, RawDe ), ISO_ATTR_DIRECTORY );
return ESUCCESS;
//
// If we have passed this file in the directory, then
// exit with the appropriate error code.
//
} else if (ComparisonResult == GreaterThan) {
return ENOENT; } }
//
// Otherwise we simply compute the next sector offset, disk offset
// and file offset.
//
SectorOffset += RawDe->DirLen; DirentOffset += RawDe->DirLen; RemainingBytes -= RawDe->DirLen; }
return ESUCCESS; }
//
// Internal support routine.
//
VOID EtfsGetDirectoryInfo( IN PRAW_DIR_REC DirEntry, IN BOOLEAN IsoVol, OUT PULONG SectorOffset, OUT PULONG DiskOffset, OUT PULONG Length )
/*++
Routine Description:
This routine takes a pointer to a raw directory structure on the disk and computes the file size, disk offset and file length for the directory entry.
Arguments:
DirEntry - This points to raw data from the disk.
IsoVol - Boolean indicating that this is an ISO volume.
SectorOffset - This supplies the address to store the sector offset of the start of the disk data.
DiskOffset - This supplies the address to store the disk offset of the start of the disk data.
Length - This supplies the address to store the number of bytes in the file referred by this disk directory.
Return Value:
None.
--*/
{ //
// The disk offset is length of the Xar blocks added to the starting
// location for the file.
//
CopyUshort2( DiskOffset, DirEntry->FileLoc ); *DiskOffset *= EtfsStructureContext->LbnBlockSize; *DiskOffset += (DirEntry->XarLen * EtfsStructureContext->LbnBlockSize);
//
// The sector offset is the least significant bytes of the disk offset.
//
*SectorOffset = *DiskOffset & (CD_SECTOR_SIZE - 1);
//
// The file size is pulled straight from the dirent. We round it
// to a sector size to protect us from faulty disks if this is a
// directory. Otherwise we use it directly from the dirent.
//
CopyUshort2( Length, DirEntry->DataLen );
if (FlagOn( DE_FILE_FLAGS( IsoVol, DirEntry ), ISO_ATTR_DIRECTORY )) {
*Length += (*SectorOffset + CD_SECTOR_SIZE - 1); *Length &= ~(CD_SECTOR_SIZE - 1); *Length -= *SectorOffset; }
return; }
//
// Internal support routine.
//
COMPARISON_RESULTS EtfsFileMatch( IN PRAW_DIR_REC DirEntry, IN PSTRING FileName )
{ STRING DirentString; ULONG Count;
PUCHAR StringPtr;
//
// We never match either '\0' or '\1'. We will return 'LessThan' in
// all of these cases.
//
if (DirEntry->FileIdLen == 1 && (DirEntry->FileId[0] == '\0' || DirEntry->FileId[0] == '\1')) {
return LessThan; }
//
// We assume that we can use the entire file name in the dirent.
//
DirentString.Length = DirEntry->FileIdLen; DirentString.Buffer = DirEntry->FileId;
//
// We walk backwards through the dirent name to check for the
// existance of a ';' character. We then set the string length
// to this position.
//
StringPtr = DirentString.Buffer + DirentString.Length - 1; Count = DirentString.Length;
while (Count--) {
if (*StringPtr == ';') {
DirentString.Length = (SHORT)Count; break; }
StringPtr--; }
//
// We also check for a terminating '.' character and truncate it.
//
StringPtr = DirentString.Buffer + DirentString.Length - 1; Count = DirentString.Length;
while (Count--) {
if (*StringPtr == '.') {
DirentString.Length = (SHORT)Count;
} else {
break; }
StringPtr--; }
//
// We now have the two filenames to compare. The result of this
// operation is simply the comparison of the two of them.
//
DirentString.MaximumLength = DirentString.Length;
return EtfsCompareNames( &DirentString, FileName ); }
#endif
|