//#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(); mFileSystem.Write(xCurrentEntry, new byte[mFileSystem.BytesPerCluster]); 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 + 1; 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 a value in aData corresponding to the type of Fat Filesystem currently in use /// /// A entry number to set. /// A value to set. /// A data array in which the value should be set /// Thrown when FAT type is unknown. private void SetValueInFat(ulong aEntryNumber, ulong aValue, byte[] aData) { 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."); } } /// /// 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[] xFatTableFirstSector; ReadFatSector(0, out xFatTableFirstSector); /* Change 3rd entry (RootDirectory) to be EOC */ SetValueInFat(2, FatEntryEofValue(), xFatTableFirstSector); /* Copy first three elements on xFatTable */ Array.Copy(xFatTableFirstSector, 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 value in 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); } /// /// Sets an array of values in a FAT entry. /// /// The entry number. /// The value. /// The offset in the sector to write the value to /// The length of data to write /// 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, byte[] aData, uint aOffset, uint aLength) { 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(xSectorOffset, out xData); switch (mFileSystem.mFatType) { case FatTypeEnum.Fat12: case FatTypeEnum.Fat16: case FatTypeEnum.Fat32: Array.Copy(aData, 0, xData, aOffset, aLength); break; default: throw new NotSupportedException("Unknown FAT type."); } WriteFatSector(xSectorOffset, 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) { Global.mFileSystemDebugger.SendInternal("-- FatFileSystem.Read --"); if (mFatType == FatTypeEnum.Fat32) { aData = NewBlockArray(); long xSector = DataSector + (aCluster - RootCluster) * SectorsPerCluster; Global.mFileSystemDebugger.SendInternal("xSector: " + aCluster); Device.ReadBlock((ulong)xSector, SectorsPerCluster, ref aData); } else { Global.mFileSystemDebugger.SendInternal("aCluster: " + aCluster); 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, aSize); 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 entries of 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 --"); Global.mFileSystemDebugger.SendInternal("baseDirectory: " + baseDirectory.mFullPath); 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, RootPath, Size, 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(); } } }