1516 lines
33 KiB
C
1516 lines
33 KiB
C
/*++
|
||
|
||
Copyright (c) 1991 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
HpfsBoot.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the Hpfs boot file system used by the operating
|
||
system loader.
|
||
|
||
Author:
|
||
|
||
Gary Kimura [GaryKi] 19-Jul-1991
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
//
|
||
// Stuff to get around the fact that we include both Fat, Hpfs, and Ntfs include
|
||
// environments
|
||
//
|
||
|
||
#define VBO ULONG
|
||
#define LBO ULONG
|
||
|
||
#include "bootlib.h"
|
||
|
||
BOOTFS_INFO HpfsBootFsInfo={L"pinball"};
|
||
|
||
|
||
//
|
||
// Local procedure prototypes.
|
||
//
|
||
|
||
ARC_STATUS
|
||
HpfsReadDisk(
|
||
IN ULONG DeviceId,
|
||
IN ULONG Lbo,
|
||
IN ULONG ByteCount,
|
||
IN OUT PVOID Buffer
|
||
);
|
||
|
||
VOID
|
||
HpfsFirstComponent(
|
||
IN OUT PSTRING String,
|
||
OUT PSTRING FirstComponent
|
||
);
|
||
|
||
typedef enum _COMPARISON_RESULTS {
|
||
LessThan = -1,
|
||
EqualTo = 0,
|
||
GreaterThan = 1
|
||
} COMPARISON_RESULTS;
|
||
|
||
COMPARISON_RESULTS
|
||
HpfsCompareNames(
|
||
IN PSTRING Name1,
|
||
IN PSTRING Name2
|
||
);
|
||
|
||
ARC_STATUS
|
||
HpfsSearchDirectory(
|
||
IN LBN Fnode,
|
||
IN PSTRING Name,
|
||
OUT PBOOLEAN IsDirectory,
|
||
OUT PLBN FoundLbn,
|
||
OUT PULONG FileSize
|
||
);
|
||
|
||
ARC_STATUS
|
||
HpfsLoadMcb(
|
||
IN LBN Fnode,
|
||
IN VBN StartingVbn
|
||
);
|
||
|
||
ARC_STATUS
|
||
HpfsVbnToLbn(
|
||
IN VBN Vbn,
|
||
OUT PLBN Lbn,
|
||
OUT PULONG ByteCount
|
||
);
|
||
|
||
//
|
||
// The following macro upcases a single ascii character
|
||
//
|
||
|
||
#define ToUpper(C) ((((C) >= 'a') && ((C) <= 'z')) ? (C) - 'a' + 'A' : (C))
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
PHPFS_STRUCTURE_CONTEXT HpfsStructureContext;
|
||
|
||
//
|
||
// File Descriptor - This is a pointer to the file descriptor for the current
|
||
// file operation that is active.
|
||
//
|
||
|
||
PBL_FILE_TABLE HpfsFileTableEntry;
|
||
|
||
//
|
||
// File entry table - This is a structure that provides entry to the Hpfs
|
||
// file system procedures. It is exported when a Hpfs file structure
|
||
// is recognized.
|
||
//
|
||
|
||
BL_DEVICE_ENTRY_TABLE HpfsDeviceEntryTable;
|
||
|
||
|
||
PBL_DEVICE_ENTRY_TABLE
|
||
IsHpfsFileStructure (
|
||
IN ULONG DeviceId,
|
||
IN PVOID StructureContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine determines if the partition on the specified channel
|
||
contains a Hpfs 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 Hpfs file structure context.
|
||
|
||
Return Value:
|
||
|
||
A pointer to the Hpfs entry table is returned if the partition is
|
||
recognized as containing a Hpfs volume. Otherwise, NULL is returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
ARC_STATUS Status;
|
||
|
||
UCHAR UnalignedSuperSector[SECTOR_SIZE+256];
|
||
UCHAR UnalignedSpareSector[SECTOR_SIZE+256];
|
||
|
||
PSUPER_SECTOR SuperSector;
|
||
PSPARE_SECTOR SpareSector;
|
||
|
||
//
|
||
// Capture in our global variable the Hpfs Structure context record
|
||
//
|
||
|
||
HpfsStructureContext = (PHPFS_STRUCTURE_CONTEXT)StructureContext;
|
||
RtlZeroMemory((PVOID)HpfsStructureContext, sizeof(HPFS_STRUCTURE_CONTEXT));
|
||
|
||
//
|
||
// Compute the properly aligned buffers for reading in our sectors
|
||
//
|
||
|
||
SuperSector = ALIGN_BUFFER(UnalignedSuperSector);
|
||
SpareSector = ALIGN_BUFFER(UnalignedSpareSector);
|
||
|
||
//
|
||
// Read in the super and sector
|
||
//
|
||
|
||
if ((HpfsReadDisk(DeviceId, SUPER_SECTOR_LBN * 512, 512, SuperSector) != ESUCCESS) ||
|
||
(HpfsReadDisk(DeviceId, SPARE_SECTOR_LBN * 512, 512, SpareSector) != ESUCCESS)) {
|
||
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// Check the signature for both sectors.
|
||
//
|
||
|
||
if ((SuperSector->Signature1 != SUPER_SECTOR_SIGNATURE1) ||
|
||
(SuperSector->Signature2 != SUPER_SECTOR_SIGNATURE2) ||
|
||
(SpareSector->Signature1 != SPARE_SECTOR_SIGNATURE1) ||
|
||
(SpareSector->Signature2 != SPARE_SECTOR_SIGNATURE2)) {
|
||
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// Initialize the file entry table.
|
||
//
|
||
|
||
HpfsDeviceEntryTable.Open = HpfsOpen;
|
||
HpfsDeviceEntryTable.Close = HpfsClose;
|
||
HpfsDeviceEntryTable.Read = HpfsRead;
|
||
HpfsDeviceEntryTable.Seek = HpfsSeek;
|
||
HpfsDeviceEntryTable.Write = HpfsWrite;
|
||
HpfsDeviceEntryTable.GetFileInformation = HpfsGetFileInformation;
|
||
HpfsDeviceEntryTable.SetFileInformation = HpfsSetFileInformation;
|
||
HpfsDeviceEntryTable.BootFsInfo = &HpfsBootFsInfo;
|
||
|
||
//
|
||
// And return the address of the table to our caller.
|
||
//
|
||
|
||
return &HpfsDeviceEntryTable;
|
||
}
|
||
|
||
|
||
ARC_STATUS
|
||
HpfsClose (
|
||
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
|
||
HpfsGetFileInformation (
|
||
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 if the open operation is successful. Otherwise,
|
||
an unsuccessful status is returned that describes the reason for failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i;
|
||
|
||
HpfsFileTableEntry = &BlFileTable[FileId];
|
||
|
||
//
|
||
// Zero out the buffer, and fill in its non-zero values
|
||
//
|
||
|
||
RtlZeroMemory(Buffer, sizeof(FILE_INFORMATION));
|
||
|
||
Buffer->EndingAddress.LowPart = HpfsFileTableEntry->u.HpfsFileContext.FileSize;
|
||
|
||
Buffer->CurrentPosition.LowPart = HpfsFileTableEntry->Position.LowPart;
|
||
|
||
Buffer->FileNameLength = HpfsFileTableEntry->FileNameLength;
|
||
|
||
for (i = 0; i < HpfsFileTableEntry->FileNameLength; i += 1) {
|
||
|
||
Buffer->FileName[i] = HpfsFileTableEntry->FileName[i];
|
||
}
|
||
|
||
return ESUCCESS;
|
||
}
|
||
|
||
|
||
ARC_STATUS
|
||
HpfsOpen (
|
||
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;
|
||
|
||
UCHAR UnalignedSuperSector[SECTOR_SIZE+256];
|
||
PSUPER_SECTOR SuperSector;
|
||
|
||
LBN Fnode;
|
||
|
||
STRING PathName;
|
||
|
||
//
|
||
// Save the address of the file table entry, context area, and the device
|
||
// id in use.
|
||
//
|
||
|
||
HpfsFileTableEntry = &BlFileTable[*FileId];
|
||
HpfsStructureContext = (PHPFS_STRUCTURE_CONTEXT)HpfsFileTableEntry->StructureContext;
|
||
|
||
DeviceId = HpfsFileTableEntry->DeviceId;
|
||
|
||
//
|
||
// Compute the properly aligned buffers for reading in our sectors
|
||
//
|
||
|
||
SuperSector = ALIGN_BUFFER(UnalignedSuperSector);
|
||
|
||
//
|
||
// Read in the Super sector.
|
||
//
|
||
|
||
if ((Status = HpfsReadDisk(DeviceId, SUPER_SECTOR_LBN * 512, 512, SuperSector)) != ESUCCESS) {
|
||
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Double check that the super sector is real
|
||
//
|
||
|
||
if ((SuperSector->Signature1 != SUPER_SECTOR_SIGNATURE1) ||
|
||
(SuperSector->Signature2 != SUPER_SECTOR_SIGNATURE2)) {
|
||
|
||
return EIO;
|
||
}
|
||
|
||
//
|
||
// Get the root fnode lbn
|
||
//
|
||
|
||
Fnode = SuperSector->RootDirectoryFnode;
|
||
|
||
//
|
||
// Construct a file name descriptor from the input file name.
|
||
//
|
||
|
||
RtlInitString( &PathName, FileName );
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
while (PathName.Length > 0) {
|
||
|
||
STRING Name;
|
||
BOOLEAN IsDirectory;
|
||
ULONG FileSize;
|
||
|
||
//
|
||
// Extract the first component and search the directory for a match, but
|
||
// first copy the first part to the file name buffer in the file table entry
|
||
//
|
||
|
||
for (HpfsFileTableEntry->FileNameLength = 0;
|
||
(((USHORT)HpfsFileTableEntry->FileNameLength < PathName.Length) &&
|
||
(PathName.Buffer[HpfsFileTableEntry->FileNameLength] != '\\'));
|
||
HpfsFileTableEntry->FileNameLength += 1) {
|
||
|
||
HpfsFileTableEntry->FileName[HpfsFileTableEntry->FileNameLength] =
|
||
PathName.Buffer[HpfsFileTableEntry->FileNameLength];
|
||
}
|
||
|
||
HpfsFirstComponent( &PathName, &Name );
|
||
|
||
if ((Status = HpfsSearchDirectory(Fnode, &Name, &IsDirectory, &Fnode, &FileSize)) != ESUCCESS) {
|
||
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// If we didn't get back a directory then we're about to stop either
|
||
// with an error or with success
|
||
//
|
||
|
||
if (!IsDirectory) {
|
||
|
||
//
|
||
// if the path name still has some characters in it then the
|
||
// caller wanted to continue but we can't because we're not
|
||
// currently sitting on a directory
|
||
//
|
||
|
||
if (PathName.Length > 0) {
|
||
|
||
return ENOENT;
|
||
}
|
||
|
||
//
|
||
// Load in the mcb for the file, set the fnode in structure
|
||
// context, file size, open flags, position, and return
|
||
// success to our caller
|
||
//
|
||
|
||
if ((Status = HpfsLoadMcb(Fnode, 0)) != ESUCCESS) {
|
||
|
||
return Status;
|
||
}
|
||
|
||
HpfsStructureContext->Fnode = Fnode;
|
||
|
||
HpfsFileTableEntry->u.HpfsFileContext.FileSize = FileSize;
|
||
|
||
HpfsFileTableEntry->Flags.Open = 1;
|
||
HpfsFileTableEntry->Flags.Read = 1;
|
||
HpfsFileTableEntry->Position.LowPart = 0;
|
||
HpfsFileTableEntry->Position.HighPart = 0;
|
||
|
||
return ESUCCESS;
|
||
}
|
||
}
|
||
|
||
//
|
||
// 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
|
||
HpfsRead (
|
||
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 RemainingSectorCount;
|
||
|
||
//
|
||
// Save the address of the file table entry, context area, and the device
|
||
// id in use.
|
||
//
|
||
|
||
HpfsFileTableEntry = &BlFileTable[FileId];
|
||
HpfsStructureContext = (PHPFS_STRUCTURE_CONTEXT)HpfsFileTableEntry->StructureContext;
|
||
|
||
DeviceId = HpfsFileTableEntry->DeviceId;
|
||
|
||
//
|
||
// Clear the transfer count
|
||
//
|
||
|
||
*Transfer = 0;
|
||
|
||
//
|
||
// Read in runs (i.e., sectors) until the byte count goes to zero
|
||
//
|
||
|
||
while (Length > 0) {
|
||
|
||
VBN Vbn;
|
||
LBN Lbn;
|
||
|
||
ULONG CurrentRunByteCount;
|
||
|
||
//
|
||
// Compute the Vbn from the current byte position and then
|
||
// lookup the corresponding Lbn and current run length
|
||
//
|
||
|
||
Vbn = HpfsFileTableEntry->Position.LowPart / 512;
|
||
|
||
if ((Status = HpfsVbnToLbn( Vbn, &Lbn, &CurrentRunByteCount )) != ESUCCESS) {
|
||
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Now bias the run size by the offset we are into the
|
||
// sector
|
||
//
|
||
|
||
CurrentRunByteCount -= (HpfsFileTableEntry->Position.LowPart & 511);
|
||
|
||
//
|
||
// while there are sectors to be read in from the current run
|
||
// length and we haven't exhausted the request we loop reading
|
||
// in sectors. The biggest request we'll handle is only 64
|
||
// contiguous sectors per physical read. So we might need to loop
|
||
// through the run.
|
||
//
|
||
|
||
while ((Length > 0) && (CurrentRunByteCount > 0)) {
|
||
|
||
ULONG i;
|
||
ULONG Lbo;
|
||
|
||
//
|
||
// Compute the size of the next physical read
|
||
//
|
||
|
||
i = (Length < (64 * 512) ? Length : (64 * 512));
|
||
i = (i < CurrentRunByteCount ? i : CurrentRunByteCount);
|
||
|
||
//
|
||
// Don't read beyond the eof
|
||
//
|
||
|
||
if (i + HpfsFileTableEntry->Position.LowPart >=
|
||
HpfsFileTableEntry->u.HpfsFileContext.FileSize) {
|
||
|
||
i = HpfsFileTableEntry->u.HpfsFileContext.FileSize -
|
||
HpfsFileTableEntry->Position.LowPart;
|
||
|
||
if (i == 0) {
|
||
|
||
return ESUCCESS;
|
||
}
|
||
|
||
Length = i;
|
||
}
|
||
|
||
//
|
||
// Compute the lbo to read, and then issue the read.
|
||
//
|
||
|
||
Lbo = (Lbn * 512) | (HpfsFileTableEntry->Position.LowPart & 511);
|
||
|
||
if ((Status = HpfsReadDisk( DeviceId, Lbo, i, Buffer)) != ESUCCESS) {
|
||
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Update the remaining length, Current run byte count
|
||
// and new Lbn offset
|
||
//
|
||
|
||
Length -= i;
|
||
CurrentRunByteCount -= i;
|
||
|
||
Lbn += i/512;
|
||
|
||
//
|
||
// Update the current position and the number of bytes transfered
|
||
//
|
||
|
||
HpfsFileTableEntry->Position.LowPart += i;
|
||
*Transfer += i;
|
||
|
||
//
|
||
// Update buffer to point to the next byte location to fill in
|
||
//
|
||
|
||
Buffer = (PCHAR)Buffer + i;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we get here then remaining sector count is zero so we can
|
||
// return success to our caller
|
||
//
|
||
|
||
return ESUCCESS;
|
||
}
|
||
|
||
|
||
ARC_STATUS
|
||
HpfsSeek (
|
||
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.HpfsFileContext.FileSize) {
|
||
|
||
return EINVAL;
|
||
}
|
||
|
||
//
|
||
// Otherwise set the new position and return to our caller
|
||
//
|
||
|
||
BlFileTable[FileId].Position.LowPart = NewPosition;
|
||
|
||
return ESUCCESS;
|
||
}
|
||
|
||
|
||
ARC_STATUS
|
||
HpfsSetFileInformation (
|
||
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:
|
||
|
||
ESUCCESS is returned if the read operation is successful. Otherwise,
|
||
an unsuccessful status is returned that describes the reason for failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
return EROFS;
|
||
}
|
||
|
||
|
||
ARC_STATUS
|
||
HpfsWrite (
|
||
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;
|
||
}
|
||
|
||
|
||
ARC_STATUS
|
||
HpfsInitialize (
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes the hpfs boot filesystem.
|
||
Currently this is a no-op.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
ESUCCESS.
|
||
|
||
--*/
|
||
|
||
{
|
||
return ESUCCESS;
|
||
}
|
||
|
||
|
||
//
|
||
// Internal support routine
|
||
//
|
||
|
||
ARC_STATUS
|
||
HpfsReadDisk(
|
||
IN ULONG DeviceId,
|
||
IN ULONG Lbo,
|
||
IN ULONG ByteCount,
|
||
IN OUT PVOID Buffer
|
||
)
|
||
|
||
/*++
|
||
|
||
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 to start reading from.
|
||
|
||
ByteCount - Supplies the number of bytes to read.
|
||
|
||
Buffer - Supplies a pointer to the buffer to read the bytes into.
|
||
|
||
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;
|
||
}
|
||
|
||
//
|
||
// Seek to the appropriate offset on the volume
|
||
//
|
||
|
||
LargeLbo.LowPart = Lbo;
|
||
LargeLbo.HighPart = 0;
|
||
|
||
if ((Status = ArcSeek( DeviceId, &LargeLbo, SeekAbsolute )) != ESUCCESS) {
|
||
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Issue the arc read request
|
||
//
|
||
|
||
if ((Status = ArcRead( DeviceId, Buffer, ByteCount, &i)) != 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
|
||
HpfsFirstComponent(
|
||
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
|
||
HpfsCompareNames(
|
||
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 ((UCHAR)(ToUpper(Name1->Buffer[i])) < (UCHAR)(ToUpper(Name2->Buffer[i]))) {
|
||
|
||
return LessThan;
|
||
}
|
||
|
||
if ((UCHAR)(ToUpper(Name1->Buffer[i])) > (UCHAR)(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
|
||
HpfsSearchDirectory(
|
||
IN LBN Fnode,
|
||
IN PSTRING Name,
|
||
OUT PBOOLEAN IsDirectory,
|
||
OUT PLBN FoundLbn,
|
||
OUT PULONG FileSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine searches the indicated directory for a matching name
|
||
|
||
Arguments:
|
||
|
||
Fnode - Supplies the fnode of the directory to search
|
||
|
||
Name - Supplies the name to search for
|
||
|
||
Directory - Recieves an indication of the found file is a directory
|
||
or a file.
|
||
|
||
FoundLbn - Receives the lbn of the fnode for the file/directory if
|
||
one is found.
|
||
|
||
FileSize - Receives the size of the file if one is found.
|
||
|
||
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 DeviceId;
|
||
|
||
UCHAR UnalignedFnodeSector[SECTOR_SIZE+256];
|
||
UCHAR UnalignedDirDiskBuffer[(SECTOR_SIZE*4)+256];
|
||
|
||
PFNODE_SECTOR FnodeSector;
|
||
PDIRECTORY_DISK_BUFFER DirDiskBuffer;
|
||
|
||
LBN DirDiskBufferLbn;
|
||
|
||
PPBDIRENT Dirent;
|
||
|
||
//
|
||
// Compute the properly aligned buffers for reading in our sectors
|
||
//
|
||
|
||
FnodeSector = ALIGN_BUFFER(UnalignedFnodeSector);
|
||
DirDiskBuffer = ALIGN_BUFFER(UnalignedDirDiskBuffer);
|
||
|
||
//
|
||
// Capture the device id from our global variable
|
||
//
|
||
|
||
DeviceId = HpfsFileTableEntry->DeviceId;
|
||
|
||
//
|
||
// Read in the fnode for the directory, and check that it is real
|
||
//
|
||
|
||
if ((Status = HpfsReadDisk(DeviceId, Fnode * 512, 512, FnodeSector)) != ESUCCESS) {
|
||
|
||
return Status;
|
||
}
|
||
|
||
if (FnodeSector->Signature != FNODE_SECTOR_SIGNATURE) {
|
||
|
||
return EIO;
|
||
}
|
||
|
||
//
|
||
// Now setup the lbn for the first dir disk buffer
|
||
//
|
||
|
||
DirDiskBufferLbn = FnodeSector->Allocation.Leaf[0].Lbn;
|
||
|
||
//
|
||
// the following loop is executed until we either find our entry
|
||
// or have gone past where it could possible be
|
||
//
|
||
|
||
while (TRUE) {
|
||
|
||
BOOLEAN ReadNewDirDiskBuffer;
|
||
|
||
//
|
||
// Read in the next dir disk buffer, and check that it is real
|
||
//
|
||
|
||
if ((Status = HpfsReadDisk( DeviceId,
|
||
DirDiskBufferLbn * 512,
|
||
512 * 4,
|
||
DirDiskBuffer )) != ESUCCESS) {
|
||
|
||
return Status;
|
||
}
|
||
|
||
if (DirDiskBuffer->Signature != DIRECTORY_DISK_BUFFER_SIGNATURE) {
|
||
|
||
return EIO;
|
||
}
|
||
|
||
//
|
||
// Search each dirent in the dir disk buffer, we continue the
|
||
// loop until we either exit or read new dir disk buffer is set
|
||
// to true.
|
||
//
|
||
|
||
ReadNewDirDiskBuffer = FALSE;
|
||
|
||
for ( Dirent = (PPBDIRENT)GetFirstDirent( DirDiskBuffer );
|
||
!ReadNewDirDiskBuffer;
|
||
Dirent = (PPBDIRENT)GetNextDirent( Dirent )) {
|
||
|
||
STRING String;
|
||
COMPARISON_RESULTS CompareResults;
|
||
|
||
//
|
||
// Get a string for the file name in the dirent and then
|
||
// compare the names against each other
|
||
//
|
||
|
||
String.Length = Dirent->FileNameLength;
|
||
String.Buffer = &Dirent->FileName[0];
|
||
|
||
CompareResults = HpfsCompareNames( Name, &String );
|
||
|
||
//
|
||
// If the names are equal then we've found our match and we
|
||
// need to figure out if this is a directory, store the
|
||
// found fnode and return success
|
||
//
|
||
|
||
if (CompareResults == EqualTo) {
|
||
|
||
*IsDirectory = FlagOn(Dirent->FatFlags, FAT_DIRENT_ATTR_DIRECTORY);
|
||
|
||
*FoundLbn = Dirent->Fnode;
|
||
*FileSize = Dirent->FileSize;
|
||
|
||
return ESUCCESS;
|
||
|
||
//
|
||
// If the results is less than then we've gone too far in
|
||
// the current dir disk buffer. If we have a down pointer then
|
||
// there are other buffers to search through otherwise the
|
||
// name doesn't exist in the directory
|
||
//
|
||
|
||
} else if (CompareResults == LessThan) {
|
||
|
||
if (FlagOn(Dirent->Flags, DIRENT_BTREE_POINTER)) {
|
||
|
||
//
|
||
// Compute the new dir disk buffer to search and
|
||
// indicate to the for loop that we need to read in
|
||
// another dir disk buffer
|
||
//
|
||
|
||
DirDiskBufferLbn = GetBtreePointerInDirent( Dirent );
|
||
|
||
ReadNewDirDiskBuffer = TRUE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// We didn't find the name in the directory
|
||
//
|
||
|
||
return ENOENT;
|
||
}
|
||
|
||
//
|
||
// Otherwise the result is greater than which means we need to
|
||
// compare against the next dirent.
|
||
//
|
||
|
||
} else {
|
||
|
||
NOTHING;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Internal support routine
|
||
//
|
||
|
||
ARC_STATUS
|
||
HpfsLoadMcb(
|
||
IN LBN Fnode,
|
||
IN VBN StartingVbn
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine loads into our cache (i.e., structure context's boot mcb)
|
||
the retrieval information for the starting vbn.
|
||
|
||
Arguments:
|
||
|
||
Fnode - Supplies the lbn for the fnode for the file we're reading
|
||
|
||
StartingVbn - Supplies the Vbn that we want to load
|
||
|
||
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;
|
||
|
||
PHPFS_BOOT_MCB Mcb = &HpfsStructureContext->BootMcb;
|
||
ULONG DeviceId;
|
||
|
||
UCHAR UnalignedFnodeSector[SECTOR_SIZE+256];
|
||
UCHAR UnalignedAllocationSector[SECTOR_SIZE+256];
|
||
|
||
PFNODE_SECTOR FnodeSector;
|
||
PALLOCATION_SECTOR AllocationSector;
|
||
|
||
PALLOCATION_HEADER AllocationHeader;
|
||
PALLOCATION_LEAF Leafs;
|
||
PALLOCATION_NODE Nodes;
|
||
|
||
ULONG i;
|
||
|
||
//
|
||
// Compute the properly aligned buffers for reading in our sectors
|
||
//
|
||
|
||
FnodeSector = ALIGN_BUFFER(UnalignedFnodeSector);
|
||
AllocationSector = ALIGN_BUFFER(UnalignedAllocationSector);
|
||
|
||
//
|
||
// Capture the device id from our global variable
|
||
//
|
||
|
||
DeviceId = HpfsFileTableEntry->DeviceId;
|
||
|
||
//
|
||
// Read in the fnode for the file, and check that it is real
|
||
//
|
||
|
||
if ((Status = HpfsReadDisk(DeviceId, Fnode * 512, 512, FnodeSector)) != ESUCCESS) {
|
||
|
||
return Status;
|
||
}
|
||
|
||
if (FnodeSector->Signature != FNODE_SECTOR_SIGNATURE) {
|
||
|
||
return EIO;
|
||
}
|
||
|
||
//
|
||
// Setup the allocation header, leafs and nodes
|
||
//
|
||
|
||
AllocationHeader = &FnodeSector->AllocationHeader;
|
||
Leafs = &FnodeSector->Allocation.Leaf[0];
|
||
Nodes = &FnodeSector->Allocation.Node[0];
|
||
|
||
//
|
||
// While we have nodes and not leafs we need to search for the entry
|
||
// containing our starting vbn and then subsearch in that allocation
|
||
// sector
|
||
//
|
||
|
||
while (FlagOn(AllocationHeader->Flags, ALLOCATION_BLOCK_NODE)) {
|
||
|
||
for (i = 0; i < AllocationHeader->OccupiedCount; i += 1) {
|
||
|
||
if (StartingVbn < Nodes[i].Vbn) {
|
||
|
||
//
|
||
// We found a node that contains our starting vbn so
|
||
// read in the next allocation sector and check that
|
||
// it is real.
|
||
|
||
if ((Status = HpfsReadDisk( DeviceId,
|
||
Nodes[i].Lbn * 512,
|
||
512,
|
||
AllocationSector )) != ESUCCESS) {
|
||
|
||
return Status;
|
||
}
|
||
|
||
if (AllocationSector->Signature != ALLOCATION_SECTOR_SIGNATURE) {
|
||
|
||
return EIO;
|
||
}
|
||
|
||
//
|
||
// Setup the allocation header, leafs and nodes, and then
|
||
// break out of the for loop and let our while loop check
|
||
// if we have allocation leafs or nodes.
|
||
//
|
||
|
||
AllocationHeader = &AllocationSector->AllocationHeader;
|
||
Leafs = &AllocationSector->Allocation.Leaf[0];
|
||
Nodes = &AllocationSector->Allocation.Node[0];
|
||
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now the allocation header indictes that we have leaf entries
|
||
// so we can simply load up the cached mcb. We set the in use here
|
||
// The most entries we'll ever preload is the maximum available in
|
||
// an allocation sector (i.e., 40).
|
||
//
|
||
|
||
Mcb->InUse = AllocationHeader->OccupiedCount;
|
||
|
||
for (i = 0; i < AllocationHeader->OccupiedCount; i += 1) {
|
||
|
||
//
|
||
// For each entry we set the vbn and lbn value. We also
|
||
// set the next vbn value to get the size of the run.
|
||
// This means that we'll really be double setting each vbn
|
||
// value (except the first and last entry) but that's okay
|
||
// because they better compute the same value
|
||
//
|
||
|
||
Mcb->Vbn[i] = Leafs[i].Vbn;
|
||
Mcb->Lbn[i] = Leafs[i].Lbn;
|
||
Mcb->Vbn[i+1] = Leafs[i].Vbn + Leafs[i].Length;
|
||
}
|
||
|
||
//
|
||
// We're all done so return success to our caller
|
||
//
|
||
|
||
return ESUCCESS;
|
||
}
|
||
|
||
|
||
//
|
||
// Internal support routine
|
||
//
|
||
|
||
ARC_STATUS
|
||
HpfsVbnToLbn(
|
||
IN VBN Vbn,
|
||
OUT PLBN Lbn,
|
||
OUT PULONG ByteCount
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine computes the run denoted by the input vbn to into its
|
||
corresponding lbn and also returns the number of bytes remaining in
|
||
the run. For all cases this byte count will be a multiple of 512.
|
||
|
||
Arguments:
|
||
|
||
Vbn - Supplies the Vbn to match
|
||
|
||
Lbn - Recieves the corresponding Lbn
|
||
|
||
ByteCount - Receives the number of bytes remaining in the run
|
||
|
||
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;
|
||
|
||
PHPFS_BOOT_MCB Mcb = &HpfsStructureContext->BootMcb;
|
||
ULONG i;
|
||
|
||
//
|
||
// Check if the boot mcb has the range we're asking for. If it
|
||
// doesn't then call load mcb to load in the right range.
|
||
//
|
||
|
||
if ((Vbn < Mcb->Vbn[0]) || (Vbn >= Mcb->Vbn[Mcb->InUse])) {
|
||
|
||
if ((Status = HpfsLoadMcb(HpfsStructureContext->Fnode, Vbn)) != ESUCCESS) {
|
||
|
||
return Status;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now search for the slot where the Vbn fits in the mcb. Note that
|
||
// we could also do a binary search here but because the run count
|
||
// is probably small the extra overhead of a binary search doesn't
|
||
// buy us anything
|
||
//
|
||
|
||
for (i = 0; i < Mcb->InUse; i += 1) {
|
||
|
||
//
|
||
// We found our slot if the vbn we're after is less then the
|
||
// next mcb's vbn
|
||
//
|
||
|
||
if (Vbn < Mcb->Vbn[i+1]) {
|
||
|
||
//
|
||
// Compute the corresponding lbn which is the stored lbn plus
|
||
// the difference between the stored vbn and the vbn we're
|
||
// looking up. Also compute the byte count which is the
|
||
// difference between the current vbn we're looking up and
|
||
// the vbn for the next run, all multiplied by 512.
|
||
//
|
||
|
||
*Lbn = Mcb->Lbn[i] + (Vbn - Mcb->Vbn[i]);
|
||
|
||
*ByteCount = (Mcb->Vbn[i+1] - Vbn) * 512;
|
||
|
||
//
|
||
// and return success to our caller
|
||
//
|
||
|
||
return ESUCCESS;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we really reach here we have an error in the mcb
|
||
//
|
||
|
||
return EIO;
|
||
}
|