//#define COSMOSDEBUG
using System;
using System.Collections.Generic;
using Cosmos.Common.Extensions;
using Cosmos.HAL.BlockDevice;
using Cosmos.System.FileSystem.FAT.Listing;
using Cosmos.System.FileSystem.Listing;
namespace Cosmos.System.FileSystem.FAT
{
///
/// FatFileSystem class.
///
internal class FatFileSystem : FileSystem
{
///
/// FAT class. Used to manage individual FAT entry.
///
internal class Fat
{
private readonly FatFileSystem mFileSystem;
private readonly ulong mFatSector;
///
/// Initializes a new instance of the class.
///
/// The file system.
/// The first sector of the FAT table.
/// Thrown when aFileSystem is null.
public Fat(FatFileSystem aFileSystem, ulong aFatSector)
{
if (aFileSystem == null)
{
throw new ArgumentNullException(nameof(aFileSystem));
}
mFileSystem = aFileSystem;
mFatSector = aFatSector;
}
///
/// Gets the size of a FAT entry in bytes.
///
/// The size of a FAT entry in bytes.
/// Thrown when FAT type is unknown.
private uint GetFatEntrySizeInBytes()
{
switch (mFileSystem.mFatType)
{
case FatTypeEnum.Fat32:
return 4;
case FatTypeEnum.Fat16:
return 2;
case FatTypeEnum.Fat12:
// TODO:
break;
}
throw new NotSupportedException("Can not get the FAT entry size for an unknown FAT type.");
}
///
/// Gets the FAT chain.
///
/// The first entry.
/// Size of a data to be stored in bytes.
/// An array of cluster numbers for the FAT chain.
/// Thrown when the size of the chain is less then zero. (Never thrown)
/// Thrown when data lenght is greater then Int32.MaxValue.
///
///
/// - Thrown on out of memory.
/// - data size invalid.
/// - unknown file system type
/// - memory error.
///
///
/// Thrown when bad aFirstEntry passed.
/// Thrown on fatal error (contact support).
/// Thrown when FAT type is unknown.
public uint[] GetFatChain(uint aFirstEntry, long aDataSize = 0)
{
Global.mFileSystemDebugger.SendInternal("-- Fat.GetFatChain --");
Global.mFileSystemDebugger.SendInternal("aFirstEntry =");
Global.mFileSystemDebugger.SendInternal(aFirstEntry);
Global.mFileSystemDebugger.SendInternal("aDataSize =");
Global.mFileSystemDebugger.SendInternal(aDataSize);
var xReturn = new uint[0];
uint xCurrentEntry = aFirstEntry;
uint xValue;
long xEntriesRequired = aDataSize / mFileSystem.BytesPerCluster;
if (aDataSize % mFileSystem.BytesPerCluster != 0)
{
xEntriesRequired++;
}
GetFatEntry(xCurrentEntry, out xValue);
Array.Resize(ref xReturn, xReturn.Length + 1);
xReturn[xReturn.Length - 1] = xCurrentEntry;
Global.mFileSystemDebugger.SendInternal("xEntriesRequired =");
Global.mFileSystemDebugger.SendInternal(xEntriesRequired);
Global.mFileSystemDebugger.SendInternal("xCurrentEntry =");
Global.mFileSystemDebugger.SendInternal(xCurrentEntry);
Global.mFileSystemDebugger.SendInternal("xReturn.Length =");
Global.mFileSystemDebugger.SendInternal(xReturn.Length);
if (xEntriesRequired > 0)
{
while (!FatEntryIsEof(xValue))
{
xCurrentEntry = xValue;
GetFatEntry(xCurrentEntry, out xValue);
Array.Resize(ref xReturn, xReturn.Length + 1);
xReturn[xReturn.Length - 1] = xCurrentEntry;
Global.mFileSystemDebugger.SendInternal("xCurrentEntry =");
Global.mFileSystemDebugger.SendInternal(xCurrentEntry);
Global.mFileSystemDebugger.SendInternal("xReturn.Length =");
Global.mFileSystemDebugger.SendInternal(xReturn.Length);
}
if (xEntriesRequired > xReturn.Length)
{
long xNewClusters = xEntriesRequired - xReturn.Length;
for (int i = 0; i < xNewClusters; i++)
{
xCurrentEntry = GetNextUnallocatedFatEntry();
uint xLastFatEntry = xReturn[xReturn.Length - 1];
SetFatEntry(xLastFatEntry, xCurrentEntry);
SetFatEntry(xCurrentEntry, FatEntryEofValue());
Array.Resize(ref xReturn, xReturn.Length + 1);
xReturn[xReturn.Length - 1] = xCurrentEntry;
}
}
}
string xChain = "";
for (int i = 0; i < xReturn.Length; i++)
{
xChain += xReturn[i];
if (i > 0 || i < xReturn.Length - 1)
{
xChain += "->";
}
}
Global.mFileSystemDebugger.SendInternal("Fat xChain:");
Global.mFileSystemDebugger.SendInternal(xChain);
SetFatEntry(xCurrentEntry, FatEntryEofValue());
return xReturn;
}
///
/// Gets the next unallocated FAT entry.
///
/// The index of the next unallocated FAT entry.
/// Thrown when data lenght is greater then Int32.MaxValue.
/// Thrown when data size invalid / Failed to find an unallocated FAT entry.
/// Thrown on fatal error (contact support).
/// Thrown on fatal error (contact support).
/// Thrown on fatal error (contact support).
/// Thrown when FAT type is unknown.
public uint GetNextUnallocatedFatEntry()
{
Global.mFileSystemDebugger.SendInternal("-- Fat.GetNextUnallocatedFatEntry --");
uint xTotalEntries = mFileSystem.FatSectorCount * mFileSystem.BytesPerSector / GetFatEntrySizeInBytes();
for (uint i = mFileSystem.RootCluster; i < xTotalEntries; i++)
{
GetFatEntry(i, out uint xEntryValue);
if (FatEntryIsFree(xEntryValue))
{
Global.mFileSystemDebugger.SendInternal("i =");
Global.mFileSystemDebugger.SendInternal(i);
return i;
}
}
throw new Exception("Failed to find an unallocated FAT entry.");
}
///
/// Clears a FAT entry.
///
/// The entry number.
/// Thrown when FAT type is unknown.
/// Thrown when data lenght is greater then Int32.MaxValue.
/// Thrown when data size invalid.
/// Thrown when FAT sector data is null.
public void ClearFatEntry(ulong aEntryNumber)
{
SetFatEntry(aEntryNumber, 0);
}
///
/// Set FAT entry.
///
/// A data array to be set.
/// A entry number to set.
/// A value to set.
/// Thrown when FAT type is unknown.
private void SetFatEntry(byte[] aData, ulong aEntryNumber, ulong aValue)
{
uint xEntrySize = GetFatEntrySizeInBytes();
ulong xEntryOffset = aEntryNumber * xEntrySize;
switch (mFileSystem.mFatType)
{
case FatTypeEnum.Fat12:
aData.SetUInt16(xEntryOffset, (ushort)aValue);
break;
case FatTypeEnum.Fat16:
aData.SetUInt16(xEntryOffset, (ushort)aValue);
break;
case FatTypeEnum.Fat32:
aData.SetUInt32(xEntryOffset, (uint)aValue);
break;
default:
throw new NotSupportedException("Unknown FAT type.");
}
}
///
/// Get FAT entry.
///
/// A data array to read from.
/// A entry number to get.
/// Output the data to aValue.
/// Thrown when FAT type is unknown.
/// Thrown when aEntryNumber invalid.
/// Thrown when aData is null.
/// Thrown when aEntryNumber invalid.
private void GetFatEntry(byte[] aData, uint aEntryNumber, out uint aValue)
{
uint xEntrySize = GetFatEntrySizeInBytes();
ulong xEntryOffset = aEntryNumber * xEntrySize;
switch (mFileSystem.mFatType)
{
case FatTypeEnum.Fat12:
// 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
// we want the high 12-bits of the 16-bits we fetch.
uint xResult = BitConverter.ToUInt16(aData, (int)xEntryOffset);
if ((aEntryNumber & 0x01) == 0)
{
aValue = xResult & 0x0FFF; // Even
}
else
{
aValue = xResult >> 4; // Odd
}
break;
case FatTypeEnum.Fat16:
aValue = BitConverter.ToUInt16(aData, (int)xEntryOffset);
break;
case FatTypeEnum.Fat32:
aValue = BitConverter.ToUInt32(aData, (int)xEntryOffset) & 0x0FFFFFFF;
break;
default:
throw new NotSupportedException("Unknown FAT type.");
}
}
///
/// Clears all the FAT sectors.
///
/// Thrown when data lenght is greater then Int32.MaxValue.
/// Thrown when data size invalid / Unknown file system type.
/// Thrown when FAT type is unknown.
///
///
/// - Thrown when entrys aData is null.
/// - Out of memory.
///
///
/// Thrown on fatal error (contact support).
/// Thrown on fatal error (contact support).
/// Thrown when the data in aData is corrupted.
///
///
/// - Thrown when the data length is 0 or greater then Int32.MaxValue.
/// - Entrys matadata offset value is invalid.
///
///
///
///
/// - Data length is 0.
///
///
public void ClearAllFat()
{
//byte[] xFatTable = new byte[4096]; // TODO find where '4096' is defined
byte[] xFatTable = mFileSystem.NewBlockArray();
//var xFatTableSize = mFileSystem.FatSectorCount * mFileSystem.BytesPerSector / GetFatEntrySizeInBytes();
Global.mFileSystemDebugger.SendInternal($"FatSector is {mFatSector}");
Global.mFileSystemDebugger.SendInternal($"RootCluster is {mFileSystem.RootCluster}");
Global.mFileSystemDebugger.SendInternal("Clearing all Fat Table");
byte[] xFatTableFistSector;
ReadFatSector(0, out xFatTableFistSector);
/* Change 3rd entry (RootDirectory) to be EOC */
SetFatEntry(xFatTableFistSector, 2, FatEntryEofValue());
/* Copy first three elements on xFatTable */
Array.Copy(xFatTableFistSector, xFatTable, 12);
Global.mFileSystemDebugger.SendInternal($"Clearing First sector...");
/* The rest of 'xFatTable' should be all 0s as new does this internally */
WriteFatSector(0, xFatTable);
Global.mFileSystemDebugger.SendInternal($"First sector cleared");
/* Restore the Array will all 0s as it is this we have to write in the other sectors */
//Array.Clear(xFatTable, 0, 12);
/* Array.Clear() not work: stack overflow! */
for (int i = 0; i < 11; i++)
{
xFatTable[i] = 0;
}
for (ulong sector = 1; sector < mFileSystem.FatSectorCount; sector++)
{
if (sector % 100 == 0)
{
Global.mFileSystemDebugger.SendInternal($"Clearing sector {sector}");
}
WriteFatSector(sector, xFatTable);
}
}
///
/// Read FAT sector.
///
/// A sector to read from.
/// Output data byte.
/// Thrown when data lenght is greater then Int32.MaxValue.
/// Thrown when data size invalid.
private void ReadFatSector(ulong aSector, out byte[] aData)
{
aData = mFileSystem.NewBlockArray();
ulong xSector = mFatSector + aSector;
Global.mFileSystemDebugger.SendInternal("xSector =");
Global.mFileSystemDebugger.SendInternal(xSector);
mFileSystem.Device.ReadBlock(xSector, mFileSystem.SectorsPerCluster, ref aData);
}
///
/// Write FAT sector.
///
/// A sector to write to.
/// A data to write.
/// Thrown when data lenght is greater then Int32.MaxValue.
/// Thrown when data size invalid.
/// Thrown when aData is null.
private void WriteFatSector(ulong aSector, byte[] aData)
{
if (aData == null)
{
throw new ArgumentNullException(nameof(aData));
}
var xSector = mFatSector + aSector;
mFileSystem.Device.WriteBlock(xSector, mFileSystem.SectorsPerCluster, ref aData);
}
///
/// Gets a FAT entry.
///
/// The entry number.
/// The entry value.
/// Thrown when data lenght is greater then Int32.MaxValue.
/// Thrown when data size invalid.
/// Thrown when bad aEntryNumber passed.
/// Thrown on fatal error (contact support).
/// Thrown when bad aEntryNumber passed.
/// Thrown when FAT type is unknown.
internal void GetFatEntry(uint aEntryNumber, out uint aValue)
{
Global.mFileSystemDebugger.SendInternal("-- Fat.GetFatEntry --");
Global.mFileSystemDebugger.SendInternal("aEntryNumber =");
Global.mFileSystemDebugger.SendInternal(aEntryNumber);
uint xEntrySize = GetFatEntrySizeInBytes();
ulong xEntryOffset = aEntryNumber * xEntrySize;
Global.mFileSystemDebugger.SendInternal("xEntrySize =");
Global.mFileSystemDebugger.SendInternal(xEntrySize);
Global.mFileSystemDebugger.SendInternal("xEntryOffset =");
Global.mFileSystemDebugger.SendInternal(xEntryOffset);
ulong xSector = xEntryOffset / mFileSystem.BytesPerSector;
Global.mFileSystemDebugger.SendInternal("xSector =");
Global.mFileSystemDebugger.SendInternal(xSector);
ReadFatSector(xSector, out byte[] xData);
switch (mFileSystem.mFatType)
{
case FatTypeEnum.Fat12:
// 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
// we want the high 12-bits of the 16-bits we fetch.
uint xResult = BitConverter.ToUInt16(xData, (int)xEntryOffset);
if ((aEntryNumber & 0x01) == 0)
{
aValue = xResult & 0x0FFF; // Even
}
else
{
aValue = xResult >> 4; // Odd
}
break;
case FatTypeEnum.Fat16:
aValue = BitConverter.ToUInt16(xData, (int)xEntryOffset);
break;
case FatTypeEnum.Fat32:
aValue = BitConverter.ToUInt32(xData, (int)xEntryOffset) & 0x0FFFFFFF;
break;
default:
throw new NotSupportedException("Unknown FAT type.");
}
Global.mFileSystemDebugger.SendInternal("aValue =");
Global.mFileSystemDebugger.SendInternal(aValue);
}
///
/// Sets a FAT entry.
///
/// The entry number.
/// The value.
/// Thrown when FAT type is unknown.
/// Thrown when data lenght is greater then Int32.MaxValue.
/// Thrown when data size invalid.
/// Thrown when FAT sector data is null.
internal void SetFatEntry(ulong aEntryNumber, ulong aValue)
{
Global.mFileSystemDebugger.SendInternal("--- Fat.SetFatEntry ---");
Global.mFileSystemDebugger.SendInternal("aEntryNumber =");
Global.mFileSystemDebugger.SendInternal(aEntryNumber);
uint xEntrySize = GetFatEntrySizeInBytes();
ulong xEntryOffset = aEntryNumber * xEntrySize;
ulong xSector = xEntryOffset / mFileSystem.BytesPerSector;
ulong xSectorOffset = (xSector * mFileSystem.BytesPerSector) - xEntryOffset;
byte[] xData;
ReadFatSector(xSector, out xData);
switch (mFileSystem.mFatType)
{
case FatTypeEnum.Fat12:
xData.SetUInt16(xEntryOffset, (ushort)aValue);
break;
case FatTypeEnum.Fat16:
xData.SetUInt16(xEntryOffset, (ushort)aValue);
break;
case FatTypeEnum.Fat32:
xData.SetUInt32(xEntryOffset, (uint)aValue);
break;
default:
throw new NotSupportedException("Unknown FAT type.");
}
WriteFatSector(xSector, xData);
}
///
/// Check if FAT entry is free.
///
/// A entry to check.
/// bool value.
internal bool FatEntryIsFree(uint aValue)
{
return aValue == 0;
}
///
/// Check if EOF to FAT entry.
///
/// A value to check if is EOF.
/// bool value.
/// Thrown when FAT type is unknown.
internal bool FatEntryIsEof(uint aValue)
{
switch (mFileSystem.mFatType)
{
case FatTypeEnum.Fat12:
return (aValue & 0x0FFF) >= 0x0FF8;
case FatTypeEnum.Fat16:
return (aValue & 0xFFFF) >= 0xFFF8;
case FatTypeEnum.Fat32:
return (aValue & 0x0FFFFFF8) >= 0x0FFFFFF8;
default:
throw new ArgumentException("Unknown FAT type");
}
}
///
/// Check if FAT entry is bad.
///
/// A value to check0
/// bool value.
/// Thrown when FAT type is unknown.
internal bool FatEntryIsBad(uint aValue)
{
switch (mFileSystem.mFatType)
{
case FatTypeEnum.Fat12:
return (aValue & 0x0FFF) == 0x0FF7;
case FatTypeEnum.Fat16:
return (aValue & 0xFFFF) == 0xFFF7;
case FatTypeEnum.Fat32:
return (aValue & 0x0FFFFFF8) == 0x0FFFFFF7;
default:
throw new ArgumentException("Unknown FAT type");
}
}
///
/// The the EOF value for a specific FAT type.
///
/// The EOF value.
/// Unknown file system type.
internal ulong FatEntryEofValue()
{
switch (mFileSystem.mFatType)
{
case FatTypeEnum.Fat12:
return 0x0FFF;
case FatTypeEnum.Fat16:
return 0xFFFF;
case FatTypeEnum.Fat32:
return 0x0FFFFFFF;
default:
throw new Exception("Unknown file system type.");
}
}
}
///
/// Number of bytes per cluster.
///
public readonly uint BytesPerCluster;
///
/// Number of bytes per sector.
///
public readonly uint BytesPerSector;
///
/// Number of clusters.
///
public readonly uint ClusterCount;
///
/// First data sector.
///
public readonly uint DataSector; // First Data Sector
///
/// Number of data sectors.
///
public readonly uint DataSectorCount;
///
/// Number of FAT sectors.
///
public readonly uint FatSectorCount;
///
/// FAT type.
///
/// possible types:
///
/// - Unknown.
/// - Fat12.
/// - Fat16.
/// - Fat32.
///
///
///
private readonly FatTypeEnum mFatType;
///
/// Nuber of FATs in the filesystem.
///
public readonly uint NumberOfFATs;
///
/// Number of reserved sectors.
///
public readonly uint ReservedSectorCount;
///
/// FAT32 root cluster.
///
public readonly uint RootCluster; // FAT32
///
/// Number of root entrys.
///
public readonly uint RootEntryCount;
///
/// FAT12/16 root sector.
///
public readonly uint RootSector; // FAT12/16
///
/// Number of root sectors.
///
/// For FAT12/16. In FAT32 this field remains 0.
///
///
public readonly uint RootSectorCount; // FAT12/16, FAT32 remains 0
///
/// Number of sectors per cluster.
///
public readonly uint SectorsPerCluster;
///
/// Total number of sectors.
///
public readonly uint TotalSectorCount;
///
/// FATs array.
///
private readonly Fat[] mFats;
///
/// Get FAT type.
///
/// possible types:
///
/// - Unknown.
/// - Fat12.
/// - Fat16.
/// - Fat32.
///
///
///
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.");
}
}
}
///
/// Initializes a new instance of the class.
///
/// The partition.
/// The root path.
/// The partition size.
///
///
/// - Thrown when aDevice is null.
/// - Thrown when FatFileSystem is null.
/// - Thrown on fatal error (contact support).
///
///
///
///
/// - Thrown when aRootPath is null.
/// - Thrown on fatal error (contact support).
///
///
/// Thrown on fatal error (contact support).
///
///
/// - Thrown on fatal error (contact support).
/// - >FAT signature not found.
///
///
/// Thrown on fatal error (contact support).
public FatFileSystem(Partition aDevice, string aRootPath, long aSize)
: base(aDevice, aRootPath, aSize)
{
if (aDevice == null)
{
throw new ArgumentNullException(nameof(aDevice));
}
if (String.IsNullOrEmpty(aRootPath))
{
throw new ArgumentException("Argument is null or empty", nameof(aRootPath));
}
var xBPB = Device.NewBlockArray(1);
Device.ReadBlock(0UL, 1U, ref xBPB);
ushort xSig = BitConverter.ToUInt16(xBPB, 510);
if (xSig != 0xAA55)
{
throw new Exception("FAT signature not found.");
}
BytesPerSector = BitConverter.ToUInt16(xBPB, 11);
SectorsPerCluster = xBPB[13];
BytesPerCluster = BytesPerSector * SectorsPerCluster;
ReservedSectorCount = BitConverter.ToUInt16(xBPB, 14);
NumberOfFATs = xBPB[16];
RootEntryCount = BitConverter.ToUInt16(xBPB, 17);
TotalSectorCount = BitConverter.ToUInt16(xBPB, 19);
if (TotalSectorCount == 0)
{
TotalSectorCount = BitConverter.ToUInt32(xBPB, 32);
}
// FATSz
FatSectorCount = BitConverter.ToUInt16(xBPB, 22);
if (FatSectorCount == 0)
{
FatSectorCount = BitConverter.ToUInt32(xBPB, 36);
}
DataSectorCount = TotalSectorCount -
(ReservedSectorCount + NumberOfFATs * FatSectorCount + ReservedSectorCount);
// Computation rounds down.
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)
{
mFatType = FatTypeEnum.Fat12;
}
else if (ClusterCount < 65525)
{
mFatType = FatTypeEnum.Fat16;
}
else
{
mFatType = FatTypeEnum.Fat32;
}
if (mFatType == FatTypeEnum.Fat32)
{
RootCluster = BitConverter.ToUInt32(xBPB, 44);
}
else
{
RootSector = ReservedSectorCount + NumberOfFATs * FatSectorCount;
RootSectorCount = (RootEntryCount * 32 + (BytesPerSector - 1)) / BytesPerSector;
}
DataSector = ReservedSectorCount + NumberOfFATs * FatSectorCount + RootSectorCount;
mFats = new Fat[NumberOfFATs];
for (ulong i = 0; i < NumberOfFATs; i++)
{
mFats[i] = new Fat(this, (ReservedSectorCount + i * FatSectorCount));
}
}
///
/// Get FAT.
///
/// A table number to get.
/// FAT type.
/// Thrown when FAT table number doesn't exist.
internal Fat GetFat(int aTableNumber)
{
if (mFats.Length > aTableNumber)
{
return mFats[aTableNumber];
}
throw new Exception("The fat table number doesn't exist.");
}
///
/// Create new block array.
///
/// Byte array.
internal byte[] NewBlockArray()
{
return new byte[BytesPerCluster];
}
///
/// Read data from cluster.
///
/// A cluster to read from.
/// A data array to write the output to.
/// prob. unused.
/// unused.
/// Thrown when data lenght is greater then Int32.MaxValue.
/// Thrown when data size invalid.
internal void Read(long aCluster, out byte[] aData, long aSize = 0, long aOffset = 0)
{
Global.mFileSystemDebugger.SendInternal("-- FatFileSystem.Read --");
if (aSize == 0)
{
aSize = BytesPerCluster;
}
if (mFatType == FatTypeEnum.Fat32)
{
aData = NewBlockArray();
long xSector = DataSector + (aCluster - RootCluster) * SectorsPerCluster;
Device.ReadBlock((ulong)xSector, SectorsPerCluster, ref aData);
}
else
{
aData = Device.NewBlockArray(1);
Device.ReadBlock((ulong)aCluster, RootSectorCount, ref aData);
}
}
///
/// Write data to cluster.
///
/// A cluster to write to.
/// A data to write.
/// prob. unused.
/// The offset to write from.
/// Thrown when data lenght is greater then Int32.MaxValue.
/// Thrown when data size invalid.
///
///
/// - Thrown when entrys aData is null.
/// - Out of memory.
///
///
/// Thrown on fatal error (contact support).
/// Thrown on fatal error (contact support).
/// Thrown when the data in aData is corrupted.
///
///
/// - Thrown when the data length is 0 or greater then Int32.MaxValue.
/// - Entrys matadata offset value is invalid.
///
///
///
///
/// - aData length is 0.
///
///
internal void Write(long aCluster, byte[] aData, long aSize = 0, long aOffset = 0)
{
Global.mFileSystemDebugger.SendInternal("-- FatFileSystem.Write --");
if (aData == null)
{
throw new ArgumentNullException(nameof(aData));
}
if (aSize == 0)
{
aSize = BytesPerCluster;
}
byte[] xData;
Read(aCluster, out xData);
Array.Copy(aData, 0, xData, aOffset, aData.Length);
if (mFatType == FatTypeEnum.Fat32)
{
long xSector = DataSector + (aCluster - RootCluster) * SectorsPerCluster;
Device.WriteBlock((ulong)xSector, SectorsPerCluster, ref xData);
}
else
{
Device.WriteBlock((ulong)aCluster, RootSectorCount, ref xData);
}
}
///
/// Print filesystem info.
///
/// Thrown on I/O error.
public override void DisplayFileSystemInfo()
{
global::System.Console.WriteLine("-------File System--------");
global::System.Console.WriteLine("Bytes per Cluster = " + BytesPerCluster);
global::System.Console.WriteLine("Bytes per Sector = " + BytesPerSector);
global::System.Console.WriteLine("Cluster Count = " + ClusterCount);
global::System.Console.WriteLine("Data Sector = " + DataSector);
global::System.Console.WriteLine("Data Sector Count = " + DataSectorCount);
global::System.Console.WriteLine("FAT Sector Count = " + FatSectorCount);
global::System.Console.WriteLine("FAT Type = " + (uint)mFatType);
global::System.Console.WriteLine("Number of FATS = " + NumberOfFATs);
global::System.Console.WriteLine("Reserved Sector Count = " + ReservedSectorCount);
global::System.Console.WriteLine("Root Cluster = " + RootCluster);
global::System.Console.WriteLine("Root Entry Count = " + RootEntryCount);
global::System.Console.WriteLine("Root Sector = " + RootSector);
global::System.Console.WriteLine("Root Sector Count = " + RootSectorCount);
global::System.Console.WriteLine("Sectors per Cluster = " + SectorsPerCluster);
global::System.Console.WriteLine("Total Sector Count = " + TotalSectorCount);
Global.mFileSystemDebugger.SendInternal("Bytes per Cluster =");
Global.mFileSystemDebugger.SendInternal(BytesPerCluster);
Global.mFileSystemDebugger.SendInternal("Bytes per Sector =");
Global.mFileSystemDebugger.SendInternal(BytesPerSector);
Global.mFileSystemDebugger.SendInternal("Cluster Count =");
Global.mFileSystemDebugger.SendInternal(ClusterCount);
Global.mFileSystemDebugger.SendInternal("Data Sector =");
Global.mFileSystemDebugger.SendInternal(DataSector);
Global.mFileSystemDebugger.SendInternal("Data Sector Count =");
Global.mFileSystemDebugger.SendInternal(DataSectorCount);
Global.mFileSystemDebugger.SendInternal("FAT Sector Count =");
Global.mFileSystemDebugger.SendInternal(FatSectorCount);
Global.mFileSystemDebugger.SendInternal("FAT Type =");
Global.mFileSystemDebugger.SendInternal((uint)mFatType);
Global.mFileSystemDebugger.SendInternal("Number of FATS =");
Global.mFileSystemDebugger.SendInternal(NumberOfFATs);
Global.mFileSystemDebugger.SendInternal("Reserved Sector Count =");
Global.mFileSystemDebugger.SendInternal(ReservedSectorCount);
Global.mFileSystemDebugger.SendInternal("Root Cluster =");
Global.mFileSystemDebugger.SendInternal(RootCluster);
Global.mFileSystemDebugger.SendInternal("Root Entry Count =");
Global.mFileSystemDebugger.SendInternal(RootEntryCount);
Global.mFileSystemDebugger.SendInternal("Root Sector =");
Global.mFileSystemDebugger.SendInternal(RootSector);
Global.mFileSystemDebugger.SendInternal("Root Sector Count =");
Global.mFileSystemDebugger.SendInternal(RootSectorCount);
Global.mFileSystemDebugger.SendInternal("Sectors per Cluster =");
Global.mFileSystemDebugger.SendInternal(SectorsPerCluster);
Global.mFileSystemDebugger.SendInternal("Total Sector Count =");
Global.mFileSystemDebugger.SendInternal(TotalSectorCount);
}
///
/// Get list of sub-directories in a directory.
///
/// A base directory.
/// DirectoryEntry list.
/// Thrown when baseDirectory is null / memory error.
/// Thrown when data lenght is greater then Int32.MaxValue.
/// Thrown when data size invalid / invalid directory entry type.
/// Thrown on memory error.
/// Thrown on memory error.
/// Thrown on memory error.
public override List GetDirectoryListing(DirectoryEntry baseDirectory)
{
Global.mFileSystemDebugger.SendInternal("-- FatFileSystem.GetDirectoryListing --");
if (baseDirectory == null)
{
throw new ArgumentNullException(nameof(baseDirectory));
}
var result = new List();
var xEntry = (FatDirectoryEntry)baseDirectory;
var fatListing = xEntry.ReadDirectoryContents();
for (int i = 0; i < fatListing.Count; i++)
{
result.Add(fatListing[i]);
}
return result;
}
///
/// Get root directory.
///
/// DirectoryEntry value.
/// Thrown when root directory address is smaller then root directory address.
/// Thrown when filesystem is null.
/// Thrown when root path is null or empty.
public override DirectoryEntry GetRootDirectory()
{
Global.mFileSystemDebugger.SendInternal("-- FatFileSystem.GetRootDirectory --");
var xRootEntry = new FatDirectoryEntry(this, null, RootPath, Size, RootPath, RootCluster);
return xRootEntry;
}
///
/// Create directory.
///
/// A parent directory.
/// A new directory name.
/// DirectoryEntry value.
///
///
/// - Thrown when aParentDirectory is null.
/// - aNewDirectory is null or empty.
/// - memory error.
///
///
/// Thrown on memory error / unknown directory entry type.
/// Thrown when data lenght is greater then Int32.MaxValue.
/// Thrown when data size invalid / invalid directory entry type / memory error.
/// Thrown on memory error.
/// Thrown on memory error.
/// Thrown on fatal error (contact support).
/// Thrown on fatal error (contact support).
/// Thrown on memory error.
public override DirectoryEntry CreateDirectory(DirectoryEntry aParentDirectory, string aNewDirectory)
{
Global.mFileSystemDebugger.SendInternal("-- FatFileSystem.CreateDirectory --");
Global.mFileSystemDebugger.SendInternal("aParentDirectory.Name = " + aParentDirectory?.mName);
Global.mFileSystemDebugger.SendInternal("aNewDirectory = " + aNewDirectory);
if (aParentDirectory == null)
{
throw new ArgumentNullException(nameof(aParentDirectory));
}
if (string.IsNullOrEmpty(aNewDirectory))
{
throw new ArgumentNullException(nameof(aNewDirectory));
}
var xParentDirectory = (FatDirectoryEntry)aParentDirectory;
var xDirectoryEntryToAdd = xParentDirectory.AddDirectoryEntry(aNewDirectory, DirectoryEntryTypeEnum.Directory);
return xDirectoryEntryToAdd;
}
///
/// Create file.
///
/// A parent directory.
/// A new file name.
/// DirectoryEntry value.
///
///
/// - Thrown when aParentDirectory is null.
/// - aNewFile is null or empty.
/// - memory error.
///
///
/// Thrown on memory error / unknown directory entry type.
/// Thrown when data lenght is greater then Int32.MaxValue.
/// Thrown when data size invalid / invalid directory entry type / memory error.
/// Thrown on memory error.
/// Thrown on memory error.
/// Thrown on fatal error (contact support).
/// Thrown on fatal error (contact support).
/// Thrown on memory error.
public override DirectoryEntry CreateFile(DirectoryEntry aParentDirectory, string aNewFile)
{
Global.mFileSystemDebugger.SendInternal("-- FatFileSystem.CreateFile --");
Global.mFileSystemDebugger.SendInternal("aParentDirectory.Name = " + aParentDirectory?.mName);
Global.mFileSystemDebugger.SendInternal("aNewFile =" + aNewFile);
if (aParentDirectory == null)
{
throw new ArgumentNullException(nameof(aParentDirectory));
}
if (string.IsNullOrEmpty(aNewFile))
{
throw new ArgumentNullException(nameof(aNewFile));
}
var xParentDirectory = (FatDirectoryEntry)aParentDirectory;
var xDirectoryEntryToAdd = xParentDirectory.AddDirectoryEntry(aNewFile, DirectoryEntryTypeEnum.File);
return xDirectoryEntryToAdd;
}
///
/// Delete directory.
///
/// A directory entry to delete.
/// Thrown when given entry type is unknown.
///
///
/// - Thrown when tring to delete root directory.
/// - directory entry type is invalid.
/// - data size invalid.
/// - FAT table not found.
/// - out of memory.
///
///
/// Thrown when data lenght is greater then Int32.MaxValue.
///
///
/// - Thrown when aDirectoryEntry is null.
/// - aData is null.
/// - Out of memory.
///
///
/// Thrown on fatal error (contact support).
/// Thrown on fatal error (contact support).
/// Thrown when the data in aData is corrupted.
///
///
/// - Thrown when the data length is 0 or greater then Int32.MaxValue.
/// - Entrys matadata offset value is invalid.
///
///
///
///
/// - aData length is 0.
///
///
/// Thrown when FAT type is unknown.
public override void DeleteDirectory(DirectoryEntry aDirectoryEntry)
{
if (aDirectoryEntry == null)
{
throw new ArgumentNullException(nameof(aDirectoryEntry));
}
var xDirectoryEntry = (FatDirectoryEntry)aDirectoryEntry;
xDirectoryEntry.DeleteDirectoryEntry();
}
///
/// Delete file.
///
/// A directory entry to delete.
/// Thrown when given entry type is unknown.
///
///
/// - Thrown when tring to delete root directory.
/// - directory entry type is invalid.
/// - data size invalid.
/// - FAT table not found.
/// - out of memory.
///
///
///
///
/// - Thrown when data lenght is greater then Int32.MaxValue.
/// - The number of clusters in the FAT entry is greater than Int32.MaxValue.
///
///
///
///
/// - Thrown when aDirectoryEntry is null.
/// - aData is null.
/// - Out of memory.
///
///
/// Thrown on fatal error (contact support).
/// Thrown on fatal error (contact support).
/// Thrown when the data in aData is corrupted.
///
///
/// - Thrown when the data length is 0 or greater then Int32.MaxValue.
/// - The size of the chain is less then zero.
/// - Entrys matadata offset value is invalid.
///
///
///
///
/// - Thrown when FAT type is unknown.
/// - aData length is 0.
///
///
/// Thrown when FAT type is unknown.
public override void DeleteFile(DirectoryEntry aDirectoryEntry)
{
if (aDirectoryEntry == null)
{
throw new ArgumentNullException(nameof(aDirectoryEntry));
}
var xDirectoryEntry = (FatDirectoryEntry)aDirectoryEntry;
var entries = xDirectoryEntry.GetFatTable();
foreach (var entry in entries)
{
GetFat(0).ClearFatEntry(entry);
}
xDirectoryEntry.DeleteDirectoryEntry();
}
///
/// Get and set the root directory label.
///
/// Thrown when data lenght is greater then Int32.MaxValue.
///
///
/// - (get) Thrown when root path is null or empty.
/// - (set) Thrown when label is null or empty string.
/// - (set) aData length is 0.
/// - (get / set) memory error.
///
///
///
///
/// - (get) Thrown when trying to access VolumeId out of Root Directory.
/// - (set) Thrown when entry metadata could not be changed.
/// - (get / set) Invalid entry type.
/// - (get / set) Invalid entry data size.
/// - (get / set) Invalid directory entry type.
///
///
///
///
/// - (get) Thrown when filesystem is null.
/// - (set) Thrown when entrys aValue is null.
/// - (set) Thrown when entrys aData is null.
/// - (get / set) Out of memory.
///
///
/// Thrown when encoder fallback operation on aValue fails / memory error.
/// Thrown on fatal error (contact support).
/// Thrown on fatal error (contact support).
/// Thrown when the data in aValue is corrupted.
///
///
/// - (get) Thrown when root directory address is smaller then root directory address.
/// - (get) memory error.
/// - (set) Thrown when the data length is 0 or greater then Int32.MaxValue.
/// - (set) Entrys matadata offset value is invalid.
///
///
public override string Label
{
/*
* In the FAT filesystem the name field of RootDirectory is - in reality - the Volume Label
*/
get
{
Global.mFileSystemDebugger.SendInternal("-- FatFileSystem.mLabel --");
var RootDirectory = (FatDirectoryEntry)GetRootDirectory();
var VolumeId = RootDirectory.FindVolumeId();
if (VolumeId == null)
{
Global.mFileSystemDebugger.SendInternal("No VolumeID, returning drive name");
return RootDirectory.mName;
}
Global.mFileSystemDebugger.SendInternal($"Volume label is |{VolumeId.mName.TrimEnd()}|");
return VolumeId.mName.TrimEnd();
}
set
{
Global.mFileSystemDebugger.SendInternal($"FatFileSystem - Setting Volume label to |{value}|");
var RootDirectory = (FatDirectoryEntry)GetRootDirectory();
var VolumeId = RootDirectory.FindVolumeId();
if (VolumeId != null)
{
VolumeId.SetName(value);
return;
}
Global.mFileSystemDebugger.SendInternal("No VolumeID found, let's create it!");
VolumeId = RootDirectory.CreateVolumeId(value);
}
}
///
/// Get size of free space available.
///
/// Thrown on fatal error (contact support).
/// Thrown when filesystem is null.
///
///
/// - Thrown when root path is null or empty.
/// - root directory entry data corrupted.
///
///
/// Thrown when data lenght is greater then Int32.MaxValue.
/// Thrown when data size invalid.
public override long AvailableFreeSpace
{
get
{
var RootDirectory = (FatDirectoryEntry)GetRootDirectory();
// We do not support "user quotas" for now so this is effectively the same then mTotalFreeSpace
/* mSize is expressed in MegaByte */
var TotalSizeInBytes = Size * 1024 * 1024;
var UsedSpace = RootDirectory.GetUsedSpace();
Global.mFileSystemDebugger.SendInternal($"TotalSizeInBytes {TotalSizeInBytes} UsedSpace {UsedSpace}");
return TotalSizeInBytes - UsedSpace;
//return (mSize * 1024 * 1024) - RootDirectory.GetUsedSpace();
}
}
///
/// Get total free space.
///
/// Thrown on fatal error (contact support).
/// Thrown when filesystem is null.
///
///
/// - Thrown when root path is null or empty.
/// - root directory entry data corrupted.
///
///
/// Thrown when data lenght is greater then Int32.MaxValue.
/// Thrown when data size invalid.
public override long TotalFreeSpace
{
get
{
var RootDirectory = (FatDirectoryEntry)GetRootDirectory();
/* mSize is expressed in MegaByte */
var TotalSizeInBytes = Size * 1024 * 1024;
var UsedSpace = RootDirectory.GetUsedSpace();
Global.mFileSystemDebugger.SendInternal($"TotalSizeInBytes {TotalSizeInBytes} UsedSpace {UsedSpace}");
return TotalSizeInBytes - UsedSpace;
//return (mSize * 1024 * 1024) - RootDirectory.GetUsedSpace();
}
}
///
/// FAT types.
///
internal enum FatTypeEnum
{
Unknown,
Fat12,
Fat16,
Fat32
}
///
/// Format drive. (delete all)
///
/// unused.
/// unused.
///
///
/// - Thrown when the data length is 0 or greater then Int32.MaxValue.
/// - Entrys matadata offset value is invalid.
/// - Fatal error (contact support).
///
///
/// Thrown when filesystem is null / memory error.
///
///
/// - Data length is 0.
/// - Root path is null or empty.
/// - Memory error.
///
///
///
///
/// - Thrown when data size invalid.
/// - Thrown on unknown file system type.
/// - Thrown on fatal error (contact support).
///
///
/// Thrown when data lenght is greater then Int32.MaxValue.
/// Thrown on memory error.
/// Thrown when FAT type is unknown.
/// Thrown on fatal error (contact support).
/// Thrown on fatal error (contact support).
/// Thrown when the data in aData is corrupted.
/// Thrown when FAT type is unknown.
public override void Format(string aDriveFormat, bool aQuick)
{
var xRootDirectory = (FatDirectoryEntry)GetRootDirectory();
var Fat = GetFat(0);
var x = xRootDirectory.ReadDirectoryContents();
foreach (var el in x)
{
Global.mFileSystemDebugger.SendInternal($"Found '{el.mName}' of type {(int)el.mEntryType}");
// Delete yourself!
el.DeleteDirectoryEntry();
}
Fat.ClearAllFat();
}
}
}