[exFAT] Implement file operations.

This commit is contained in:
2026-02-03 12:17:40 +00:00
parent 56f9cd914c
commit ff57c21ae4
4 changed files with 163 additions and 12 deletions

View File

@@ -95,4 +95,32 @@ public sealed partial class exFAT
return ms.ToArray();
}
/// <summary>Gets the cluster number at a specific position in the FAT chain.</summary>
/// <param name="firstCluster">First cluster of the chain.</param>
/// <param name="position">Position in the chain (0-based).</param>
/// <returns>Cluster number at the position, or 0 if invalid.</returns>
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;
}
}

View File

@@ -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;
/// <inheritdoc />
public sealed partial class exFAT
{
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
public ErrorNumber GetAttributes(string path, out CommonTypes.Structs.FileAttributes attributes)
{

View File

@@ -80,5 +80,27 @@ public sealed partial class exFAT
public string Path { get; init; }
}
#endregion
#region Nested type: ExFatFileNode
sealed class ExFatFileNode : IFileNode
{
/// <summary>First cluster of the file.</summary>
internal uint FirstCluster;
/// <summary>Whether the file allocation is contiguous (NoFatChain).</summary>
internal bool IsContiguous;
/// <inheritdoc />
public string Path { get; init; }
/// <inheritdoc />
public long Length { get; init; }
/// <inheritdoc />
public long Offset { get; set; }
}
#endregion
}

View File

@@ -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
{
/// <inheritdoc />
public ErrorNumber Unmount() => throw new NotImplementedException();
/// <inheritdoc />
public ErrorNumber OpenFile(string path, out IFileNode node) => throw new NotImplementedException();
/// <inheritdoc />
public ErrorNumber CloseFile(IFileNode node) => throw new NotImplementedException();
/// <inheritdoc />
public ErrorNumber ReadFile(IFileNode node, long length, byte[] buffer, out long read) =>
throw new NotImplementedException();
}