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