data structs

This commit is contained in:
José Pedro 2018-12-04 19:42:52 +00:00
parent 3d3f032d79
commit 03c9caf688
No known key found for this signature in database
GPG key ID: B8247B9301707B83
11 changed files with 478 additions and 255 deletions

View file

@ -0,0 +1,82 @@
using System.Text;
namespace Cosmos.System.FileSystem.FAT
{
internal sealed class BiosParameterBlock : DataStructure
{
public static readonly ByteField JmpBoot0 = new ByteField(0);
public static readonly ByteField JmpBoot1 = new ByteField(1);
public static readonly ByteField JmpBoot2 = new ByteField(2);
public static readonly StringField OemName = new StringField(3, 8, Encoding.ASCII);
public static readonly UInt16Field BytesPerSector = new UInt16Field(11);
public static readonly ByteField SectorsPerCluster = new ByteField(13);
public static readonly UInt16Field ReservedSectorCount = new UInt16Field(14);
public static readonly ByteField FatCount = new ByteField(16);
public static readonly UInt16Field RootEntryCount = new UInt16Field(17);
public static readonly UInt16Field TotalSectorCount16 = new UInt16Field(19);
public static readonly EnumField<FatMediaKind, byte> MediaKind = new EnumField<FatMediaKind, byte>(new ByteField(21));
public static readonly UInt16Field FatSectorCount16 = new UInt16Field(22);
public static readonly UInt16Field SectorsPerTrack = new UInt16Field(24);
public static readonly UInt16Field HeadCount = new UInt16Field(26);
public static readonly UInt32Field HiddenSectorCount = new UInt32Field(28);
public static readonly UInt32Field TotalSectorCount32 = new UInt32Field(32);
public static readonly UInt16Field Signature = new UInt16Field(510);
public static class Fat12
{
public static readonly ByteField DriveNumber = new ByteField(36);
public static readonly ByteField Reserved1 = new ByteField(37);
public static readonly ByteField BootSignature = new ByteField(38);
public static readonly UInt32Field VolumeId = new UInt32Field(39);
public static readonly StringField VolumeLabel = new StringField(43, 11, Encoding.ASCII);
public static readonly StringField FileSystemType = new StringField(54, 8, Encoding.ASCII);
}
public static class Fat32
{
public static readonly UInt32Field FatSectorCount32 = new UInt32Field(36);
public static readonly EnumField<Fat32ExtendedFlags, ushort> ExtendedFlags =
new EnumField<Fat32ExtendedFlags, ushort>(new UInt16Field(40));
public static readonly UInt16Field FileSystemVersion = new UInt16Field(42);
public static readonly UInt32Field RootCluster = new UInt32Field(44);
public static readonly UInt16Field FileSystemInfo = new UInt16Field(48);
public static readonly UInt16Field BackupBootSector = new UInt16Field(50);
public static readonly ByteField DriveNumber = new ByteField(64);
public static readonly ByteField Reserved1 = new ByteField(65);
public static readonly ByteField BootSignature = new ByteField(66);
public static readonly UInt32Field VolumeId = new UInt32Field(67);
public static readonly StringField VolumeLabel = new StringField(71, 11, Encoding.ASCII);
public static readonly StringField FileSystemType = new StringField(82, 8, Encoding.ASCII);
}
public BiosParameterBlock(byte[] data)
: base(data)
{
}
}
}

View file

@ -0,0 +1,147 @@
using System;
using System.Text;
namespace Cosmos.System.FileSystem.FAT
{
internal abstract class DataStructure
{
protected DataStructure(byte[] data)
{
Data = data ?? throw new ArgumentNullException(nameof(data));
}
protected byte[] Data { get; }
public T GetValue<T>(Field<T> field) => field.GetValue(Data);
public void SetValue<T>(Field<T> field, T value) => field.SetValue(Data, value);
}
internal abstract class Field<T>
{
public Field(int position, int length)
{
Position = position;
Length = length;
}
/// <summary>
/// Position, in bytes, of the field.
/// </summary>
public int Position { get; }
/// <summary>
/// Length, in bytes, of the field.
/// </summary>
public int Length { get; }
/// <summary>
/// Gets the value for this field from the data structure.
/// </summary>
/// <param name="data">A byte array which contains a data structure for which this field was defined.</param>
/// <returns>The field value.</returns>
public abstract T GetValue(byte[] data);
/// <summary>
/// Sets the value for this field on the data structure.
/// </summary>
/// <param name="data">A byte array which contains a data structure for which this field was defined.</param>
/// <param name="value">The value to set this field to.</param>
public abstract void SetValue(byte[] data, T value);
}
internal class ByteField : Field<byte>
{
public ByteField(int position)
: base(position, 1)
{
}
public override byte GetValue(byte[] data) => data[Position];
public override void SetValue(byte[] data, byte value) => data[Position] = value;
}
internal class UInt16Field : Field<ushort>
{
public UInt16Field(int position)
: base(position, 2)
{
}
public override ushort GetValue(byte[] data) => BitConverter.ToUInt16(data, Position);
public override void SetValue(byte[] data, ushort value)
{
data[Position] = (byte)value;
data[Position + 1] = (byte)(value >> 8);
}
}
internal class Int32Field : Field<int>
{
public Int32Field(int position)
: base(position, 4)
{
}
public override int GetValue(byte[] data) => BitConverter.ToInt32(data, Position);
public override void SetValue(byte[] data, int value)
{
data[Position] = (byte)value;
data[Position + 1] = (byte)(value >> 8);
data[Position + 2] = (byte)(value >> 16);
data[Position + 3] = (byte)(value >> 24);
}
}
internal class UInt32Field : Field<uint>
{
public UInt32Field(int position)
: base(position, 4)
{
}
public override uint GetValue(byte[] data) => BitConverter.ToUInt32(data, Position);
public override void SetValue(byte[] data, uint value)
{
data[Position] = (byte)value;
data[Position + 1] = (byte)(value >> 8);
data[Position + 2] = (byte)(value >> 16);
data[Position + 3] = (byte)(value >> 24);
}
}
internal class StringField : Field<string>
{
public StringField(int position, int length, Encoding encoding)
: base(position, length)
{
Encoding = encoding;
}
public Encoding Encoding { get; }
public override string GetValue(byte[] data) => Encoding.GetString(data, Position, Length);
public override void SetValue(byte[] data, string value) => Encoding.GetBytes(value, 0, Length, data, Position);
}
internal class EnumField<TEnum, TEnumUnderlyingType> : Field<TEnum>
{
private static readonly EnumField<Fat32ExtendedFlags, int> test = new EnumField<Fat32ExtendedFlags, int>(new Int32Field(12));
private readonly Field<TEnumUnderlyingType> _innerField;
public EnumField(Field<TEnumUnderlyingType> innerField)
: base(innerField.Position, innerField.Length)
{
_innerField = innerField;
}
public override TEnum GetValue(byte[] data) => (TEnum)(object)_innerField.GetValue(data);
public override void SetValue(byte[] data, TEnum value) => _innerField.SetValue(data, (TEnumUnderlyingType)(object)value);
}
}

