diff --git a/.idea/.idea.Aaru/.idea/vcs.xml b/.idea/.idea.Aaru/.idea/vcs.xml index de103424d..f133a60fb 100644 --- a/.idea/.idea.Aaru/.idea/vcs.xml +++ b/.idea/.idea.Aaru/.idea/vcs.xml @@ -7,6 +7,5 @@ - \ No newline at end of file diff --git a/Aaru.Filesystems/PFS/File.cs b/Aaru.Filesystems/PFS/File.cs index 4bc907df2..07c1f1b2a 100644 --- a/Aaru.Filesystems/PFS/File.cs +++ b/Aaru.Filesystems/PFS/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; using Aaru.Logging; @@ -38,6 +39,160 @@ namespace Aaru.Filesystems; /// public sealed partial class PFS { + /// + /// + public ErrorNumber OpenFile(string path, out IFileNode node) + { + node = null; + + if(!_mounted) return ErrorNumber.AccessDenied; + + AaruLogging.Debug(MODULE_NAME, "OpenFile: path='{0}'", path); + + // Find the file entry + ErrorNumber errno = GetEntryForPath(path, out DirEntryCacheItem entry); + + if(errno != ErrorNumber.NoError) return errno; + + // Check if it's a file + if(entry.Type == EntryType.Directory || entry.Type == EntryType.HardLinkDir) + { + AaruLogging.Debug(MODULE_NAME, "OpenFile: '{0}' is a directory", path); + + return ErrorNumber.IsDirectory; + } + + // Get the file's starting anode + errno = GetAnode(entry.Anode, out Anode fileAnode); + + if(errno != ErrorNumber.NoError) + { + AaruLogging.Debug(MODULE_NAME, "OpenFile: Error getting anode {0}: {1}", entry.Anode, errno); + + return errno; + } + + // Create file node + node = new PFSFileNode + { + Path = path, + Length = entry.Size, + Offset = 0, + StartAnode = entry.Anode, + CurrentAnode = fileAnode, + AnodeOffset = 0, + BlockOffset = 0, + FileSize = entry.Size + }; + + AaruLogging.Debug(MODULE_NAME, + "OpenFile: Opened file '{0}', size={1}, anode={2}", + path, + entry.Size, + entry.Anode); + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber CloseFile(IFileNode node) + { + if(!_mounted) return ErrorNumber.AccessDenied; + + if(node is not PFSFileNode) return ErrorNumber.InvalidArgument; + + 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 PFSFileNode pfsNode) return ErrorNumber.InvalidArgument; + + // Can't read past end of file + if(pfsNode.Offset >= pfsNode.Length) return ErrorNumber.NoError; + + // Limit read to remaining file size + if(length > pfsNode.Length - pfsNode.Offset) length = pfsNode.Length - pfsNode.Offset; + + var bufferOffset = 0; + long bytesRemaining = length; + + while(bytesRemaining > 0) + { + // Calculate current block number within the filesystem + uint currentBlock = pfsNode.CurrentAnode.blocknr + pfsNode.AnodeOffset; + + // Read the current data block + ErrorNumber errno = ReadBlock(currentBlock, out byte[] blockData); + + if(errno != ErrorNumber.NoError) + { + AaruLogging.Debug(MODULE_NAME, "ReadFile: Error reading block {0}: {1}", currentBlock, errno); + + return errno; + } + + // Calculate how much data to read from this block + var dataInBlock = (int)(_blockSize - pfsNode.BlockOffset); + + if(dataInBlock > bytesRemaining) dataInBlock = (int)bytesRemaining; + + // Copy data to buffer + Array.Copy(blockData, (int)pfsNode.BlockOffset, buffer, bufferOffset, dataInBlock); + + bufferOffset += dataInBlock; + bytesRemaining -= dataInBlock; + pfsNode.Offset += dataInBlock; + + // Update position within block + pfsNode.BlockOffset += (uint)dataInBlock; + + // If we've read past the end of this block, move to the next + if(pfsNode.BlockOffset >= _blockSize) + { + pfsNode.BlockOffset = 0; + pfsNode.AnodeOffset++; + + // Check if we need to move to the next anode in the chain + if(pfsNode.AnodeOffset >= pfsNode.CurrentAnode.clustersize) + { + // Move to next anode + if(pfsNode.CurrentAnode.next == ANODE_EOF) + { + // End of file + break; + } + + errno = GetAnode(pfsNode.CurrentAnode.next, out Anode nextAnode); + + if(errno != ErrorNumber.NoError) + { + AaruLogging.Debug(MODULE_NAME, + "ReadFile: Error getting next anode {0}: {1}", + pfsNode.CurrentAnode.next, + errno); + + break; + } + + pfsNode.CurrentAnode = nextAnode; + pfsNode.AnodeOffset = 0; + } + } + } + + read = bufferOffset; + + return ErrorNumber.NoError; + } + /// public ErrorNumber GetAttributes(string path, out FileAttributes attributes) { diff --git a/Aaru.Filesystems/PFS/Internal.cs b/Aaru.Filesystems/PFS/Internal.cs index dbd4c6f49..fa82a90af 100644 --- a/Aaru.Filesystems/PFS/Internal.cs +++ b/Aaru.Filesystems/PFS/Internal.cs @@ -73,5 +73,37 @@ public sealed partial class PFS public string Path { get; init; } } +#endregion + +#region Nested type: PFSFileNode + + /// File node for reading file contents + sealed class PFSFileNode : IFileNode + { + /// Starting anode number for the file + internal uint StartAnode { get; init; } + + /// Current anode in the chain + internal Anode CurrentAnode { get; set; } + + /// Offset within current anode's cluster (in blocks) + internal uint AnodeOffset { get; set; } + + /// Offset within current block (in bytes) + internal uint BlockOffset { get; set; } + + /// File size in bytes + internal uint FileSize { get; init; } + + /// + 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/PFS/Unimplemented.cs b/Aaru.Filesystems/PFS/Unimplemented.cs index fb2675476..5349bb668 100644 --- a/Aaru.Filesystems/PFS/Unimplemented.cs +++ b/Aaru.Filesystems/PFS/Unimplemented.cs @@ -28,7 +28,6 @@ using System; using Aaru.CommonTypes.Enums; -using Aaru.CommonTypes.Interfaces; namespace Aaru.Filesystems; @@ -37,18 +36,4 @@ public sealed partial class PFS { /// public ErrorNumber Unmount() => throw new NotImplementedException(); - - - /// - public ErrorNumber ReadLink(string path, out string dest) => 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