///////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: BPB.sg
//
// Note:
//
// Based on:
// "Microsoft Extensible Firmware Initiative FAT32 File System Specification",
// Version 1.03, December 6, 2000, Microsoft Corporation.
//
using Microsoft.SingSharp;
using Microsoft.Singularity.Io;
using Microsoft.Singularity.Channels;
using System.Runtime.InteropServices;
using System.Text;
namespace Microsoft.Singularity.Services.Fat.Fs
{
///
/// Boot Sector and Bios Parameter Block structure.
///
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal pointerfree struct BPB
{
///////////////////////////////////////////////////////////////////////
// Constants
internal const int Length = 36;
internal const int OemNameLength = 8;
internal const byte Byte510Value = 0x55; // These are not strictly
internal const byte Byte511Value = 0xaa; // part of BPB.
///////////////////////////////////////////////////////////////////////
// Boot Sector fields
internal byte JmpBoot0; // 0
internal byte JmpBoot1; // 1
internal byte JmpBoot2; // 2
private byte oemName0; // 3
private byte oemName1; // 4
private byte oemName2; // 5
private byte oemName3; // 6
private byte oemName4; // 7
private byte oemName5; // 8
private byte oemName6; // 9
private byte oemName7; // 10
private ushort bytesPerSector; // 11..12
internal byte SectorsPerCluster; // 13
private ushort reservedSectorCount; // 14..15
internal byte NumberOfFats; // 16
private ushort rootEntryCount; // 17..18
private ushort totalSectors16; // 19..20
internal byte Media; // 21
private ushort fatSize16; // 22..23
private ushort sectorsPerTrack; // 24..25
private ushort numberOfHeads; // 26..27
private uint hiddenSectors; // 28..31
private uint totalSectors32; // 32..35
///////////////////////////////////////////////////////////////////////
// Methods
internal ushort BytesPerSector
{
get { return ByteOrder.LittleEndianToHost(bytesPerSector); }
set { bytesPerSector = ByteOrder.HostToLittleEndian(value); }
}
internal ushort ReservedSectorCount
{
get { return ByteOrder.LittleEndianToHost(reservedSectorCount); }
set { reservedSectorCount = ByteOrder.HostToLittleEndian(value); }
}
internal ushort RootEntryCount
{
get { return ByteOrder.LittleEndianToHost(rootEntryCount); }
set { rootEntryCount = ByteOrder.HostToLittleEndian(value); }
}
internal ushort TotalSectors16
{
get { return ByteOrder.LittleEndianToHost(totalSectors16); }
set { totalSectors16 = ByteOrder.HostToLittleEndian(value); }
}
internal ushort FatSize16
{
get { return ByteOrder.LittleEndianToHost(fatSize16); }
set { fatSize16 = ByteOrder.HostToLittleEndian(value); }
}
internal ushort SectorsPerTrack
{
get { return ByteOrder.LittleEndianToHost(sectorsPerTrack); }
set { sectorsPerTrack = ByteOrder.HostToLittleEndian(value); }
}
internal ushort NumberOfHeads
{
get { return ByteOrder.LittleEndianToHost(numberOfHeads); }
set { numberOfHeads = ByteOrder.HostToLittleEndian(value); }
}
internal uint HiddenSectors
{
get { return ByteOrder.LittleEndianToHost(hiddenSectors); }
set { hiddenSectors = ByteOrder.HostToLittleEndian(value); }
}
internal uint TotalSectors32
{
get { return ByteOrder.LittleEndianToHost(totalSectors32); }
set { totalSectors32 = ByteOrder.HostToLittleEndian(value); }
}
internal string! OemName
{
get {
byte [] oemBytes = new byte [OemNameLength] {
oemName0, oemName1, oemName2, oemName3,
oemName4, oemName5, oemName6, oemName7
};
ASCIIEncoding ascii = new ASCIIEncoding();
return (!) ascii.GetString(oemBytes);
}
set {
ASCIIEncoding ascii = new ASCIIEncoding();
byte []! bytes = new byte [OemNameLength];
int done = ascii.GetBytes(value, 0, value.Length, bytes, 0);
for (int i = done; i < OemNameLength; i++) {
bytes[i] = ' ';
}
oemName0 = bytes[0]; oemName1 = bytes[1];
oemName2 = bytes[2]; oemName3 = bytes[3];
oemName4 = bytes[4]; oemName5 = bytes[5];
oemName6 = bytes[6]; oemName7 = bytes[7];
}
}
internal static bool ValidateOverlay()
{
if (sizeof(BPB) != BPB.Length) {
DebugStub.Print("Bad BPB Length ({0} != {1})\n",
__arglist(sizeof(BPB), BPB.Length));
return false;
}
byte []! bytes = new byte[BPB.Length];
ref BPB bpb = ref bytes[0];
bpb.RootEntryCount = 0xbeef;
if (bytes[17] != 0xef || bytes[18] != 0xbe) {
DebugStub.Print("Bad BPB test for RootEntryCount (0x{0:x2}{1:x2})\n",
__arglist(bytes[17], bytes[18]));
return false;
}
bpb.TotalSectors32 = 0x32333435;
if (bytes[32] != 0x35 || bytes[33] != 0x34 ||
bytes[34] != 0x33 || bytes[35] != 0x32) {
DebugStub.Print("Bad BPB test for TotalSectors32 (0x{0:x2}{1:x2}{2:x2}{3:x2})\n", __arglist(bytes[32], bytes[33], bytes[34], bytes[35]));
return false;
}
return bpb.TotalSectors32 != 0;
}
}
///
/// FAT12 and FAT16 structure that immediately follows the BPB structure
/// in the Boot Sector (offset 36).
///
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal pointerfree struct BPB1x
{
///////////////////////////////////////////////////////////////////////
// Constants
internal const int Length = 26;
internal const byte ExpectedBootSignature = 0x29;
private const int VolumeLabelLength = 11;
private const int FileSystemTypeLength = 8;
///////////////////////////////////////////////////////////////////////
// Fields
internal byte DriveNumber; // 0
internal byte Reserved1; // 1
internal byte BootSignature; // 2
private uint volumeId; // 3..6
// XXX replace if/when arrays become
// valid in overlay.
private byte volumeLabel00; // 7
private byte volumeLabel01; // 8
private byte volumeLabel02; // 9
private byte volumeLabel03; // 10
private byte volumeLabel04; // 11
private byte volumeLabel05; // 12
private byte volumeLabel06; // 13
private byte volumeLabel07; // 14
private byte volumeLabel08; // 15
private byte volumeLabel09; // 16
private byte volumeLabel10; // 17
// XXX replace if/when arrays become
// valid in overlay.
private byte fileSystemType0; // 18
private byte fileSystemType1; // 19
private byte fileSystemType2; // 20
private byte fileSystemType3; // 21
private byte fileSystemType4; // 22
private byte fileSystemType5; // 23
private byte fileSystemType6; // 24
private byte fileSystemType7; // 25
///////////////////////////////////////////////////////////////////////
// Methods
internal bool ValidSignature
{
get { return BootSignature == ExpectedBootSignature; }
}
internal uint VolumeId
{
get { return ByteOrder.LittleEndianToHost(volumeId); }
set { volumeId = ByteOrder.HostToLittleEndian(value); }
}
internal string! VolumeLabel
{
get {
byte [] bytes = new byte [VolumeLabelLength] {
volumeLabel00, volumeLabel01, volumeLabel02,
volumeLabel03, volumeLabel04, volumeLabel05,
volumeLabel06, volumeLabel07, volumeLabel08,
volumeLabel09, volumeLabel10
};
ASCIIEncoding ascii = new ASCIIEncoding();
return (!) ascii.GetString(bytes);
}
set {
ASCIIEncoding ascii = new ASCIIEncoding();
byte []! bytes = new byte[VolumeLabelLength];
int done = ascii.GetBytes(value, 0, value.Length, bytes, 0);
for (int i = done; i < VolumeLabelLength; i++) {
bytes[i] = ' ';
}
volumeLabel00 = bytes[0]; volumeLabel01 = bytes[1];
volumeLabel02 = bytes[2]; volumeLabel03 = bytes[3];
volumeLabel04 = bytes[4]; volumeLabel05 = bytes[5];
volumeLabel06 = bytes[6]; volumeLabel07 = bytes[7];
volumeLabel08 = bytes[8]; volumeLabel09 = bytes[9];
volumeLabel10 = bytes[10];
}
}
internal string! FileSystemType
{
get {
byte [] oemBytes = new byte [FileSystemTypeLength] {
fileSystemType0, fileSystemType1, fileSystemType2,
fileSystemType3, fileSystemType4, fileSystemType5,
fileSystemType6, fileSystemType7
};
ASCIIEncoding ascii = new ASCIIEncoding();
return (!) ascii.GetString(oemBytes);
}
set {
ASCIIEncoding ascii = new ASCIIEncoding();
byte []! bytes = new byte [FileSystemTypeLength];
int done = ascii.GetBytes(value, 0, value.Length, bytes, 0);
for (int i = done; i < FileSystemTypeLength; i++) {
bytes[i] = ' ';
}
fileSystemType0 = bytes[0]; fileSystemType1 = bytes[1];
fileSystemType2 = bytes[2]; fileSystemType3 = bytes[3];
fileSystemType4 = bytes[4]; fileSystemType5 = bytes[5];
fileSystemType6 = bytes[6]; fileSystemType7 = bytes[7];
}
}
}
///
/// FAT32 structure that immediately follows the BPB structure
/// in the Boot Sector (offset 36).
///
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal pointerfree struct BPB32
{
///////////////////////////////////////////////////////////////////////
// Constants
internal const int Length = 54;
internal const byte ExpectedBootSignature = 0x29;
private const int VolumeLabelLength = 11;
private const int FileSystemTypeLength = 8;
///////////////////////////////////////////////////////////////////////
// Fields
private uint fatSize32; // 0..3 - FAT size in sectors
private ushort extFlags; // 4..5 - Mirroring flags
private ushort fsVersion; // 6..7 - 0:0 or not mountable
private uint rootCluster; // 8..11 - First cluster of root dir
private ushort fsInfoSector; // 12..13 - Primary pointer to FsInfo
private ushort bootRecordCopy; // 14..15
// XXX replace if/when arrays become
// valid in overlay.
internal byte Reserved00; // 16
internal byte Reserved01; // 17
internal byte Reserved02; // 18
internal byte Reserved03; // 19
internal byte Reserved04; // 20
internal byte Reserved05; // 21
internal byte Reserved06; // 22
internal byte Reserved07; // 23
internal byte Reserved08; // 24
internal byte Reserved09; // 25
internal byte Reserved10; // 26
internal byte Reserved11; // 27
// Fields equivalent to those in BPB1x
internal byte DriveNumber; // 28
internal byte Reserved1; // 29
internal byte BootSignature; // 30
private uint volumeId; // 31..34
// XXX replace if/when arrays become
// valid in overlay.
private byte volumeLabel00; // 35
private byte volumeLabel01; // 36
private byte volumeLabel02; // 37
private byte volumeLabel03; // 38
private byte volumeLabel04; // 39
private byte volumeLabel05; // 40
private byte volumeLabel06; // 41
private byte volumeLabel07; // 42
private byte volumeLabel08; // 43
private byte volumeLabel09; // 44
private byte volumeLabel10; // 45
// XXX replace if/when arrays become
// valid in overlay.
private byte fileSystemType0; // 46
private byte fileSystemType1; // 47
private byte fileSystemType2; // 48
private byte fileSystemType3; // 49
private byte fileSystemType4; // 50
private byte fileSystemType5; // 51
private byte fileSystemType6; // 52
private byte fileSystemType7; // 53
///////////////////////////////////////////////////////////////////////
// Methods
internal bool ValidSignature
{
get {
return (BootSignature == ExpectedBootSignature &&
fsVersion == 0);
}
}
internal uint FatSize32
{
get { return ByteOrder.LittleEndianToHost(fatSize32); }
set { fatSize32 = ByteOrder.HostToLittleEndian(value); }
}
internal ushort ExtFlags
{
get { return ByteOrder.LittleEndianToHost(extFlags); }
set { extFlags = ByteOrder.HostToLittleEndian(value); }
}
internal ushort FsVersion
{
get { return ByteOrder.LittleEndianToHost(fsVersion); }
set { fsVersion = ByteOrder.HostToLittleEndian(value); }
}
internal uint RootCluster
{
get { return ByteOrder.LittleEndianToHost(rootCluster); }
set { rootCluster = ByteOrder.HostToLittleEndian(value); }
}
internal ushort FsInfoSector
{
get { return ByteOrder.LittleEndianToHost(fsInfoSector); }
set { fsInfoSector = ByteOrder.HostToLittleEndian(value); }
}
internal ushort BootRecordCopy
{
get { return ByteOrder.LittleEndianToHost(bootRecordCopy); }
set { bootRecordCopy = ByteOrder.HostToLittleEndian(value); }
}
internal uint VolumeId
{
get { return ByteOrder.LittleEndianToHost(volumeId); }
set { volumeId = ByteOrder.HostToLittleEndian(value); }
}
internal string! VolumeLabel
{
get {
byte [] bytes = new byte [VolumeLabelLength] {
volumeLabel00, volumeLabel01, volumeLabel02,
volumeLabel03, volumeLabel04, volumeLabel05,
volumeLabel06, volumeLabel07, volumeLabel08,
volumeLabel09, volumeLabel10
};
ASCIIEncoding ascii = new ASCIIEncoding();
return (!) ascii.GetString(bytes);
}
set {
ASCIIEncoding ascii = new ASCIIEncoding();
byte []! bytes = new byte[VolumeLabelLength];
int done = ascii.GetBytes(value, 0, value.Length, bytes, 0);
for (int i = done; i < VolumeLabelLength; i++) {
bytes[i] = ' ';
}
volumeLabel00 = bytes[0]; volumeLabel01 = bytes[1];
volumeLabel02 = bytes[2]; volumeLabel03 = bytes[3];
volumeLabel04 = bytes[4]; volumeLabel05 = bytes[5];
volumeLabel06 = bytes[6]; volumeLabel07 = bytes[7];
volumeLabel08 = bytes[8]; volumeLabel09 = bytes[9];
volumeLabel10 = bytes[10];
}
}
internal string! FileSystemType
{
get {
byte [] oemBytes = new byte [FileSystemTypeLength] {
fileSystemType0, fileSystemType1, fileSystemType2,
fileSystemType3, fileSystemType4, fileSystemType5,
fileSystemType6, fileSystemType7
};
ASCIIEncoding ascii = new ASCIIEncoding();
return (!) ascii.GetString(oemBytes);
}
set {
ASCIIEncoding ascii = new ASCIIEncoding();
byte []! bytes = new byte [FileSystemTypeLength];
int done = ascii.GetBytes(value, 0, value.Length, bytes, 0);
for (int i = done; i < FileSystemTypeLength; i++) {
bytes[i] = ' ';
}
fileSystemType0 = bytes[0]; fileSystemType1 = bytes[1];
fileSystemType2 = bytes[2]; fileSystemType3 = bytes[3];
fileSystemType4 = bytes[4]; fileSystemType5 = bytes[5];
fileSystemType6 = bytes[6]; fileSystemType7 = bytes[7];
}
}
}
internal class BpbException : System.Exception
{
internal BpbException(string message)
: base (message)
{
}
}
enum FatVersion : byte {
Fat12,
Fat16,
Fat32
};
internal class BpbSummary
{
internal readonly FatVersion Version; // yes
internal readonly uint BytesPerSector; // yes
internal readonly uint BytesPerCluster; // yes
internal readonly uint ClusterCount; // yes
internal readonly uint FirstFatSector; // yes
internal readonly ulong FirstClusterSector; // yes
internal readonly ulong FsInfoSector; // yes
internal readonly uint NumberOfFats; // yes
internal readonly uint ReservedSectors; // yes
internal readonly uint RootEntryCount; // yes
internal readonly uint RootDirectorySectors; // yes
internal readonly uint RootCluster; // yes
internal readonly uint SectorsPerFat; // yes
internal readonly uint SectorsPerCluster; // yes
internal readonly uint TotalSectors; // yes
private static bool IsNonZeroPowerOfTwo(uint value)
{
return (value != 0) && ((value & (value - 1)) == 0);
}
internal BpbSummary(byte[]! in ExHeap sector0)
{
ref BPB bpb = ref sector0[0];
ref BPB1x bpb1x = ref sector0[BPB.Length];
ref BPB32 bpb32 = ref sector0[BPB.Length];
BytesPerSector = bpb.BytesPerSector;
if (!IsNonZeroPowerOfTwo(BytesPerSector) ||
BytesPerSector < 512 || BytesPerSector > 4096) {
throw new BpbException("Bad bytes per sector in BPB.");
}
if (sector0[510] != BPB.Byte510Value ||
sector0[511] != BPB.Byte511Value) {
throw new
BpbException("Bad trailing signature in boot block.");
}
NumberOfFats = bpb.NumberOfFats;
if (NumberOfFats == 0) {
throw new BpbException("No FATs described in BPB.");
}
SectorsPerCluster = bpb.SectorsPerCluster;
if (!IsNonZeroPowerOfTwo(SectorsPerCluster)) {
throw new BpbException("Bad sectors per cluster value.");
}
ReservedSectors = bpb.ReservedSectorCount;
if (ReservedSectors == 0) {
throw new BpbException("Bad reserved sectors count.");
}
BytesPerCluster = BytesPerSector * SectorsPerCluster;
if (BytesPerCluster > 32768) {
throw new BpbException("Bad bytes per cluster value.");
}
if (bpb.FatSize16 != 0) {
// Looks like FAT12/16
if (bpb1x.ValidSignature == false) {
throw new BpbException("Bad signature in Fat12/16 BPB.");
}
SectorsPerFat = bpb.FatSize16;
if (bpb.TotalSectors16 != 0) {
TotalSectors = bpb.TotalSectors16;
}
else if (bpb.TotalSectors32 != 0) {
TotalSectors = bpb.TotalSectors32;
}
else {
throw new BpbException("Bad total sectors value in BPB.");
}
}
else {
// Looks like FAT32
if (bpb32.ValidSignature == false) {
throw new BpbException("Bad signature in Fat32 BPB.");
}
if (bpb.TotalSectors16 != 0 ||
bpb.FatSize16 != 0) {
throw new BpbException(
"Legacy fields set in BPB for Fat32."
);
}
if (bpb32.FatSize32 == 0) {
throw new BpbException("Bad FAT size in Fat32 BPB.");
}
SectorsPerFat = bpb32.FatSize32;
if (bpb.TotalSectors32 == 0) {
throw new BpbException("Bad total sectors value in BPB.");
}
TotalSectors = bpb.TotalSectors32;
}
RootEntryCount = bpb.RootEntryCount;
RootDirectorySectors =
(RootEntryCount * 32 + BytesPerSector - 1) / BytesPerSector;
FirstClusterSector = (ulong)ReservedSectors +
NumberOfFats * SectorsPerFat + RootDirectorySectors;
FirstFatSector = ReservedSectors;
// Check First cluster sector < total sectors
if (FirstClusterSector > TotalSectors) {
throw new BpbException(
"First cluster sector exceeds total sectors."
);
}
ClusterCount =
(uint) (TotalSectors - FirstClusterSector) / SectorsPerCluster;
if (ClusterCount < 4085) {
Version = FatVersion.Fat12;
FsInfoSector = 0;
RootCluster = 0;
}
else if (ClusterCount < 65525 || bpb.FatSize16 != 0) {
// HACK!HACK!HACK! The
// spec says the limit should be 65525, but the
// table on page for formatting disks up to 2GB
// gives a value that leads to 65527 (0xfff6)
// clusters.
if (ClusterCount > 65527) {
throw new BpbException(
"FAT16 partition with bad cluster count."
);
}
Version = FatVersion.Fat16;
FsInfoSector = 0;
RootCluster = 0;
}
else if (ClusterCount < 0x0ffffff7) {
Version = FatVersion.Fat32;
if (bpb32.FsInfoSector <= ReservedSectors) {
FsInfoSector = bpb32.FsInfoSector;
}
else {
FsInfoSector = 0;
}
RootCluster = bpb32.RootCluster;
}
else {
throw new BpbException("Invalid cluster count.");
}
}
}
}