View file

@ -0,0 +1,11 @@
using System;
namespace Cosmos.System.FileSystem.FAT
{
[Flags]
internal enum Fat32ExtendedFlags
{
ActiveFatMask = 0x000F,
Mirrored = 0x0080
}
}

View file

@ -7,6 +7,8 @@ using Cosmos.Common.Extensions;
using Cosmos.HAL.BlockDevice; using Cosmos.HAL.BlockDevice;
using Cosmos.System.FileSystem.FAT.Listing; using Cosmos.System.FileSystem.FAT.Listing;
using Cosmos.System.FileSystem.Listing; using Cosmos.System.FileSystem.Listing;
using Fields = Cosmos.System.FileSystem.FAT.BiosParameterBlock;
using static Cosmos.System.FileSystem.FAT.BiosParameterBlock;
namespace Cosmos.System.FileSystem.FAT namespace Cosmos.System.FileSystem.FAT
{ {
@ -25,12 +27,7 @@ namespace Cosmos.System.FileSystem.FAT
/// <param name="aFatSector">The first sector of the FAT table.</param> /// <param name="aFatSector">The first sector of the FAT table.</param>
public Fat(FatFileSystem aFileSystem, ulong aFatSector) public Fat(FatFileSystem aFileSystem, ulong aFatSector)
{ {
if (aFileSystem == null) mFileSystem = aFileSystem ?? throw new ArgumentNullException(nameof(aFileSystem));
{
throw new ArgumentNullException(nameof(aFileSystem));
}
mFileSystem = aFileSystem;
mFatSector = aFatSector; mFatSector = aFatSector;
} }
@ -41,15 +38,15 @@ namespace Cosmos.System.FileSystem.FAT
/// <exception cref="NotSupportedException">Can not get the FAT entry size for an unknown FAT type.</exception> /// <exception cref="NotSupportedException">Can not get the FAT entry size for an unknown FAT type.</exception>
private uint GetFatEntrySizeInBytes() private uint GetFatEntrySizeInBytes()
{ {
switch (mFileSystem.mFatType) switch (mFileSystem.FatKind)
{ {
case FatTypeEnum.Fat32: case FatKind.Fat32:
return 4; return 4;
case FatTypeEnum.Fat16: case FatKind.Fat16:
return 2; return 2;
case FatTypeEnum.Fat12: case FatKind.Fat12:
// TODO: // TODO:
break; break;
} }
@ -72,9 +69,8 @@ namespace Cosmos.System.FileSystem.FAT
Global.mFileSystemDebugger.SendInternal("aDataSize ="); Global.mFileSystemDebugger.SendInternal("aDataSize =");
Global.mFileSystemDebugger.SendInternal(aDataSize); Global.mFileSystemDebugger.SendInternal(aDataSize);
var xReturn = new uint[0]; var xReturn = Array.Empty<uint>();
uint xCurrentEntry = aFirstEntry; uint xCurrentEntry = aFirstEntry;
uint xValue;
long xEntriesRequired = aDataSize / mFileSystem.BytesPerCluster; long xEntriesRequired = aDataSize / mFileSystem.BytesPerCluster;
if (aDataSize % mFileSystem.BytesPerCluster != 0) if (aDataSize % mFileSystem.BytesPerCluster != 0)
@ -82,7 +78,7 @@ namespace Cosmos.System.FileSystem.FAT
xEntriesRequired++; xEntriesRequired++;
} }
GetFatEntry(xCurrentEntry, out xValue); GetFatEntry(xCurrentEntry, out var xValue);
Array.Resize(ref xReturn, xReturn.Length + 1); Array.Resize(ref xReturn, xReturn.Length + 1);
xReturn[xReturn.Length - 1] = xCurrentEntry; xReturn[xReturn.Length - 1] = xCurrentEntry;
@ -145,8 +141,7 @@ namespace Cosmos.System.FileSystem.FAT
uint xTotalEntries = mFileSystem.FatSectorCount * mFileSystem.BytesPerSector / GetFatEntrySizeInBytes(); uint xTotalEntries = mFileSystem.FatSectorCount * mFileSystem.BytesPerSector / GetFatEntrySizeInBytes();
for (uint i = mFileSystem.RootCluster; i < xTotalEntries; i++) for (uint i = mFileSystem.RootCluster; i < xTotalEntries; i++)
{ {
uint xEntryValue; GetFatEntry(i, out var xEntryValue);
GetFatEntry(i, out xEntryValue);
if (!FatEntryIsEof(xEntryValue)) if (!FatEntryIsEof(xEntryValue))
{ {
Global.mFileSystemDebugger.SendInternal("i ="); Global.mFileSystemDebugger.SendInternal("i =");
@ -172,17 +167,15 @@ namespace Cosmos.System.FileSystem.FAT
uint xEntrySize = GetFatEntrySizeInBytes(); uint xEntrySize = GetFatEntrySizeInBytes();
ulong xEntryOffset = aEntryNumber * xEntrySize; ulong xEntryOffset = aEntryNumber * xEntrySize;
switch (mFileSystem.FatKind)
switch (mFileSystem.mFatType)
{ {
case FatTypeEnum.Fat12: case FatKind.Fat12:
aData.SetUInt16(xEntryOffset, (ushort)aValue); aData.SetUInt16(xEntryOffset, (ushort)aValue);
break; break;
case FatTypeEnum.Fat16: case FatKind.Fat16:
aData.SetUInt16(xEntryOffset, (ushort)aValue); aData.SetUInt16(xEntryOffset, (ushort)aValue);
break; break;
case FatTypeEnum.Fat32: case FatKind.Fat32:
aData.SetUInt32(xEntryOffset, (uint)aValue); aData.SetUInt32(xEntryOffset, (uint)aValue);
break; break;
default: default:
@ -195,9 +188,9 @@ namespace Cosmos.System.FileSystem.FAT
uint xEntrySize = GetFatEntrySizeInBytes(); uint xEntrySize = GetFatEntrySizeInBytes();
ulong xEntryOffset = aEntryNumber * xEntrySize; ulong xEntryOffset = aEntryNumber * xEntrySize;
switch (mFileSystem.mFatType) switch (mFileSystem.FatKind)
{ {
case FatTypeEnum.Fat12: case FatKind.Fat12:
// We now access the FAT entry as a WORD just as we do for FAT16, but if the cluster number is // We now access the FAT entry as a WORD just as we do for FAT16, but if the cluster number is
// EVEN, we only want the low 12-bits of the 16-bits we fetch. If the cluster number is ODD // EVEN, we only want the low 12-bits of the 16-bits we fetch. If the cluster number is ODD
// we want the high 12-bits of the 16-bits we fetch. // we want the high 12-bits of the 16-bits we fetch.
@ -211,10 +204,10 @@ namespace Cosmos.System.FileSystem.FAT
aValue = xResult >> 4; // Odd aValue = xResult >> 4; // Odd
} }
break; break;
case FatTypeEnum.Fat16: case FatKind.Fat16:
aValue = BitConverter.ToUInt16(aData, (int)xEntryOffset); aValue = BitConverter.ToUInt16(aData, (int)xEntryOffset);
break; break;
case FatTypeEnum.Fat32: case FatKind.Fat32:
aValue = BitConverter.ToUInt32(aData, (int)xEntryOffset) & 0x0FFFFFFF; aValue = BitConverter.ToUInt32(aData, (int)xEntryOffset) & 0x0FFFFFFF;
break; break;
default: default:
@ -232,8 +225,7 @@ namespace Cosmos.System.FileSystem.FAT
Global.mFileSystemDebugger.SendInternal($"RootCluster is {mFileSystem.RootCluster}"); Global.mFileSystemDebugger.SendInternal($"RootCluster is {mFileSystem.RootCluster}");
Global.mFileSystemDebugger.SendInternal("Clearing all Fat Table"); Global.mFileSystemDebugger.SendInternal("Clearing all Fat Table");
byte[] xFatTableFistSector; ReadFatSector(0, out var xFatTableFistSector);
ReadFatSector(0, out xFatTableFistSector);
/* Change 3rd entry (RootDirectory) to be EOC */ /* Change 3rd entry (RootDirectory) to be EOC */
SetFatEntry(xFatTableFistSector, 2, FatEntryEofValue()); SetFatEntry(xFatTableFistSector, 2, FatEntryEofValue());
@ -312,9 +304,9 @@ namespace Cosmos.System.FileSystem.FAT
ReadFatSector(xSector, out xData); ReadFatSector(xSector, out xData);
switch (mFileSystem.mFatType) switch (mFileSystem.FatKind)
{ {
case FatTypeEnum.Fat12: case FatKind.Fat12:
// We now access the FAT entry as a WORD just as we do for FAT16, but if the cluster number is // We now access the FAT entry as a WORD just as we do for FAT16, but if the cluster number is
// EVEN, we only want the low 12-bits of the 16-bits we fetch. If the cluster number is ODD // EVEN, we only want the low 12-bits of the 16-bits we fetch. If the cluster number is ODD
// we want the high 12-bits of the 16-bits we fetch. // we want the high 12-bits of the 16-bits we fetch.
@ -329,11 +321,11 @@ namespace Cosmos.System.FileSystem.FAT
} }
break; break;
case FatTypeEnum.Fat16: case FatKind.Fat16:
aValue = BitConverter.ToUInt16(xData, (int)xEntryOffset); aValue = BitConverter.ToUInt16(xData, (int)xEntryOffset);
break; break;
case FatTypeEnum.Fat32: case FatKind.Fat32:
aValue = BitConverter.ToUInt32(xData, (int)xEntryOffset) & 0x0FFFFFFF; aValue = BitConverter.ToUInt32(xData, (int)xEntryOffset) & 0x0FFFFFFF;
break; break;
@ -364,20 +356,17 @@ namespace Cosmos.System.FileSystem.FAT
byte[] xData; byte[] xData;
ReadFatSector(xSector, out xData); ReadFatSector(xSector, out xData);
switch (mFileSystem.mFatType) switch (mFileSystem.FatKind)
{ {
case FatTypeEnum.Fat12: case FatKind.Fat12:
xData.SetUInt16(xEntryOffset, (ushort)aValue); xData.SetUInt16(xEntryOffset, (ushort)aValue);
break; break;
case FatKind.Fat16:
case FatTypeEnum.Fat16:
xData.SetUInt16(xEntryOffset, (ushort)aValue); xData.SetUInt16(xEntryOffset, (ushort)aValue);
break; break;
case FatKind.Fat32:
case FatTypeEnum.Fat32:
xData.SetUInt32(xEntryOffset, (uint)aValue); xData.SetUInt32(xEntryOffset, (uint)aValue);
break; break;
default: default:
throw new NotSupportedException("Unknown FAT type."); throw new NotSupportedException("Unknown FAT type.");
} }
@ -393,188 +382,170 @@ namespace Cosmos.System.FileSystem.FAT
/// <exception cref="Exception">Unknown file system type.</exception> /// <exception cref="Exception">Unknown file system type.</exception>
private bool FatEntryIsEof(ulong aValue) private bool FatEntryIsEof(ulong aValue)
{ {
switch (mFileSystem.mFatType) switch (mFileSystem.FatKind)
{ {
case FatTypeEnum.Fat12: case FatKind.Fat12:
return aValue >= 0xFF8; return aValue >= 0xFF8;
case FatKind.Fat16:
case FatTypeEnum.Fat16:
return aValue >= 0xFFF8; return aValue >= 0xFFF8;
case FatKind.Fat32:
case FatTypeEnum.Fat32:
return aValue >= 0xFFFFFF8; return aValue >= 0xFFFFFF8;
default: default:
throw new Exception("Unknown file system type."); throw new Exception("Unknown file system type.");
} }
} }
/// <summary> /// <summary>
/// The the EOF value for a specific FAT type. /// The EOF value for a specific FAT type.
/// </summary> /// </summary>
/// <returns>The EOF value.</returns> /// <returns>The EOF value.</returns>
/// <exception cref="Exception">Unknown file system type.</exception> /// <exception cref="Exception">Unknown file system type.</exception>
private ulong FatEntryEofValue() private ulong FatEntryEofValue()
{ {
switch (mFileSystem.mFatType) switch (mFileSystem.FatKind)
{ {
case FatTypeEnum.Fat12: case FatKind.Fat12:
return 0x0FFF; return 0x0FFF;
case FatKind.Fat16:
case FatTypeEnum.Fat16:
return 0xFFFF; return 0xFFFF;
case FatKind.Fat32:
case FatTypeEnum.Fat32:
return 0x0FFFFFFF; return 0x0FFFFFFF;
default: default:
throw new Exception("Unknown file system type."); throw new Exception("Unknown file system type.");
} }
} }
} }
public readonly uint BytesPerCluster;
public readonly uint BytesPerSector;
public readonly uint ClusterCount;
public readonly uint DataSector; // First Data Sector
public readonly uint DataSectorCount;
public readonly uint FatSectorCount;
private readonly FatTypeEnum mFatType;
public readonly uint NumberOfFATs;
public readonly uint ReservedSectorCount;
public readonly uint RootCluster; // FAT32
public readonly uint RootEntryCount;
public readonly uint RootSector; // FAT12/16
public readonly uint RootSectorCount; // FAT12/16, FAT32 remains 0
public readonly uint SectorsPerCluster;
public readonly uint TotalSectorCount;
private readonly Fat[] mFats; private readonly Fat[] mFats;
public override string Type
{
get
{
switch (mFatType)
{
case FatTypeEnum.Fat12:
return "FAT12";
case FatTypeEnum.Fat16:
return "FAT16";
case FatTypeEnum.Fat32:
return "FAT32";
default:
throw new Exception("Unknown FAT file system type.");
}
}
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="FatFileSystem"/> class. /// Initializes a new instance of the <see cref="FatFileSystem"/> class.
/// </summary> /// </summary>
/// <param name="aDevice">The partition.</param> /// <param name="aDevice">The partition.</param>
/// <param name="aRootPath">The root path.</param> /// <param name="aRootPath">The root path.</param>
/// <exception cref="Exception">FAT signature not found.</exception> /// <exception cref="Exception">FAT signature not found.</exception>
public FatFileSystem(Partition aDevice, string aRootPath, long aSize) public FatFileSystem(BiosParameterBlock bpb, Partition aDevice, string aRootPath, long aSize)
: base(aDevice, aRootPath, aSize) : base(aDevice, aRootPath, aSize)
{ {
if (aDevice == null) BiosParameterBlock = bpb;
{
throw new ArgumentNullException(nameof(aDevice));
}
if (String.IsNullOrEmpty(aRootPath)) BytesPerSector = BiosParameterBlock.GetValue(Fields.BytesPerSector);
{ SectorsPerCluster = BiosParameterBlock.GetValue(Fields.SectorsPerCluster);
throw new ArgumentException("Argument is null or empty", nameof(aRootPath));
}
var xBPB = Device.NewBlockArray(1); BytesPerCluster = (uint)(BytesPerSector * SectorsPerCluster);
Device.ReadBlock(0UL, 1U, xBPB); ReservedSectorCount = BiosParameterBlock.GetValue(Fields.ReservedSectorCount);
ushort xSig = BitConverter.ToUInt16(xBPB, 510); FatCount = BiosParameterBlock.GetValue(Fields.FatCount);
if (xSig != 0xAA55)
{
throw new Exception("FAT signature not found.");
}
BytesPerSector = BitConverter.ToUInt16(xBPB, 11); var rootEntryCount = BiosParameterBlock.GetValue(Fields.RootEntryCount);
SectorsPerCluster = xBPB[13];
BytesPerCluster = BytesPerSector * SectorsPerCluster;
ReservedSectorCount = BitConverter.ToUInt16(xBPB, 14);
NumberOfFATs = xBPB[16];
RootEntryCount = BitConverter.ToUInt16(xBPB, 17);
TotalSectorCount = BitConverter.ToUInt16(xBPB, 19); // 1.
if (TotalSectorCount == 0) RootDirectorySectorCount = (uint)(((rootEntryCount * 32) + (BytesPerSector - 1)) / BytesPerSector);
{
TotalSectorCount = BitConverter.ToUInt32(xBPB, 32); // 2.
} FatSectorCount = BiosParameterBlock.GetValue(Fields.FatSectorCount16);
// FATSz
FatSectorCount = BitConverter.ToUInt16(xBPB, 22);
if (FatSectorCount == 0) if (FatSectorCount == 0)
{ {
FatSectorCount = BitConverter.ToUInt32(xBPB, 36); FatSectorCount = BiosParameterBlock.GetValue(Fat32.FatSectorCount32);
} }
DataSectorCount = TotalSectorCount - TotalSectorCount = BiosParameterBlock.GetValue(Fields.TotalSectorCount16);
(ReservedSectorCount + NumberOfFATs * FatSectorCount + ReservedSectorCount);
// Computation rounds down. if (TotalSectorCount == 0)
{
TotalSectorCount = BiosParameterBlock.GetValue(Fields.TotalSectorCount32);
}
FirstDataSector = ReservedSectorCount + (FatCount * FatSectorCount) + RootDirectorySectorCount;
DataSectorCount = TotalSectorCount - FirstDataSector;
// 3.
ClusterCount = DataSectorCount / SectorsPerCluster; ClusterCount = DataSectorCount / SectorsPerCluster;
// Determine the FAT type. Do not use another method - this IS the official and
// proper way to determine FAT type.
// Comparisons are purposefully < and not <=
// FAT16 starts at 4085, FAT32 starts at 65525
if (ClusterCount < 4085) if (ClusterCount < 4085)
{ {
mFatType = FatTypeEnum.Fat12; FatKind = FatKind.Fat12;
} }
else if (ClusterCount < 65525) else if (ClusterCount < 65525)
{ {
mFatType = FatTypeEnum.Fat16; FatKind = FatKind.Fat16;
} }
else else
{ {
mFatType = FatTypeEnum.Fat32; FatKind = FatKind.Fat32;
} }
if (mFatType == FatTypeEnum.Fat32) if (FatKind == FatKind.Fat32)
{ {
RootCluster = BitConverter.ToUInt32(xBPB, 44); RootCluster = BiosParameterBlock.GetValue(Fat32.RootCluster);
} }
else else
{ {
RootSector = ReservedSectorCount + NumberOfFATs * FatSectorCount; RootSector = ReservedSectorCount + FatCount * FatSectorCount;
RootSectorCount = (RootEntryCount * 32 + (BytesPerSector - 1)) / BytesPerSector;
} }
DataSector = ReservedSectorCount + NumberOfFATs * FatSectorCount + RootSectorCount;
mFats = new Fat[NumberOfFATs]; mFats = new Fat[FatCount];
for (ulong i = 0; i < NumberOfFATs; i++) for (ulong i = 0; i < FatCount; i++)
{ {
mFats[i] = new Fat(this, (ReservedSectorCount + i * FatSectorCount)); mFats[i] = new Fat(this, (ReservedSectorCount + i * FatSectorCount));
} }
} }
public FatKind FatKind { get; }
protected BiosParameterBlock BiosParameterBlock { get; }
protected ushort BytesPerSector { get; }
protected byte SectorsPerCluster { get; }
public uint BytesPerCluster { get; }
protected ushort ReservedSectorCount { get; }
protected byte FatCount { get; }
protected uint RootDirectorySectorCount { get; }
protected uint FatSectorCount { get; }
protected uint TotalSectorCount { get; }
protected uint FirstDataSector { get; }
protected uint DataSectorCount { get; }
protected uint ClusterCount { get; }
// FAT12/16
public uint RootSector { get; }
// FAT32
public uint RootCluster { get; }
public override string Type
{
get
{
switch (FatKind)
{
case FatKind.Fat12:
return "FAT12";
case FatKind.Fat16:
return "FAT16";
case FatKind.Fat32:
return "FAT32";
default:
throw new Exception("Unknown FAT file system type.");
}
}
}
internal Fat GetFat(int aTableNumber) internal Fat GetFat(int aTableNumber)
{ {
if (mFats.Length > aTableNumber) if (mFats.Length > aTableNumber)
@ -582,7 +553,7 @@ namespace Cosmos.System.FileSystem.FAT
return mFats[aTableNumber]; return mFats[aTableNumber];
} }
throw new Exception("The fat table number doesn't exist."); throw new Exception("The FAT table number doesn't exist.");
} }
internal byte[] NewBlockArray() internal byte[] NewBlockArray()
@ -599,16 +570,16 @@ namespace Cosmos.System.FileSystem.FAT
aSize = BytesPerCluster; aSize = BytesPerCluster;
} }
if (mFatType == FatTypeEnum.Fat32) if (FatKind == FatKind.Fat32)
{ {
aData = NewBlockArray(); aData = NewBlockArray();
long xSector = DataSector + (aCluster - RootCluster) * SectorsPerCluster; long xSector = FirstDataSector + (aCluster - RootCluster) * SectorsPerCluster;
Device.ReadBlock((ulong)xSector, SectorsPerCluster, aData); Device.ReadBlock((ulong)xSector, SectorsPerCluster, aData);
} }
else else
{ {
aData = Device.NewBlockArray(1); aData = Device.NewBlockArray(1);
Device.ReadBlock((ulong)aCluster, RootSectorCount, aData); Device.ReadBlock((ulong)aCluster, RootDirectorySectorCount, aData);
} }
} }
@ -626,19 +597,19 @@ namespace Cosmos.System.FileSystem.FAT
aSize = BytesPerCluster; aSize = BytesPerCluster;
} }
byte[] xData; Read(aCluster, out var xData);
Read(aCluster, out xData);
Array.Copy(aData, 0, xData, aOffset, aData.Length); Array.Copy(aData, 0, xData, aOffset, aData.Length);
if (mFatType == FatTypeEnum.Fat32)
if (FatKind == FatKind.Fat32)
{ {
long xSector = DataSector + (aCluster - RootCluster) * SectorsPerCluster; long xSector = FirstDataSector + (aCluster - RootCluster) * SectorsPerCluster;
Device.WriteBlock((ulong)xSector, SectorsPerCluster, xData); Device.WriteBlock((ulong)xSector, SectorsPerCluster, xData);
} }
else else
{ {
Device.WriteBlock((ulong)aCluster, RootSectorCount, xData); Device.WriteBlock((ulong)aCluster, RootSector, xData);
} }
} }
@ -648,16 +619,15 @@ namespace Cosmos.System.FileSystem.FAT
global::System.Console.WriteLine("Bytes per Cluster = " + BytesPerCluster); global::System.Console.WriteLine("Bytes per Cluster = " + BytesPerCluster);
global::System.Console.WriteLine("Bytes per Sector = " + BytesPerSector); global::System.Console.WriteLine("Bytes per Sector = " + BytesPerSector);
global::System.Console.WriteLine("Cluster Count = " + ClusterCount); global::System.Console.WriteLine("Cluster Count = " + ClusterCount);
global::System.Console.WriteLine("Data Sector = " + DataSector); global::System.Console.WriteLine("First Data Sector = " + FirstDataSector);
global::System.Console.WriteLine("Data Sector Count = " + DataSectorCount); global::System.Console.WriteLine("Data Sector Count = " + DataSectorCount);
global::System.Console.WriteLine("FAT Sector Count = " + FatSectorCount); global::System.Console.WriteLine("FAT Sector Count = " + FatSectorCount);
global::System.Console.WriteLine("FAT Type = " + (uint)mFatType); global::System.Console.WriteLine("FAT Kind = " + (uint)FatKind);
global::System.Console.WriteLine("Number of FATS = " + NumberOfFATs); global::System.Console.WriteLine("Number of FATs = " + FatCount);
global::System.Console.WriteLine("Reserved Sector Count = " + ReservedSectorCount); global::System.Console.WriteLine("Reserved Sector Count = " + ReservedSectorCount);
global::System.Console.WriteLine("Root Cluster = " + RootCluster); global::System.Console.WriteLine("Root Cluster = " + RootCluster);
global::System.Console.WriteLine("Root Entry Count = " + RootEntryCount); global::System.Console.WriteLine("Root Entry Count = " + BiosParameterBlock.GetValue(Fields.RootEntryCount));
global::System.Console.WriteLine("Root Sector = " + RootSector); global::System.Console.WriteLine("Root Sector Count = " + RootDirectorySectorCount);
global::System.Console.WriteLine("Root Sector Count = " + RootSectorCount);
global::System.Console.WriteLine("Sectors per Cluster = " + SectorsPerCluster); global::System.Console.WriteLine("Sectors per Cluster = " + SectorsPerCluster);
global::System.Console.WriteLine("Total Sector Count = " + TotalSectorCount); global::System.Console.WriteLine("Total Sector Count = " + TotalSectorCount);
@ -667,26 +637,24 @@ namespace Cosmos.System.FileSystem.FAT
Global.mFileSystemDebugger.SendInternal(BytesPerSector); Global.mFileSystemDebugger.SendInternal(BytesPerSector);
Global.mFileSystemDebugger.SendInternal("Cluster Count ="); Global.mFileSystemDebugger.SendInternal("Cluster Count =");
Global.mFileSystemDebugger.SendInternal(ClusterCount); Global.mFileSystemDebugger.SendInternal(ClusterCount);
Global.mFileSystemDebugger.SendInternal("Data Sector ="); Global.mFileSystemDebugger.SendInternal("First Data Sector =");
Global.mFileSystemDebugger.SendInternal(DataSector); Global.mFileSystemDebugger.SendInternal(FirstDataSector);
Global.mFileSystemDebugger.SendInternal("Data Sector Count ="); Global.mFileSystemDebugger.SendInternal("Data Sector Count =");
Global.mFileSystemDebugger.SendInternal(DataSectorCount); Global.mFileSystemDebugger.SendInternal(DataSectorCount);
Global.mFileSystemDebugger.SendInternal("FAT Sector Count ="); Global.mFileSystemDebugger.SendInternal("FAT Sector Count =");
Global.mFileSystemDebugger.SendInternal(FatSectorCount); Global.mFileSystemDebugger.SendInternal(FatSectorCount);
Global.mFileSystemDebugger.SendInternal("FAT Type ="); Global.mFileSystemDebugger.SendInternal("FAT Type =");
Global.mFileSystemDebugger.SendInternal((uint)mFatType); Global.mFileSystemDebugger.SendInternal((uint)FatKind);
Global.mFileSystemDebugger.SendInternal("Number of FATS ="); Global.mFileSystemDebugger.SendInternal("Number of FATs =");
Global.mFileSystemDebugger.SendInternal(NumberOfFATs); Global.mFileSystemDebugger.SendInternal(FatCount);
Global.mFileSystemDebugger.SendInternal("Reserved Sector Count ="); Global.mFileSystemDebugger.SendInternal("Reserved Sector Count =");
Global.mFileSystemDebugger.SendInternal(ReservedSectorCount); Global.mFileSystemDebugger.SendInternal(ReservedSectorCount);
Global.mFileSystemDebugger.SendInternal("Root Cluster ="); Global.mFileSystemDebugger.SendInternal("Root Cluster =");
Global.mFileSystemDebugger.SendInternal(RootCluster); Global.mFileSystemDebugger.SendInternal(RootCluster);
Global.mFileSystemDebugger.SendInternal("Root Entry Count ="); Global.mFileSystemDebugger.SendInternal("Root Entry Count =");
Global.mFileSystemDebugger.SendInternal(RootEntryCount); Global.mFileSystemDebugger.SendInternal(BiosParameterBlock.GetValue(Fields.RootEntryCount));
Global.mFileSystemDebugger.SendInternal("Root Sector =");
Global.mFileSystemDebugger.SendInternal(RootSector);
Global.mFileSystemDebugger.SendInternal("Root Sector Count ="); Global.mFileSystemDebugger.SendInternal("Root Sector Count =");
Global.mFileSystemDebugger.SendInternal(RootSectorCount); Global.mFileSystemDebugger.SendInternal(RootDirectorySectorCount);
Global.mFileSystemDebugger.SendInternal("Sectors per Cluster ="); Global.mFileSystemDebugger.SendInternal("Sectors per Cluster =");
Global.mFileSystemDebugger.SendInternal(SectorsPerCluster); Global.mFileSystemDebugger.SendInternal(SectorsPerCluster);
Global.mFileSystemDebugger.SendInternal("Total Sector Count ="); Global.mFileSystemDebugger.SendInternal("Total Sector Count =");
@ -869,17 +837,6 @@ namespace Cosmos.System.FileSystem.FAT
} }
} }
private enum FatTypeEnum
{
Unknown,
Fat12,
Fat16,
Fat32
}
public override void Format(string aDriveFormat, bool aQuick) public override void Format(string aDriveFormat, bool aQuick)
{ {
var xRootDirectory = (FatDirectoryEntry)GetRootDirectory(); var xRootDirectory = (FatDirectoryEntry)GetRootDirectory();

View file

@ -0,0 +1,54 @@
using System;
using Cosmos.HAL.BlockDevice;
namespace Cosmos.System.FileSystem.FAT
{
public class FatFileSystemFactory : FileSystemFactory
{
public override string Name => "FAT";
public override bool IsType(Partition aDevice)
{
if (aDevice == null)
{
throw new ArgumentNullException(nameof(aDevice));
}
var bootSector = ReadBootSector(aDevice);
var bpb = new BiosParameterBlock(bootSector);
return IsFatPartition(bpb);
}
/// <summary>
/// Initializes a new instance of the <see cref="FatFileSystem"/> class.
/// </summary>
/// <param name="aDevice">The partition.</param>
/// <param name="aRootPath">The root path.</param>
/// <exception cref="Exception">FAT signature not found.</exception>
public override FileSystem Create(Partition aDevice, string aRootPath, long aSize)
{
var bootSector = ReadBootSector(aDevice);
var bpb = new BiosParameterBlock(bootSector);
if (!IsFatPartition(bpb))
{
throw new InvalidOperationException("Partition file system is not FAT!");
}
return new FatFileSystem(bpb, aDevice, aRootPath, aSize);
}
private byte[] ReadBootSector(Partition partition)
{
var bootSector = partition.NewBlockArray(1);
partition.ReadBlock(0, 1, bootSector);
return bootSector;
}
private bool IsFatPartition(BiosParameterBlock bpb) =>
bpb.GetValue(BiosParameterBlock.Signature) == 0xAA55;
}
}

View file

@ -0,0 +1,10 @@
namespace Cosmos.System.FileSystem.FAT
{
internal enum FatKind
{
Unkown,
Fat12,
Fat16,
Fat32
}
}

View file

@ -0,0 +1,15 @@
namespace Cosmos.System.FileSystem.FAT
{
public enum FatMediaKind
{
Removable = 0xF0,
Fixed = 0xF8,
Floppy1 = 0xF9,
Floppy2 = 0xFA,
Floppy3 = 0xFB,
Floppy4 = 0xFC,
Floppy5 = 0xFD,
Floppy6 = 0xFE,
Floppy7 = 0xFF
}
}

View file

@ -31,12 +31,7 @@ namespace Cosmos.System.FileSystem.FAT
public FatStream(FatDirectoryEntry aEntry) public FatStream(FatDirectoryEntry aEntry)
{ {
if (aEntry == null) mDirectoryEntry = aEntry ?? throw new ArgumentNullException(nameof(aEntry));
{
throw new ArgumentNullException(nameof(aEntry));
}
mDirectoryEntry = aEntry;
mFS = aEntry.GetFileSystem(); mFS = aEntry.GetFileSystem();
mFatTable = aEntry.GetFatTable(); mFatTable = aEntry.GetFatTable();
mSize = aEntry.mSize; mSize = aEntry.mSize;
@ -47,29 +42,11 @@ namespace Cosmos.System.FileSystem.FAT
} }
} }
public override bool CanSeek public override bool CanSeek => true;
{
get
{
return true;
}
}
public override bool CanRead public override bool CanRead => true;
{
get
{
return true;
}
}
public override bool CanWrite public override bool CanWrite => true;
{
get
{
return true;
}
}
public sealed override long Length public sealed override long Length
{ {
@ -78,7 +55,7 @@ namespace Cosmos.System.FileSystem.FAT
Global.mFileSystemDebugger.SendInternal("-- FatStream.get_Length --"); Global.mFileSystemDebugger.SendInternal("-- FatStream.get_Length --");
Global.mFileSystemDebugger.SendInternal("Length ="); Global.mFileSystemDebugger.SendInternal("Length =");
Global.mFileSystemDebugger.SendInternal(mSize); Global.mFileSystemDebugger.SendInternal(mSize);
return (long)mSize; return mSize;
} }
} }
@ -89,7 +66,7 @@ namespace Cosmos.System.FileSystem.FAT
Global.mFileSystemDebugger.SendInternal("-- FatStream.get_Position --"); Global.mFileSystemDebugger.SendInternal("-- FatStream.get_Position --");
Global.mFileSystemDebugger.SendInternal("Position ="); Global.mFileSystemDebugger.SendInternal("Position =");
Global.mFileSystemDebugger.SendInternal(mPosition); Global.mFileSystemDebugger.SendInternal(mPosition);
return (long)mPosition; return mPosition;
} }
set set
{ {

View file

@ -1,34 +0,0 @@
using System;
using Cosmos.HAL.BlockDevice;
using Cosmos.System.FileSystem.FAT;
namespace Cosmos.System.FileSystem
{
public class FatFileSystemFactory : FileSystemFactory
{
public override string Name => "FAT";
public override bool IsType(Partition aDevice)
{
if (aDevice == null)
{
throw new ArgumentNullException(nameof(aDevice));
}
var xBPB = aDevice.NewBlockArray(1);
aDevice.ReadBlock(0UL, 1U, xBPB);
var xSig = BitConverter.ToUInt16(xBPB, 510);
return xSig == 0xAA55;
}
/// <summary>
/// Initializes a new instance of the <see cref="FatFileSystem"/> class.
/// </summary>
/// <param name="aDevice">The partition.</param>
/// <param name="aRootPath">The root path.</param>
/// <exception cref="Exception">FAT signature not found.</exception>
public override FileSystem Create(Partition aDevice, string aRootPath, long aSize) => new FatFileSystem(aDevice, aRootPath, aSize);
}
}

View file

@ -1,8 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using Cosmos.HAL.BlockDevice; using Cosmos.HAL.BlockDevice;
using Cosmos.System.FileSystem.FAT;
using Cosmos.System.FileSystem.Listing; using Cosmos.System.FileSystem.Listing;
namespace Cosmos.System.FileSystem namespace Cosmos.System.FileSystem
@ -11,8 +10,8 @@ namespace Cosmos.System.FileSystem
{ {
protected FileSystem(Partition aDevice, string aRootPath, long aSize) protected FileSystem(Partition aDevice, string aRootPath, long aSize)
{ {
Device = aDevice; Device = aDevice ?? throw new ArgumentNullException(nameof(aDevice));
RootPath = aRootPath; RootPath = aRootPath ?? throw new ArgumentNullException(nameof(aRootPath));
Size = aSize; Size = aSize;
} }

View file

@ -1,4 +1,6 @@
using Cosmos.HAL.BlockDevice; using System;
using Cosmos.HAL.BlockDevice;
namespace Cosmos.System.FileSystem namespace Cosmos.System.FileSystem
{ {
@ -15,6 +17,7 @@ namespace Cosmos.System.FileSystem
/// <param name="aDevice">The partition.</param> /// <param name="aDevice">The partition.</param>
/// <returns>Returns true if the file system can handle the partition, false otherwise.</returns> /// <returns>Returns true if the file system can handle the partition, false otherwise.</returns>
public abstract bool IsType(Partition aDevice); public abstract bool IsType(Partition aDevice);
/// <summary> /// <summary>
/// Creates a new <see cref="FileSystem"/> object for the given partition, root path, and size. /// Creates a new <see cref="FileSystem"/> object for the given partition, root path, and size.
/// </summary> /// </summary>
@ -23,5 +26,7 @@ namespace Cosmos.System.FileSystem
/// <param name="aSize">The size, in MB.</param> /// <param name="aSize">The size, in MB.</param>
/// <returns></returns> /// <returns></returns>
public abstract FileSystem Create(Partition aDevice, string aRootPath, long aSize); public abstract FileSystem Create(Partition aDevice, string aRootPath, long aSize);
public virtual void Format(Partition partition) => throw new NotImplementedException();
} }
} }