From ff57c21ae4b48986264a4dcd3fceffc61418bc9e Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Tue, 3 Feb 2026 12:17:40 +0000 Subject: [PATCH] [exFAT] Implement file operations. --- Aaru.Filesystems/exFAT/Clusters.cs | 28 ++++++ Aaru.Filesystems/exFAT/File.cs | 113 ++++++++++++++++++++++++ Aaru.Filesystems/exFAT/Internal.cs | 22 +++++ Aaru.Filesystems/exFAT/Unimplemented.cs | 12 --- 4 files changed, 163 insertions(+), 12 deletions(-) diff --git a/Aaru.Filesystems/exFAT/Clusters.cs b/Aaru.Filesystems/exFAT/Clusters.cs index c8f32df1d..6ea68d28c 100644 --- a/Aaru.Filesystems/exFAT/Clusters.cs +++ b/Aaru.Filesystems/exFAT/Clusters.cs @@ -95,4 +95,32 @@ public sealed partial class exFAT return ms.ToArray(); } + + /// Gets the cluster number at a specific position in the FAT chain. + /// First cluster of the chain. + /// Position in the chain (0-based). + /// Cluster number at the position, or 0 if invalid. + uint GetClusterAtPosition(uint firstCluster, uint position) + { + if(firstCluster < 2 || firstCluster > _clusterCount + 1) return 0; + + uint currentCluster = firstCluster; + + for(uint i = 0; i < position; i++) + { + if(currentCluster < 2 || currentCluster > _clusterCount + 1) return 0; + + uint nextCluster = _fatEntries[currentCluster]; + + // End of chain markers + if(nextCluster >= 0xFFFFFFF8) return 0; + + // Bad cluster marker + if(nextCluster == 0xFFFFFFF7) return 0; + + currentCluster = nextCluster; + } + + return currentCluster; + } } \ No newline at end of file diff --git a/Aaru.Filesystems/exFAT/File.cs b/Aaru.Filesystems/exFAT/File.cs index 60cf348c9..b334f99ff 100644 --- a/Aaru.Filesystems/exFAT/File.cs +++ b/Aaru.Filesystems/exFAT/File.cs @@ -29,6 +29,7 @@ using System; using System.Collections.Generic; using Aaru.CommonTypes.Enums; +using Aaru.CommonTypes.Interfaces; using Aaru.CommonTypes.Structs; using Aaru.Helpers; @@ -38,6 +39,118 @@ namespace Aaru.Filesystems; /// public sealed partial class exFAT { + /// + public ErrorNumber OpenFile(string path, out IFileNode node) + { + node = null; + + if(!_mounted) return ErrorNumber.AccessDenied; + + ErrorNumber errno = GetFileEntry(path, out CompleteDirectoryEntry entry); + + if(errno != ErrorNumber.NoError) return errno; + + if(entry.IsDirectory) return ErrorNumber.IsDirectory; + + node = new ExFatFileNode + { + Path = path, + Length = (long)entry.DataLength, + Offset = 0, + FirstCluster = entry.FirstCluster, + IsContiguous = entry.IsContiguous + }; + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber CloseFile(IFileNode node) + { + if(!_mounted) return ErrorNumber.AccessDenied; + + if(node is not ExFatFileNode myNode) return ErrorNumber.InvalidArgument; + + myNode.Offset = -1; + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber ReadFile(IFileNode node, long length, byte[] buffer, out long read) + { + read = 0; + + if(!_mounted) return ErrorNumber.AccessDenied; + + if(buffer is null || buffer.Length < length) return ErrorNumber.InvalidArgument; + + if(node is not ExFatFileNode myNode) return ErrorNumber.InvalidArgument; + + if(myNode.Offset < 0) return ErrorNumber.InvalidArgument; + + if(length < 0) return ErrorNumber.InvalidArgument; + + // End of file reached + if(myNode.Offset >= myNode.Length) return ErrorNumber.NoError; + + // Adjust length if it would read past end of file + if(myNode.Offset + length > myNode.Length) length = myNode.Length - myNode.Offset; + + if(length == 0) return ErrorNumber.NoError; + + // Calculate which clusters we need to read + long firstCluster = myNode.Offset / _bytesPerCluster; + long offsetInCluster = myNode.Offset % _bytesPerCluster; + long bytesToRead = length; + long sizeInClusters = (bytesToRead + offsetInCluster + _bytesPerCluster - 1) / _bytesPerCluster; + + long bufferOffset = 0; + + for(long i = 0; i < sizeInClusters; i++) + { + // Get the cluster number for this position + uint clusterNumber; + + if(myNode.IsContiguous) + { + // For contiguous files, clusters are sequential starting from FirstCluster + clusterNumber = (uint)(myNode.FirstCluster + firstCluster + i); + } + else + { + // For non-contiguous files, follow the FAT chain + clusterNumber = GetClusterAtPosition(myNode.FirstCluster, (uint)(firstCluster + i)); + + if(clusterNumber < 2 || clusterNumber > _clusterCount + 1) return ErrorNumber.InvalidArgument; + } + + // Calculate sector for this cluster + ulong sector = _clusterHeapOffset + (ulong)(clusterNumber - 2) * _sectorsPerCluster; + + // Read the cluster + ErrorNumber errno = _image.ReadSectors(sector, false, _sectorsPerCluster, out byte[] clusterData, out _); + + if(errno != ErrorNumber.NoError) return errno; + + // Calculate how much to copy from this cluster + long clusterOffset = i == 0 ? offsetInCluster : 0; + long bytesFromThisCluster = _bytesPerCluster - clusterOffset; + + if(bytesFromThisCluster > bytesToRead - bufferOffset) bytesFromThisCluster = bytesToRead - bufferOffset; + + // Copy data to buffer + Array.Copy(clusterData, clusterOffset, buffer, bufferOffset, bytesFromThisCluster); + bufferOffset += bytesFromThisCluster; + } + + read = bufferOffset; + myNode.Offset += read; + + return ErrorNumber.NoError; + } + + /// public ErrorNumber GetAttributes(string path, out CommonTypes.Structs.FileAttributes attributes) { diff --git a/Aaru.Filesystems/exFAT/Internal.cs b/Aaru.Filesystems/exFAT/Internal.cs index d84279125..1a4c67ee3 100644 --- a/Aaru.Filesystems/exFAT/Internal.cs +++ b/Aaru.Filesystems/exFAT/Internal.cs @@ -80,5 +80,27 @@ public sealed partial class exFAT public string Path { get; init; } } +#endregion + +#region Nested type: ExFatFileNode + + sealed class ExFatFileNode : IFileNode + { + /// First cluster of the file. + internal uint FirstCluster; + + /// Whether the file allocation is contiguous (NoFatChain). + internal bool IsContiguous; + + /// + public string Path { get; init; } + + /// + public long Length { get; init; } + + /// + public long Offset { get; set; } + } + #endregion } \ No newline at end of file diff --git a/Aaru.Filesystems/exFAT/Unimplemented.cs b/Aaru.Filesystems/exFAT/Unimplemented.cs index e3aa71b8c..ffe0270ed 100644 --- a/Aaru.Filesystems/exFAT/Unimplemented.cs +++ b/Aaru.Filesystems/exFAT/Unimplemented.cs @@ -28,7 +28,6 @@ using System; using Aaru.CommonTypes.Enums; -using Aaru.CommonTypes.Interfaces; namespace Aaru.Filesystems; @@ -38,15 +37,4 @@ public sealed partial class exFAT { /// public ErrorNumber Unmount() => throw new NotImplementedException(); - - - /// - public ErrorNumber OpenFile(string path, out IFileNode node) => throw new NotImplementedException(); - - /// - public ErrorNumber CloseFile(IFileNode node) => throw new NotImplementedException(); - - /// - public ErrorNumber ReadFile(IFileNode node, long length, byte[] buffer, out long read) => - throw new NotImplementedException(); } \ No newline at end of file