diff --git a/Aaru.Filesystems/SonyPFS/File.cs b/Aaru.Filesystems/SonyPFS/File.cs
index eaca64d60..670d53fb7 100644
--- a/Aaru.Filesystems/SonyPFS/File.cs
+++ b/Aaru.Filesystems/SonyPFS/File.cs
@@ -30,6 +30,8 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces;
using Aaru.CommonTypes.Structs;
@@ -219,6 +221,49 @@ public partial class SonyPFS
return ErrorNumber.NoError;
}
+ ///
+ public ErrorNumber ReadLink(string path, out string dest)
+ {
+ dest = null;
+
+ if(!_mounted) return ErrorNumber.AccessDenied;
+
+ ErrorNumber err = GetFileEntry(path, out DirEntry entry);
+
+ if(err != ErrorNumber.NoError) return err;
+
+ err = ReadInode(entry.Inode, entry.SubPart, out Inode inode);
+
+ if(err != ErrorNumber.NoError) return err;
+
+ if((inode.mode & (ushort)FileType.IFMT) != (ushort)FileType.IFLNK) return ErrorNumber.InvalidArgument;
+
+ // Symlink target is stored as raw bytes starting at data[1] in the inode
+ // data[0] is the inode's own block; data[1..] onward contains the path string
+ // Reinterpret data[1..] as bytes
+ int blockInfoSize = Marshal.SizeOf();
+ int maxStringBytes = (PFS_INODE_MAX_BLOCKS - 1) * blockInfoSize;
+ var linkBytes = new byte[maxStringBytes];
+
+ for(var i = 1; i < inode.data.Length; i++)
+ {
+ int offset = (i - 1) * blockInfoSize;
+
+ byte[] biBytes = Helpers.Marshal.StructureToByteArrayLittleEndian(inode.data[i]);
+
+ Array.Copy(biBytes, 0, linkBytes, offset, blockInfoSize);
+ }
+
+ // Find null terminator
+ int len = Array.IndexOf(linkBytes, (byte)0);
+
+ if(len < 0) len = maxStringBytes;
+
+ dest = Encoding.ASCII.GetString(linkBytes, 0, len);
+
+ return ErrorNumber.NoError;
+ }
+
/// Resolves a path to a DirEntry by walking the directory tree.
ErrorNumber GetFileEntry(string path, out DirEntry entry)
{
diff --git a/Aaru.Filesystems/SonyPFS/Unimplemented.cs b/Aaru.Filesystems/SonyPFS/Unimplemented.cs
deleted file mode 100644
index ab5731f4b..000000000
--- a/Aaru.Filesystems/SonyPFS/Unimplemented.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-// /***************************************************************************
-// Aaru Data Preservation Suite
-// ----------------------------------------------------------------------------
-//
-// Filename : Unimplemented.cs
-// Author(s) : Natalia Portillo
-//
-// Component : PlayStation FileSystem plugin.
-//
-// --[ License ] --------------------------------------------------------------
-//
-// This library is free software; you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as
-// published by the Free Software Foundation; either version 2.1 of the
-// License, or (at your option) any later version.
-//
-// This library is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public
-// License along with this library; if not, see .
-//
-// ----------------------------------------------------------------------------
-// Copyright © 2011-2026 Natalia Portillo
-// ****************************************************************************/
-
-using System;
-using Aaru.CommonTypes.Enums;
-
-namespace Aaru.Filesystems;
-
-public partial class SonyPFS
-{
- ///
- public ErrorNumber ReadLink(string path, out string dest) => throw new NotImplementedException();
-}
\ No newline at end of file