singrdk/base/boot/SingLdrPc/blfat.cpp

1538 lines
34 KiB
C++

//++
//
// Copyright (c) Microsoft Corporation
//
// Module Name:
//
// blfat.cpp
//
// Abstract:
//
// This module implements FAT support for the boot loader.
//
//--
#include "bl.h"
//
// MBR definitions.
//
#pragma pack(1)
#define MBR_BOOTABLE 0x80
typedef struct _MBR_PARTITION {
UINT8 Status;
UINT8 FirstSectorCHS[3];
UINT8 Type;
UINT8 LastSectorCHS[3];
UINT32 FirstSector;
UINT32 NumberOfSectors;
} MBR_PARTITION, *PMBR_PARTITION;
C_ASSERT(sizeof(MBR_PARTITION) == 16);
#define MBR_SIGNATURE 0xAA55
typedef struct _MBR {
UINT8 BootCode[446];
MBR_PARTITION Partition[4];
UINT16 Signature;
} MBR, *PMBR;
C_ASSERT(sizeof(MBR) == 512);
#pragma pack()
//
// FAT definitions.
//
#define FAT_SECTOR_SIZE 512
#define FAT_FIRST_DATA_CLUSTER 2
#define FAT16_CLUSTER_MASK 0xFFFF
#define FAT16_LINK_TERMINATOR 0xFFFF
#define FAT32_CLUSTER_MASK 0x0FFFFFFF
#define FAT32_LINK_TERMINATOR 0x0FFFFFFF
#pragma pack(1)
typedef struct __declspec(align(FAT_SECTOR_SIZE)) _FAT_SECTOR {
UINT8 Data[FAT_SECTOR_SIZE];
} FAT_SECTOR, *PFAT_SECTOR;
typedef struct _FAT16_BOOT_SECTOR {
UINT8 JumpInstruction[3];
UINT8 OemName[8];
UINT16 BytesPerSector;
UINT8 SectorsPerCluster;
UINT16 NumberOfReservedSectors;
UINT8 NumberOfFATs;
UINT16 NumberOfRootDirectoryEntries;
UINT16 TotalSectorCount16;
UINT8 Media;
UINT16 SectorsPerFAT;
UINT16 SectorsPerTrack;
UINT16 NumberOfHeads;
UINT32 NumberOfHiddenSectors;
UINT32 TotalSectorCount32;
UINT8 DriveNumber;
UINT8 Reserved1;
UINT8 ExtendedBootSignature;
UINT32 VolumeSerialNumber;
UINT8 VolumeLabel[11];
UINT8 FileSystemType[8];
UINT8 BootCode[448];
UINT16 Signature;
} FAT16_BOOT_SECTOR, *PFAT16_BOOT_SECTOR;
C_ASSERT(sizeof(FAT16_BOOT_SECTOR) == FAT_SECTOR_SIZE);
typedef struct _FAT32_BOOT_SECTOR {
UINT8 JumpInstruction[3];
UINT8 OemName[8];
UINT16 BytesPerSector;
UINT8 SectorsPerCluster;
UINT16 NumberOfReservedSectors;
UINT8 NumberOfFATs;
UINT16 NumberOfRootDirectoryEntries;
UINT16 TotalSectorCount16;
UINT8 Media;
UINT16 SectorsPerFAT16;
UINT16 SectorsPerTrack;
UINT16 NumberOfHeads;
UINT32 NumberOfHiddenSectors;
UINT32 TotalSectorCount32;
UINT32 SectorsPerFAT32;
UINT16 Flags;
UINT16 FileSystemVersion;
UINT32 RootDirectoryFirstCluster;
UINT16 FileSystemInfoSector;
UINT16 BackupBootSector;
UINT8 Reserved[12];
UINT8 DriveNumber;
UINT8 Reserved1;
UINT8 ExtendedBootSignature;
UINT32 VolumeSerialNumber;
UINT8 VolumeLabel[11];
UINT8 FileSystemType[8];
UINT8 BootCode[420];
UINT16 Signature;
} FAT32_BOOT_SECTOR, *PFAT32_BOOT_SECTOR;
C_ASSERT(sizeof(FAT32_BOOT_SECTOR) == FAT_SECTOR_SIZE);
typedef struct _FAT_BOOT_SECTOR {
union {
FAT16_BOOT_SECTOR Fat16;
FAT32_BOOT_SECTOR Fat32;
} u1;
} FAT_BOOT_SECTOR, *PFAT_BOOT_SECTOR;
C_ASSERT(sizeof(FAT_BOOT_SECTOR) == FAT_SECTOR_SIZE);
#define FAT_DIRECTORY_ENTRY_FREE 0xE5
#define FAT_DIRECTORY_ENTRY_LAST 0x00
#define FAT_ATTRIBUTE_READ_ONLY 0x01
#define FAT_ATTRIBUTE_HIDDEN 0x02
#define FAT_ATTRIBUTE_SYSTEM 0x04
#define FAT_ATTRIBUTE_VOLUME_ID 0x08
#define FAT_ATTRIBUTE_DIRECTORY 0x10
#define FAT_ATTRIBUTE_ARCHIVE 0x20
#define FAT_ATTRIBUTE_LONG_NAME (FAT_ATTRIBUTE_READ_ONLY | FAT_ATTRIBUTE_HIDDEN | FAT_ATTRIBUTE_SYSTEM | FAT_ATTRIBUTE_VOLUME_ID)
#define FAT_ATTRIBUTE_MASK 0x3F
#define FAT_LONG_NAME_TERMINATOR 0x40
#define FAT_LONG_NAME_ORDER_MASK 0x3F
typedef struct _FAT_DIRECTORY_ENTRY {
union {
struct {
UINT8 Name[11];
UINT8 Attribute;
UINT8 ReservedForNT;
UINT8 CreationTime[3];
UINT8 CreationDate[2];
UINT8 LastAccessDate[2];
UINT16 FirstClusterHigh;
UINT8 ModificationTime[2];
UINT8 ModificationDate[2];
UINT16 FirstClusterLow;
UINT32 Size;
} Short;
struct {
UINT8 Order;
WCHAR NameW1_5[5];
UINT8 Attribute;
UINT8 Type;
UINT8 Checksum;
WCHAR NameW6_11[6];
UINT16 Zero;
WCHAR NameW12_13[2];
} Long;
} u1;
} FAT_DIRECTORY_ENTRY, *PFAT_DIRECTORY_ENTRY;
C_ASSERT(sizeof(FAT_DIRECTORY_ENTRY) == 32);
#define FAT_MAX_PATH 255
typedef struct _FAT_NAME {
UINT8 ShortName[13];
UINT8 LongName[FAT_MAX_PATH + 1];
} FAT_NAME, *PFAT_NAME;
#pragma pack()
FAT_BOOT_SECTOR BlFatBootSector;
UINT32 BlFatBytesPerCluster;
UINT32 BlFatDataStart;
UINT8 BlFatDriveId;
INT13_DRIVE_PARAMETERS BlFatDriveParameters;
UINT32 BlFatLinkTerminator;
MBR BlFatMbr;
UINT32 BlFatNumberOfDataClusters;
UINT32 BlFatNumberOfRootDirectoryEntries;
UINT32 BlFatPartitionId;
UINT32 BlFatPartitionStart;
UINT32 BlFatPartitionSize;
PFAT_DIRECTORY_ENTRY BlFatRootDirectory;
UINT32 BlFatRootStart;
UINT32 BlFatSectorsPerCluster;
UINT32 BlFatTableStart;
FAT_SECTOR BlFatTemporaryBlock[64];
UINT16 BlFatTemporaryBlockCount = sizeof(BlFatTemporaryBlock) / sizeof(BlFatTemporaryBlock[0]);
UINT32 BlFatTotalSectorCount;
#define FAT_IS_DATA_CLUSTER(X) (((X) >= FAT_FIRST_DATA_CLUSTER) && (((X) - FAT_FIRST_DATA_CLUSTER) < BlFatNumberOfDataClusters))
#define FAT_DATA_CLUSTER_TO_SECTOR(X) (BlFatDataStart + (((X) - 2) * BlFatSectorsPerCluster))
#define FAT_IS_TERMINATOR(X) (((UINT32) (X)) == BlFatLinkTerminator)
#define BlFatHalt() BlFatHaltInternal(__LINE__)
BOOLEAN
(*BlFatGetNextCluster)(
UINT32 Cluster,
PUINT32 NextCluster
);
VOID
BlFatHaltInternal(
UINT32 Line
)
//++
//
// Routine Description:
//
// Prints an error message and halts.
//
//--
{
BlRtlPrintf("FAT: Error reading disk image!\n");
BlRtlHaltInternal(__FILE__, Line);
}
BOOLEAN
BlFatReadSector(
UINT32 FirstSector,
UINT32 NumberOfSectors,
PFAT_SECTOR Buffer
)
//++
//
// Routine Description:
//
// This function reads the specified range of sectors.
//
// Arguments:
//
// FirstSector - Supplies the first sector to read.
//
// NumberOfSectors - Supplies the number of sectors to read.
//
// Buffer - Receives data.
//
// Return Value:
//
// TRUE, if read operation was successful.
// FALSE, otherwise.
//
//--
{
UINT16 StepSize;
BLASSERT_PTR(FirstSector < BlFatTotalSectorCount, FirstSector);
BLASSERT(NumberOfSectors > 0);
BLASSERT(FirstSector + NumberOfSectors > FirstSector);
BLASSERT((FirstSector + NumberOfSectors) < BlFatTotalSectorCount);
while (NumberOfSectors > 0) {
if (NumberOfSectors < BlFatTemporaryBlockCount) {
StepSize = (UINT16) NumberOfSectors;
} else {
StepSize = BlFatTemporaryBlockCount;
}
if (BlRtlReadDrive(BlFatDriveId,
BlFatPartitionStart + FirstSector,
StepSize,
BlFatTemporaryBlock) == FALSE) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: I/O error reading sector %u on drive 0x%02x!\n",
BlFatPartitionStart + FirstSector,
BlFatDriveId);
#endif
return FALSE;
}
BlRtlCopyMemory(Buffer,
BlFatTemporaryBlock,
StepSize * FAT_SECTOR_SIZE);
FirstSector += StepSize;
NumberOfSectors -= StepSize;
Buffer += StepSize;
}
return TRUE;
}
BOOLEAN
BlFatDirectoryEntryToName(
PFAT_DIRECTORY_ENTRY ShortEntry,
PFAT_NAME Name,
PFAT_DIRECTORY_ENTRY TableStart
)
//++
//
// Routine Description:
//
// This function extracts FAT short and long names from the specified directory entry.
//
// Arguments:
//
// ShortEntry - Supplies a pointer to the short name entry.
//
// Name - Receives short and long names.
//
// TableStart - Supplies the start address for the directory table.
//
// Return Value:
//
// TRUE, if names were extracted successfully.
// FALSE, otherwise.
//
//--
{
UINT8 Character;
PFAT_DIRECTORY_ENTRY Entry;
UINT8 LongNameComponentIndex;
UINT32 SourceIndex;
UINT32 TargetIndex;
if ((ShortEntry->u1.Short.Attribute & FAT_ATTRIBUTE_MASK) == FAT_ATTRIBUTE_LONG_NAME) {
return FALSE;
}
//
// Extract short name.
//
TargetIndex = 0;
for (SourceIndex = 0; SourceIndex < 8; SourceIndex += 1) {
Character = ShortEntry->u1.Short.Name[SourceIndex];
if (Character == ' ') {
if (SourceIndex == 0) {
return FALSE;
}
break;
}
if (Character == '.') {
return FALSE;
}
Name->ShortName[TargetIndex] = Character;
TargetIndex += 1;
}
if (ShortEntry->u1.Short.Name[8] != ' ') {
Name->ShortName[TargetIndex] = '.';
TargetIndex += 1;
for (SourceIndex = 8; SourceIndex < 11; SourceIndex += 1) {
Character = ShortEntry->u1.Short.Name[SourceIndex];
if (Character == ' ') {
break;
}
if (Character == '.') {
return FALSE;
}
Name->ShortName[TargetIndex] = Character;
TargetIndex += 1;
}
}
Name->ShortName[TargetIndex] = 0;
//
// If there is a long name, extract it by walking backwards from the short entry.
// Otherwise, set long name to empty string.
//
Name->LongName[0] = 0;
Entry = ShortEntry - 1;
if (Entry < TableStart) {
return TRUE;
}
if ((Entry->u1.Short.Attribute & FAT_ATTRIBUTE_MASK) != FAT_ATTRIBUTE_LONG_NAME) {
return TRUE;
}
LongNameComponentIndex = 1;
TargetIndex = 0;
for (;;) {
if (TargetIndex == FAT_MAX_PATH) {
return FALSE;
}
if (Entry < TableStart) {
return FALSE;
}
if ((Entry->u1.Long.Order & FAT_LONG_NAME_ORDER_MASK) != LongNameComponentIndex) {
return FALSE;
}
#define ADD_CHARACTER(C) \
\
if (TargetIndex == FAT_MAX_PATH) { \
\
return FALSE; \
} \
\
if (((C) != 0) && ((C) != 0xFFFF)) { \
\
Name->LongName[TargetIndex] = (UINT8) (C); \
TargetIndex += 1; \
}
ADD_CHARACTER(Entry->u1.Long.NameW1_5[0]);
ADD_CHARACTER(Entry->u1.Long.NameW1_5[1]);
ADD_CHARACTER(Entry->u1.Long.NameW1_5[2]);
ADD_CHARACTER(Entry->u1.Long.NameW1_5[3]);
ADD_CHARACTER(Entry->u1.Long.NameW1_5[4]);
ADD_CHARACTER(Entry->u1.Long.NameW6_11[0]);
ADD_CHARACTER(Entry->u1.Long.NameW6_11[1]);
ADD_CHARACTER(Entry->u1.Long.NameW6_11[2]);
ADD_CHARACTER(Entry->u1.Long.NameW6_11[3]);
ADD_CHARACTER(Entry->u1.Long.NameW6_11[4]);
ADD_CHARACTER(Entry->u1.Long.NameW6_11[5]);
ADD_CHARACTER(Entry->u1.Long.NameW12_13[0]);
ADD_CHARACTER(Entry->u1.Long.NameW12_13[1]);
#undef ADD_CHARACTER
if ((Entry->u1.Long.Order & FAT_LONG_NAME_TERMINATOR)) {
break;
}
Entry -= 1;
LongNameComponentIndex += 1;
}
Name->LongName[TargetIndex] = 0;
return TRUE;
}
PFAT_DIRECTORY_ENTRY
BlFatFindDirectoryTableEntry(
PFAT_DIRECTORY_ENTRY Table,
UINT32 NumberOfEntries,
PCSTR Name
)
//++
//
// Routine Description:
//
// This function finds the directory table entry matching the specified name.
//
// Arguments:
//
// Table - Supplies a pointer to the table to find in.
//
// NumberOfEntries - Supplies the number of entries in the table.
//
// Name - Supplies the name to look for.
//
// Return Value:
//
// TRUE, if a matching entry was located.
// FALSE, otherwise.
//
//--
{
PFAT_DIRECTORY_ENTRY Entry;
FAT_NAME EntryName;
PFAT_DIRECTORY_ENTRY Limit;
BLASSERT(Name[0] != 0);
Limit = Table + NumberOfEntries;
for (Entry = Table; Entry != Limit; Entry += 1) {
if (Entry->u1.Short.Name[0] == FAT_DIRECTORY_ENTRY_FREE) {
continue;
}
if (Entry->u1.Short.Name[0] == FAT_DIRECTORY_ENTRY_LAST) {
break;
}
if (Entry->u1.Short.Name[0] == '.') {
continue;
}
if ((Entry->u1.Short.Attribute & FAT_ATTRIBUTE_MASK) == FAT_ATTRIBUTE_VOLUME_ID) {
continue;
}
if (BlFatDirectoryEntryToName(Entry,
&EntryName,
Table) != FALSE) {
if ((BlRtlEqualStringI(Name, (PCSTR) EntryName.ShortName) != FALSE) ||
(BlRtlEqualStringI(Name, (PCSTR) EntryName.LongName) != FALSE)) {
return Entry;
}
}
}
return NULL;
}
BOOLEAN
BlFatGetLengthClusterChain(
UINT32 Cluster,
PUINT32 Length
)
//++
//
// Routine Description:
//
// This function queries the length of the specified cluster chain.
//
// Arguments:
//
// Cluster - Supplies the index of the first cluster in the chain.
//
// Length - Receives the length of the chain.
//
// Return Value:
//
// TRUE, if query operation was successful.
// FALSE, otherwise.
//
//--
{
*Length = 0;
do {
if (FAT_IS_DATA_CLUSTER(Cluster) == FALSE) {
return FALSE;
}
if (BlFatGetNextCluster(Cluster, &Cluster) == FALSE) {
return FALSE;
}
*Length += 1;
} while (Cluster != BlFatLinkTerminator);
return TRUE;
}
BOOLEAN
BlFatReadClusterChain(
UINT32 Cluster,
UINT32 BytesToRead,
PVOID Buffer
)
//++
//
// Routine Description:
//
// This function reads from the specified FAT data cluster chain.
//
// Arguments:
//
// Cluster - Supplies the index of the first cluster in the chain.
//
// BytesToRead - Supplies number of bytes to read.
//
// Buffer - Receives data.
//
// Return Value:
//
// TRUE, if read operation was successful.
// FALSE, otherwise.
//
//--
{
PVOID ClusterData;
PUINT8 Next;
UINT32 Sector;
BLASSERT_PTR(FAT_IS_DATA_CLUSTER(Cluster) != FALSE, Cluster);
BLASSERT(BytesToRead > 0);
Next = (PUINT8) Buffer;
for (;;) {
//
// If the cluster number is not within the valid data range, then fail the read operation.
//
if (FAT_IS_DATA_CLUSTER(Cluster) == FALSE) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: ReadClusterChain: Cluster %u is out of range!\n", Cluster);
#endif
return FALSE;
}
//
// Calculate the first sector in the cluster.
//
Sector = FAT_DATA_CLUSTER_TO_SECTOR(Cluster);
//
// If remaining bytes to read is less than the cluster size, then read it using a
// temporary buffer, since the caller provided buffer is not necessarily a multiple
// of cluster size.
//
if (BytesToRead < BlFatBytesPerCluster) {
ClusterData = BlPoolAllocateBlock(BlFatBytesPerCluster);
if (BlFatReadSector(Sector,
ROUND_UP_TO_POWER2(BytesToRead, FAT_SECTOR_SIZE) / FAT_SECTOR_SIZE,
(PFAT_SECTOR) ClusterData) == FALSE) {
BlPoolFreeBlock(ClusterData);
return FALSE;
}
BlRtlCopyMemory(Next,
ClusterData,
BytesToRead);
BlPoolFreeBlock(ClusterData);
return TRUE;
}
//
// Otherwise, read the entire cluster and advance by full cluster size.
//
if (BlFatReadSector(Sector,
BlFatSectorsPerCluster,
(PFAT_SECTOR) Next) == FALSE) {
return FALSE;
}
BytesToRead -= BlFatBytesPerCluster;
Next += BlFatBytesPerCluster;
if (BytesToRead == 0) {
return TRUE;
}
//
// Get the next cluster index.
//
if (BlFatGetNextCluster(Cluster, &Cluster) == FALSE) {
return FALSE;
}
}
}
BOOLEAN
BlFatFindFileEntry(
PCSTR Path,
PFAT_DIRECTORY_ENTRY FileEntry
)
//++
//
// Routine Description:
//
// This function finds the entry matching the specified file path.
//
// Arguments:
//
// Path - Supplies a pointer to the path to look up.
//
// FileEntry - Receives the contents of the entry matching the specified path.
//
// Return Value:
//
// TRUE, if a match was found.
// FALSE, otherwise.
//
//--
{
UINT32 DirectoryCluster;
UINT32 DirectoryClusterCount;
UINT32 Depth;
FAT_DIRECTORY_ENTRY Entry;
PFAT_DIRECTORY_ENTRY Match;
PCSTR Next;
PFAT_DIRECTORY_ENTRY Table;
UINT32 TableSize;
UINT8 Token[FAT_MAX_PATH];
UINT32 TokenIndex;
if ((Path[0] == 0) ||
(Path[0] == '/') ||
(BlRtlStringLength(Path) >= FAT_MAX_PATH)) {
return FALSE;
}
Next = Path;
Depth = 0;
SATISFY_OVERZEALOUS_COMPILER(BlRtlZeroMemory(&Entry, sizeof(Entry)));
for (;;) {
if (*Next == 0) {
if ((Entry.u1.Short.Attribute & FAT_ATTRIBUTE_DIRECTORY) != 0) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: FindFileEntry: %s is a directory!\n", Path);
#endif
return FALSE;
}
*FileEntry = Entry;
return TRUE;
}
//
// If the next token is empty (i.e. back to back separators), then this is a malformed path.
//
if (*Next == '/') {
#if FAT_VERBOSE
BlRtlPrintf("FAT: FindFileEntry: %s is a malformed path!\n", Path);
#endif
return FALSE;
}
//
// Extract the next token.
//
TokenIndex = 0;
for (;;) {
if (*Next == 0) {
break;
}
if (*Next == '/') {
Next += 1;
break;
}
Token[TokenIndex] = *Next;
TokenIndex += 1;
Next += 1;
}
BLASSERT(TokenIndex > 0);
Token[TokenIndex] = 0;
if (Depth == 0) {
Table = BlFatRootDirectory;
TableSize = BlFatNumberOfRootDirectoryEntries;
} else {
DirectoryCluster = Entry.u1.Short.FirstClusterLow;
if (BlFatGetLengthClusterChain(DirectoryCluster, &DirectoryClusterCount) == FALSE) {
return FALSE;
}
Table = (PFAT_DIRECTORY_ENTRY)
BlPoolAllocateBlock(DirectoryClusterCount * BlFatBytesPerCluster);
if (BlFatReadClusterChain(DirectoryCluster,
DirectoryClusterCount * BlFatBytesPerCluster,
Table) == FALSE) {
BlPoolFreeBlock(Table);
return FALSE;
}
TableSize = (DirectoryClusterCount * BlFatBytesPerCluster) / sizeof(FAT_DIRECTORY_ENTRY);
}
//
// Walk the directory table matching the previous token for an entry matching the next token.
//
Match = BlFatFindDirectoryTableEntry(Table,
TableSize,
(PCSTR) Token);
if (Match == NULL) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: FindFileEntry: Unable to find directory entry for token %s.\n", Token);
#endif
if (Table != BlFatRootDirectory) {
BlPoolFreeBlock(Table);
}
return FALSE;
}
Entry = *Match;
if (Table != BlFatRootDirectory) {
BlPoolFreeBlock(Table);
}
Depth += 1;
}
}
BOOLEAN
BlFatGetFileSize(
PCSTR Path,
PUINT32 FileSize
)
//++
//
// Routine Description:
//
// This function queries the size of the specified file.
//
// Arguments:
//
// Path - Supplies the path to the file to query.
//
// FileSize - Receives the size of the file.
//
// Return Value:
//
// TRUE, if the query operation was successful.
// FALSE, otherwise.
//
//--
{
FAT_DIRECTORY_ENTRY Entry;
if (BlFatFindFileEntry(Path, &Entry) == FALSE) {
return FALSE;
}
*FileSize = Entry.u1.Short.Size;
return TRUE;
}
BOOLEAN
BlFatReadFile(
PCSTR Path,
PVOID Buffer,
UINT32 NumberOfBytes
)
//++
//
// Routine Description:
//
// This function reads from the specified file.
//
// Arguments:
//
// Path - Supplies the path to the file to read.
//
// Buffer - Receives data.
//
// NumberOfBytes - Supplies the number of bytes to read.
//
// Return Value:
//
// TRUE, if the read operation was successful.
// FALSE, otherwise.
//
//--
{
FAT_DIRECTORY_ENTRY Entry;
BOOLEAN Result;
if (BlFatFindFileEntry(Path, &Entry) == FALSE) {
return FALSE;
}
if (NumberOfBytes > Entry.u1.Short.Size) {
return FALSE;
}
Result = BlFatReadClusterChain(Entry.u1.Short.FirstClusterLow,
NumberOfBytes,
Buffer);
return Result;
}
BOOLEAN
BlFat16GetNextCluster(
UINT32 Cluster,
PUINT32 NextCluster
)
//++
//
// Routine Description:
//
// This function gets the index of the cluster following the specified cluster.
//
// Arguments:
//
// Cluster - Supplies the index of the cluster.
//
// NextCluster - Receives the index of the next cluster.
//
// Return Value:
//
// TRUE, if query operation was successful.
// FALSE, otherwise.
//
//--
{
UINT32 Offset;
UINT32 Sector;
FAT_SECTOR TablePage;
if (FAT_IS_DATA_CLUSTER(Cluster) == FALSE) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: Fat16GetNextCluster: Cluster %u is out of range!\n", Cluster);
#endif
return FALSE;
}
Sector = BlFatTableStart + (Cluster / 256);
Offset = Cluster % 256;
if (BlFatReadSector(Sector, 1, &TablePage) == FALSE) {
return FALSE;
}
*NextCluster = (UINT32) (((PUINT16) &TablePage)[Offset]);
return TRUE;
}
VOID
BlFat16Initialize(
VOID
)
//++
//
// Routine Description:
//
// This function initializes FAT16 support.
//
//--
{
PFAT16_BOOT_SECTOR BootSector;
BootSector = &BlFatBootSector.u1.Fat16;
BLASSERT(BlFatMbr.Partition[BlFatPartitionId].Type == MBR_FAT16LBA);
//
// Read FAT16 boot sector.
//
if (BlRtlReadDrive(BlFatDriveId,
BlFatPartitionStart,
1,
BootSector) == FALSE) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: Error reading boot sector!\n");
#endif
BlFatHalt();
}
//
// Extract volume geometry.
//
if (BootSector->BytesPerSector != FAT_SECTOR_SIZE) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: Unsupported sector size (%u)!\n", BootSector->BytesPerSector);
#endif
BlFatHalt();
}
BlFatSectorsPerCluster = BootSector->SectorsPerCluster;
if (BlFatSectorsPerCluster == 0) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: SectorsPerCluster == 0!\n");
#endif
BlFatHalt();
}
BlFatBytesPerCluster = BlFatSectorsPerCluster * FAT_SECTOR_SIZE;
if (BootSector->TotalSectorCount32 > 0) {
BlFatTotalSectorCount = BootSector->TotalSectorCount32;
} else {
BlFatTotalSectorCount = BootSector->TotalSectorCount16;
}
if (BlFatTotalSectorCount > BlFatPartitionSize) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: Boot sector claims more sectors than MBR!\n");
#endif
BlFatHalt();
}
if (BootSector->NumberOfFATs == 0) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: NumberOfFATs == 0!\n");
#endif
BlFatHalt();
}
if (BootSector->SectorsPerFAT == 0) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: SectorsPerFAT == 0!\n");
#endif
BlFatHalt();
}
BlFatNumberOfRootDirectoryEntries = BootSector->NumberOfRootDirectoryEntries;
if ((BlFatNumberOfRootDirectoryEntries == 0) ||
((BlFatNumberOfRootDirectoryEntries % 64) != 0)) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: Invalid number of root directory entries (%u)!\n", BlFatNumberOfRootDirectoryEntries);
#endif
BlFatHalt();
}
BlFatTableStart = BootSector->NumberOfReservedSectors;
if (BlFatTotalSectorCount < BlFatTableStart) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: TotalSectorCount < TableStart!\n");
#endif
BlFatHalt();
}
BlFatRootStart = BlFatTableStart + (BootSector->NumberOfFATs * BootSector->SectorsPerFAT);
if (BlFatTotalSectorCount < BlFatRootStart) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: TotalSectorCount < RootStart!\n");
#endif
BlFatHalt();
}
BlFatDataStart = BlFatRootStart + (ROUND_UP_TO_POWER2(BlFatNumberOfRootDirectoryEntries * sizeof(FAT_DIRECTORY_ENTRY), FAT_SECTOR_SIZE) / FAT_SECTOR_SIZE);
if (BlFatTotalSectorCount < BlFatDataStart) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: TotalSectorCount < DataStart!\n");
#endif
BlFatHalt();
}
BlFatNumberOfDataClusters = (BlFatTotalSectorCount - BlFatDataStart) / BlFatSectorsPerCluster;
if (BlFatNumberOfDataClusters == 0) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: NumberOfDataClusters == 0!\n");
#endif
BlFatHalt();
}
BlFatLinkTerminator = FAT16_LINK_TERMINATOR;
BlFatGetNextCluster = BlFat16GetNextCluster;
//
// Read root directory.
//
BlFatRootDirectory = (PFAT_DIRECTORY_ENTRY) BlPoolAllocateBlock((BlFatDataStart - BlFatRootStart) * FAT_SECTOR_SIZE);
if (BlFatReadSector(BlFatRootStart,
BlFatDataStart - BlFatRootStart,
(PFAT_SECTOR) BlFatRootDirectory) == FALSE) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: Error reading root directory!\n");
#endif
BlFatHalt();
}
BLASSERT(FAT_IS_DATA_CLUSTER(FAT16_LINK_TERMINATOR) == FALSE);
return;
}
BOOLEAN
BlFat32GetNextCluster(
UINT32 Cluster,
PUINT32 NextCluster
)
//++
//
// Routine Description:
//
// This function gets the index of the cluster following the specified cluster.
//
// Arguments:
//
// Cluster - Supplies the index of the cluster.
//
// NextCluster - Receives the index of the next cluster.
//
// Return Value:
//
// TRUE, if query operation was successful.
// FALSE, otherwise.
//
//--
{
UINT32 Offset;
UINT32 Sector;
FAT_SECTOR TablePage;
if (FAT_IS_DATA_CLUSTER(Cluster) == FALSE) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: Fat32GetNextCluster: Cluster %u is out of range!\n", Cluster);
#endif
return FALSE;
}
Sector = BlFatTableStart + (Cluster / 128);
Offset = Cluster % 128;
if (BlFatReadSector(Sector, 1, &TablePage) == FALSE) {
return FALSE;
}
*NextCluster = ((PUINT32) &TablePage)[Offset];
return TRUE;
}
VOID
BlFat32Initialize(
VOID
)
//++
//
// Routine Description:
//
// This function initializes FAT32 support.
//
//--
{
PFAT32_BOOT_SECTOR BootSector;
UINT32 RootDirectoryChainLength;
BootSector = &BlFatBootSector.u1.Fat32;
BLASSERT(BlFatMbr.Partition[BlFatPartitionId].Type == MBR_FAT32LBA);
//
// Read FAT32 boot sector.
//
if (BlRtlReadDrive(BlFatDriveId,
BlFatPartitionStart,
1,
BootSector) == FALSE) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: Error reading boot sector!\n");
#endif
BlFatHalt();
}
//
// Extract volume geometry.
//
if (BootSector->BytesPerSector != FAT_SECTOR_SIZE) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: Unsupported sector size (%u)!\n", BootSector->BytesPerSector);
#endif
BlFatHalt();
}
BlFatSectorsPerCluster = BootSector->SectorsPerCluster;
if (BlFatSectorsPerCluster == 0) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: SectorsPerCluster == 0!\n");
#endif
BlFatHalt();
}
BlFatBytesPerCluster = BlFatSectorsPerCluster * FAT_SECTOR_SIZE;
if (BootSector->TotalSectorCount32 > 0) {
BlFatTotalSectorCount = BootSector->TotalSectorCount32;
} else {
BlFatTotalSectorCount = BootSector->TotalSectorCount16;
}
if (BlFatTotalSectorCount > BlFatPartitionSize) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: Boot sector claims more sectors than MBR!\n");
#endif
BlFatHalt();
}
if (BootSector->NumberOfFATs == 0) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: NumberOfFATs == 0!\n");
#endif
BlFatHalt();
}
if (BootSector->SectorsPerFAT32 == 0) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: SectorsPerFAT == 0!\n");
#endif
BlFatHalt();
}
if (BootSector->NumberOfRootDirectoryEntries != 0) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: BootSector->NumberOfRootDirectoryEntries != 0!\n");
#endif
BlFatHalt();
}
BlFatTableStart = BootSector->NumberOfReservedSectors;
if (BlFatTotalSectorCount < BlFatTableStart) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: TotalSectorCount < TableStart!\n");
#endif
BlFatHalt();
}
BlFatDataStart = BlFatTableStart + (BootSector->NumberOfFATs * BootSector->SectorsPerFAT32);
if (BlFatTotalSectorCount < BlFatDataStart) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: TotalSectorCount < DataStart!\n");
#endif
BlFatHalt();
}
BlFatNumberOfDataClusters = (BlFatTotalSectorCount - BlFatDataStart) / BlFatSectorsPerCluster;
if (BlFatNumberOfDataClusters == 0) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: NumberOfDataClusters == 0!\n");
#endif
BlFatHalt();
}
BlFatLinkTerminator = FAT32_LINK_TERMINATOR;
BlFatGetNextCluster = BlFat32GetNextCluster;
//
// Read root directory.
//
if (BlFatGetLengthClusterChain(BootSector->RootDirectoryFirstCluster, &RootDirectoryChainLength) == FALSE) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: Error querying chain length of root directory!\n");
#endif
BlFatHalt();
}
BlFatRootDirectory = (PFAT_DIRECTORY_ENTRY) BlPoolAllocateBlock(RootDirectoryChainLength * BlFatBytesPerCluster);
if (BlFatReadClusterChain(BootSector->RootDirectoryFirstCluster,
RootDirectoryChainLength * BlFatBytesPerCluster,
BlFatRootDirectory) == FALSE) {
#if FAT_VERBOSE
BlRtlPrintf("FAT: Error reading root directory!\n");
#endif
BlFatHalt();
}
BlFatNumberOfRootDirectoryEntries = (RootDirectoryChainLength * BlFatBytesPerCluster) / sizeof(FAT_DIRECTORY_ENTRY);
BLASSERT(FAT_IS_DATA_CLUSTER(FAT32_LINK_TERMINATOR) == FALSE);
return;
}
VOID
BlFatInitialize(
UINT8 DriveId,
UINT8 FatType
)
//++
//
// Routine Description:
//
// This function initializes FAT support.
//
// Arguments:
//
// DriveId - Supplies boot drive ID.
//
// FatType - Supplies the FAT type to look for.
//
//--
{
UINT32 Index;
BLASSERT((FatType == MBR_FAT16LBA) || (FatType == MBR_FAT32LBA));
if (BlRtlGetDriveParameters(DriveId, &BlFatDriveParameters) == FALSE) {
BlRtlPrintf("FAT: Can't get drive info 0x%02x!\n", DriveId);
BlRtlHalt();
}
if (BlFatDriveParameters.BytesPerSector != FAT_SECTOR_SIZE) {
BlRtlPrintf("FAT: Unexpected bytes per sector (%u)!\n", BlFatDriveParameters.BytesPerSector);
BlRtlHalt();
}
if (BlRtlReadDrive(DriveId, 0, 1, &BlFatMbr) == FALSE) {
BlRtlPrintf("FAT: Error reading MBR!\n");
BlRtlHalt();
}
if (BlFatMbr.Signature != MBR_SIGNATURE) {
BlRtlPrintf("FAT: No MBR signature!\n");
}
BlFatPartitionId = (UINT32) -1;
for (Index = 0; Index <= 4; Index += 1) {
if (FatType == BlFatMbr.Partition[Index].Type) {
switch (BlFatMbr.Partition[Index].Type) {
case MBR_FAT16LBA: {
BlFatDriveId = DriveId;
BlFatPartitionId = Index;
BlFatPartitionStart = BlFatMbr.Partition[Index].FirstSector;
BlFatPartitionSize = BlFatMbr.Partition[Index].NumberOfSectors;
BlFat16Initialize();
break;
}
case MBR_FAT32LBA: {
BlFatDriveId = DriveId;
BlFatPartitionId = Index;
BlFatPartitionStart = BlFatMbr.Partition[Index].FirstSector;
BlFatPartitionSize = BlFatMbr.Partition[Index].NumberOfSectors;
BlFat32Initialize();
break;
}
}
}
}
if (BlFatPartitionId == (UINT32) -1) {
BlRtlPrintf("FAT: No %s partitions!\n", FatType == MBR_FAT16LBA ? "FAT16" : "FAT32");
BlRtlHalt();
}
BlFsGetFileSize = BlFatGetFileSize;
BlFsReadFile = BlFatReadFile;
return;
}