diff --git a/build/CodeGen/results.csv b/build/CodeGen/results.csv
index ae1127ff..48befe8d 100644
--- a/build/CodeGen/results.csv
+++ b/build/CodeGen/results.csv
@@ -106,6 +106,7 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
2,3355,,AllocationFailureInSubdirectoryFileSystemA,In Initialize allocating RootPathBuffer
2,3383,,AllocationFailureInAesXtsFileE,In Initialize
2,3394,,AllocationFailureInEncryptedFileSystemCreatorA,In Create allocating AesXtsFileSystem
+2,3407,,AllocationFailureInFileSystemInterfaceAdapter, In OpenFile or OpenDirectory
2,3420,,AllocationFailureInNew,
2,3421,,AllocationFailureInCreateShared,
2,3422,,AllocationFailureInMakeUnique,
@@ -238,7 +239,7 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
2,6030,6059,InvalidPathForOperation,
2,6031,,DirectoryNotDeletable,
-2,6032,,DestinationIsSubPathOfSource,
+2,6032,,DirectoryNotRenamable,
2,6033,,PathNotFoundInSaveDataFileTable,
2,6034,,DifferentDestFileSystem,
@@ -277,6 +278,7 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
2,6350,,UnsupportedOperationInRoGameCardStorageWrite,
2,6351,,UnsupportedOperationInRoGameCardStorageSetSize,
2,6359,,UnsupportedOperationInConcatFsQueryEntry,
+2,6362,,UnsupportedOperationInFileServiceObjectAdapterA,Called OperateRange with an invalid operation ID.
2,6364,,UnsupportedOperationModifyRomFsFileSystem,
2,6365,,UnsupportedOperationInRomFsFileSystem,Called RomFsFileSystem::CommitProvisionally.
2,6366,,UnsupportedOperationRomFsFileSystemGetSpace,
diff --git a/src/LibHac/Fs/FileTimeStamp.cs b/src/LibHac/Fs/FileTimeStamp.cs
index 74aff00a..cda15cec 100644
--- a/src/LibHac/Fs/FileTimeStamp.cs
+++ b/src/LibHac/Fs/FileTimeStamp.cs
@@ -1,9 +1,13 @@
-namespace LibHac.Fs
+using System.Runtime.InteropServices;
+
+namespace LibHac.Fs
{
+ [StructLayout(LayoutKind.Sequential, Size = 0x20)]
public struct FileTimeStampRaw
{
public long Created;
public long Accessed;
public long Modified;
+ public bool IsLocalTime;
}
}
diff --git a/src/LibHac/Fs/Fsa/IFileSystem.cs b/src/LibHac/Fs/Fsa/IFileSystem.cs
index df54e17b..e9dc742b 100644
--- a/src/LibHac/Fs/Fsa/IFileSystem.cs
+++ b/src/LibHac/Fs/Fsa/IFileSystem.cs
@@ -1,4 +1,5 @@
using System;
+using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.FsSystem;
@@ -167,7 +168,7 @@ namespace LibHac.Fs.Fsa
/// does not exist or is a file:
/// 's parent directory does not exist:
/// already exists as either a file or directory:
- /// Either or is a subpath of the other:
+ /// Either or is a subpath of the other:
///
public Result RenameDirectory(U8Span oldPath, U8Span newPath)
{
@@ -391,7 +392,7 @@ namespace LibHac.Fs.Fsa
protected virtual Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, U8Span path)
{
- timeStamp = default;
+ Unsafe.SkipInit(out timeStamp);
return ResultFs.NotImplemented.Log();
}
diff --git a/src/LibHac/Fs/Fsa/IMultiCommitTarget.cs b/src/LibHac/Fs/Fsa/IMultiCommitTarget.cs
new file mode 100644
index 00000000..533d3968
--- /dev/null
+++ b/src/LibHac/Fs/Fsa/IMultiCommitTarget.cs
@@ -0,0 +1,9 @@
+using LibHac.FsSrv.Sf;
+
+namespace LibHac.Fs.Fsa
+{
+ public interface IMultiCommitTarget
+ {
+ ReferenceCountedDisposable GetMultiCommitTarget();
+ }
+}
diff --git a/src/LibHac/Fs/Impl/DirectoryServiceObjectAdapter.cs b/src/LibHac/Fs/Impl/DirectoryServiceObjectAdapter.cs
new file mode 100644
index 00000000..4f02e30e
--- /dev/null
+++ b/src/LibHac/Fs/Impl/DirectoryServiceObjectAdapter.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Runtime.InteropServices;
+using LibHac.Fs.Fsa;
+using LibHac.FsSrv.Sf;
+
+namespace LibHac.Fs.Impl
+{
+ ///
+ /// An adapter for using an service object as an . Used
+ /// when receiving a Horizon IPC directory object so it can be used as an locally.
+ ///
+ internal class DirectoryServiceObjectAdapter : IDirectory
+ {
+ private ReferenceCountedDisposable BaseDirectory { get; }
+
+ public DirectoryServiceObjectAdapter(ReferenceCountedDisposable baseDirectory)
+ {
+ BaseDirectory = baseDirectory.AddReference();
+ }
+
+ protected override Result DoRead(out long entriesRead, Span entryBuffer)
+ {
+ Span buffer = MemoryMarshal.Cast(entryBuffer);
+ return BaseDirectory.Target.Read(out entriesRead, buffer);
+ }
+
+ protected override Result DoGetEntryCount(out long entryCount)
+ {
+ return BaseDirectory.Target.GetEntryCount(out entryCount);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ BaseDirectory?.Dispose();
+ }
+
+ base.Dispose(disposing);
+ }
+ }
+}
diff --git a/src/LibHac/Fs/Impl/FileServiceObjectAdapter.cs b/src/LibHac/Fs/Impl/FileServiceObjectAdapter.cs
new file mode 100644
index 00000000..da2f9850
--- /dev/null
+++ b/src/LibHac/Fs/Impl/FileServiceObjectAdapter.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Runtime.CompilerServices;
+using LibHac.Common;
+using LibHac.Fs.Fsa;
+using LibHac.FsSrv.Sf;
+
+namespace LibHac.Fs.Impl
+{
+ ///
+ /// An adapter for using an service object as an . Used
+ /// when receiving a Horizon IPC file object so it can be used as an locally.
+ ///
+ internal class FileServiceObjectAdapter : IFile
+ {
+ private ReferenceCountedDisposable BaseFile { get; }
+
+ public FileServiceObjectAdapter(ReferenceCountedDisposable baseFile)
+ {
+ BaseFile = baseFile.AddReference();
+ }
+
+ protected override Result DoRead(out long bytesRead, long offset, Span destination, in ReadOption option)
+ {
+ return BaseFile.Target.Read(out bytesRead, offset, destination, option);
+ }
+
+ protected override Result DoWrite(long offset, ReadOnlySpan source, in WriteOption option)
+ {
+ return BaseFile.Target.Write(offset, source, option);
+ }
+
+ protected override Result DoFlush()
+ {
+ return BaseFile.Target.Flush();
+ }
+
+ protected override Result DoSetSize(long size)
+ {
+ return BaseFile.Target.SetSize(size);
+ }
+
+ protected override Result DoGetSize(out long size)
+ {
+ return BaseFile.Target.GetSize(out size);
+ }
+
+ protected override Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer)
+ {
+ switch (operationId)
+ {
+ case OperationId.InvalidateCache:
+ return BaseFile.Target.OperateRange(out _, (int)OperationId.InvalidateCache, offset, size);
+ case OperationId.QueryRange:
+ if (outBuffer.Length != Unsafe.SizeOf())
+ return ResultFs.InvalidSize.Log();
+
+ ref QueryRangeInfo info = ref SpanHelpers.AsStruct(outBuffer);
+
+ return BaseFile.Target.OperateRange(out info, (int)OperationId.QueryRange, offset, size);
+ default:
+ return ResultFs.UnsupportedOperationInFileServiceObjectAdapterA.Log();
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ BaseFile?.Dispose();
+ }
+
+ base.Dispose(disposing);
+ }
+ }
+}
diff --git a/src/LibHac/Fs/Impl/FileSystemServiceObjectAdapter.cs b/src/LibHac/Fs/Impl/FileSystemServiceObjectAdapter.cs
new file mode 100644
index 00000000..174bb397
--- /dev/null
+++ b/src/LibHac/Fs/Impl/FileSystemServiceObjectAdapter.cs
@@ -0,0 +1,211 @@
+using System;
+using System.Runtime.CompilerServices;
+using LibHac.Common;
+using LibHac.Fs.Fsa;
+using LibHac.FsSrv.Sf;
+using LibHac.Util;
+
+namespace LibHac.Fs.Impl
+{
+ ///
+ /// An adapter for using an service object as an . Used
+ /// when receiving a Horizon IPC file system object so it can be used as an locally.
+ ///
+ internal class FileSystemServiceObjectAdapter : IFileSystem, IMultiCommitTarget
+ {
+ private ReferenceCountedDisposable BaseFs { get; }
+
+ public FileSystemServiceObjectAdapter(ReferenceCountedDisposable baseFileSystem)
+ {
+ BaseFs = baseFileSystem.AddReference();
+ }
+
+ protected override Result DoCreateFile(U8Span path, long size, CreateFileOptions option)
+ {
+ Result rc = GetPathForServiceObject(out Path sfPath, path);
+ if (rc.IsFailure()) return rc;
+
+ return BaseFs.Target.CreateFile(in sfPath, size, (int)option);
+ }
+
+ protected override Result DoDeleteFile(U8Span path)
+ {
+ Result rc = GetPathForServiceObject(out Path sfPath, path);
+ if (rc.IsFailure()) return rc;
+
+ return BaseFs.Target.DeleteFile(in sfPath);
+ }
+
+ protected override Result DoCreateDirectory(U8Span path)
+ {
+ Result rc = GetPathForServiceObject(out Path sfPath, path);
+ if (rc.IsFailure()) return rc;
+
+ return BaseFs.Target.DeleteFile(in sfPath);
+ }
+
+ protected override Result DoDeleteDirectory(U8Span path)
+ {
+ Result rc = GetPathForServiceObject(out Path sfPath, path);
+ if (rc.IsFailure()) return rc;
+
+ return BaseFs.Target.DeleteDirectory(in sfPath);
+ }
+
+ protected override Result DoDeleteDirectoryRecursively(U8Span path)
+ {
+ Result rc = GetPathForServiceObject(out Path sfPath, path);
+ if (rc.IsFailure()) return rc;
+
+ return BaseFs.Target.DeleteDirectoryRecursively(in sfPath);
+ }
+
+ protected override Result DoCleanDirectoryRecursively(U8Span path)
+ {
+ Result rc = GetPathForServiceObject(out Path sfPath, path);
+ if (rc.IsFailure()) return rc;
+
+ return BaseFs.Target.CleanDirectoryRecursively(in sfPath);
+ }
+
+ protected override Result DoRenameFile(U8Span oldPath, U8Span newPath)
+ {
+ Result rc = GetPathForServiceObject(out Path oldSfPath, oldPath);
+ if (rc.IsFailure()) return rc;
+
+ rc = GetPathForServiceObject(out Path newSfPath, newPath);
+ if (rc.IsFailure()) return rc;
+
+ return BaseFs.Target.RenameFile(in oldSfPath, in newSfPath);
+ }
+
+ protected override Result DoRenameDirectory(U8Span oldPath, U8Span newPath)
+ {
+ Result rc = GetPathForServiceObject(out Path oldSfPath, oldPath);
+ if (rc.IsFailure()) return rc;
+
+ rc = GetPathForServiceObject(out Path newSfPath, newPath);
+ if (rc.IsFailure()) return rc;
+
+ return BaseFs.Target.RenameDirectory(in oldSfPath, in newSfPath);
+ }
+
+ protected override Result DoGetEntryType(out DirectoryEntryType entryType, U8Span path)
+ {
+ Unsafe.SkipInit(out entryType);
+
+ Result rc = GetPathForServiceObject(out Path sfPath, path);
+ if (rc.IsFailure()) return rc;
+
+ ref uint sfEntryType = ref Unsafe.As(ref entryType);
+
+ return BaseFs.Target.GetEntryType(out sfEntryType, in sfPath);
+ }
+
+ protected override Result DoGetFreeSpaceSize(out long freeSpace, U8Span path)
+ {
+ Unsafe.SkipInit(out freeSpace);
+
+ Result rc = GetPathForServiceObject(out Path sfPath, path);
+ if (rc.IsFailure()) return rc;
+
+ return BaseFs.Target.GetFreeSpaceSize(out freeSpace, in sfPath);
+ }
+
+ protected override Result DoGetTotalSpaceSize(out long totalSpace, U8Span path)
+ {
+ Unsafe.SkipInit(out totalSpace);
+
+ Result rc = GetPathForServiceObject(out Path sfPath, path);
+ if (rc.IsFailure()) return rc;
+
+ return BaseFs.Target.GetTotalSpaceSize(out totalSpace, in sfPath);
+ }
+
+ protected override Result DoOpenFile(out IFile file, U8Span path, OpenMode mode)
+ {
+ file = default;
+
+ Result rc = GetPathForServiceObject(out Path sfPath, path);
+ if (rc.IsFailure()) return rc;
+
+ rc = BaseFs.Target.OpenFile(out ReferenceCountedDisposable sfFile, in sfPath, (uint)mode);
+ if (rc.IsFailure()) return rc;
+
+ file = new FileServiceObjectAdapter(sfFile);
+ return Result.Success;
+ }
+
+ protected override Result DoOpenDirectory(out IDirectory directory, U8Span path, OpenDirectoryMode mode)
+ {
+ directory = default;
+
+ Result rc = GetPathForServiceObject(out Path sfPath, path);
+ if (rc.IsFailure()) return rc;
+
+ rc = BaseFs.Target.OpenDirectory(out ReferenceCountedDisposable sfDir, in sfPath, (uint)mode);
+ if (rc.IsFailure()) return rc;
+
+ directory = new DirectoryServiceObjectAdapter(sfDir);
+ return Result.Success;
+ }
+
+ protected override Result DoCommit()
+ {
+ return BaseFs.Target.Commit();
+ }
+
+ protected override Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, U8Span path)
+ {
+ Unsafe.SkipInit(out timeStamp);
+
+ Result rc = GetPathForServiceObject(out Path sfPath, path);
+ if (rc.IsFailure()) return rc;
+
+ return BaseFs.Target.GetFileTimeStampRaw(out timeStamp, in sfPath);
+ }
+
+ protected override Result DoQueryEntry(Span outBuffer, ReadOnlySpan inBuffer, QueryId queryId, U8Span path)
+ {
+ Result rc = GetPathForServiceObject(out Path sfPath, path);
+ if (rc.IsFailure()) return rc;
+
+ return BaseFs.Target.QueryEntry(outBuffer, inBuffer, (int)queryId, in sfPath);
+ }
+
+ public ReferenceCountedDisposable GetMultiCommitTarget()
+ {
+ return BaseFs;
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ BaseFs?.Dispose();
+ }
+
+ base.Dispose(disposing);
+ }
+
+ private Result GetPathForServiceObject(out Path sfPath, U8Span path)
+ {
+ // This is the function used to create Sf.Path structs. Get an unsafe byte span for init only.
+ Unsafe.SkipInit(out sfPath);
+ Span outPath = SpanHelpers.AsByteSpan(ref sfPath);
+
+ // Copy and null terminate
+ StringUtils.Copy(outPath, path);
+ outPath[Unsafe.SizeOf() - 1] = StringTraits.NullTerminator;
+
+ // Replace directory separators
+ PathUtility.Replace(outPath, StringTraits.AltDirectorySeparator, StringTraits.DirectorySeparator);
+
+ // Get lengths
+ int windowsSkipLength = PathUtility.GetWindowsPathSkipLength(path);
+ var nonWindowsPath = new U8Span(sfPath.Str.Slice(windowsSkipLength));
+ int maxLength = PathTool.EntryNameLengthMax - windowsSkipLength;
+ return PathUtility.VerifyPath(nonWindowsPath, maxLength, maxLength);
+ }
+ }
+}
diff --git a/src/LibHac/Fs/Impl/ReaderWriterLockHandlers.cs b/src/LibHac/Fs/Impl/ReaderWriterLockHandlers.cs
new file mode 100644
index 00000000..5911caf2
--- /dev/null
+++ b/src/LibHac/Fs/Impl/ReaderWriterLockHandlers.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Threading;
+
+namespace LibHac.Fs.Impl
+{
+ ///
+ /// A wrapper for handling write access to a reader-writer lock.
+ ///
+ public class UniqueLock : IDisposable
+ {
+ private ReaderWriterLockSlim _lock;
+ private bool _hasLock;
+
+ public UniqueLock(ReaderWriterLockSlim readerWriterLock)
+ {
+ _lock = readerWriterLock;
+ readerWriterLock.EnterWriteLock();
+ _hasLock = true;
+ }
+
+ public void Dispose()
+ {
+ if (_hasLock)
+ {
+ _lock.ExitWriteLock();
+ }
+ }
+ }
+
+ ///
+ /// A wrapper for handling read access to a reader-writer lock.
+ ///
+ public class SharedLock : IDisposable
+ {
+ private ReaderWriterLockSlim _lock;
+ private bool _hasLock;
+
+ public SharedLock(ReaderWriterLockSlim readerWriterLock)
+ {
+ _lock = readerWriterLock;
+ readerWriterLock.EnterReadLock();
+ _hasLock = true;
+ }
+
+ public void Dispose()
+ {
+ if (_hasLock)
+ {
+ _lock.EnterReadLock();
+ }
+ }
+ }
+}
diff --git a/src/LibHac/Fs/Impl/StorageServiceObjectAdapter.cs b/src/LibHac/Fs/Impl/StorageServiceObjectAdapter.cs
new file mode 100644
index 00000000..c67e34d8
--- /dev/null
+++ b/src/LibHac/Fs/Impl/StorageServiceObjectAdapter.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Runtime.CompilerServices;
+using LibHac.Common;
+using LibHac.FsSrv.Sf;
+
+namespace LibHac.Fs.Impl
+{
+ ///
+ /// An adapter for using an service object as an . Used
+ /// when receiving a Horizon IPC storage object so it can be used as an locally.
+ ///
+ internal class StorageServiceObjectAdapter : IStorage
+ {
+ private ReferenceCountedDisposable BaseStorage { get; }
+
+ public StorageServiceObjectAdapter(ReferenceCountedDisposable baseStorage)
+ {
+ BaseStorage = baseStorage.AddReference();
+ }
+ protected override Result DoRead(long offset, Span destination)
+ {
+ return BaseStorage.Target.Read(offset, destination);
+ }
+
+ protected override Result DoWrite(long offset, ReadOnlySpan source)
+ {
+ return BaseStorage.Target.Write(offset, source);
+ }
+
+ protected override Result DoFlush()
+ {
+ return BaseStorage.Target.Flush();
+ }
+
+ protected override Result DoSetSize(long size)
+ {
+ return BaseStorage.Target.SetSize(size);
+ }
+
+ protected override Result DoGetSize(out long size)
+ {
+ return BaseStorage.Target.GetSize(out size);
+ }
+
+ protected override Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer)
+ {
+ switch (operationId)
+ {
+ case OperationId.InvalidateCache:
+ return BaseStorage.Target.OperateRange(out _, (int)OperationId.InvalidateCache, offset, size);
+ case OperationId.QueryRange:
+ if (outBuffer.Length != Unsafe.SizeOf())
+ return ResultFs.InvalidSize.Log();
+
+ ref QueryRangeInfo info = ref SpanHelpers.AsStruct(outBuffer);
+
+ return BaseStorage.Target.OperateRange(out info, (int)OperationId.QueryRange, offset, size);
+ default:
+ return ResultFs.UnsupportedOperationInFileServiceObjectAdapterA.Log();
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ BaseStorage?.Dispose();
+ }
+
+ base.Dispose(disposing);
+ }
+ }
+}
diff --git a/src/LibHac/Fs/PathUtility.cs b/src/LibHac/Fs/PathUtility.cs
index 93af9103..23fd693f 100644
--- a/src/LibHac/Fs/PathUtility.cs
+++ b/src/LibHac/Fs/PathUtility.cs
@@ -19,6 +19,7 @@ namespace LibHac.Fs
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsWindowsDriveCharacter(byte c)
{
+ // Mask lowercase letters to uppercase and check if it's in range
return (0b1101_1111 & c) - 'A' <= 'Z' - 'A';
//return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z';
}
diff --git a/src/LibHac/Fs/QueryRangeInfo.cs b/src/LibHac/Fs/QueryRangeInfo.cs
index 58473e6a..930ac5e9 100644
--- a/src/LibHac/Fs/QueryRangeInfo.cs
+++ b/src/LibHac/Fs/QueryRangeInfo.cs
@@ -1,11 +1,31 @@
-using System.Runtime.InteropServices;
+using System;
+using System.Runtime.InteropServices;
namespace LibHac.Fs
{
[StructLayout(LayoutKind.Sequential, Size = 0x40)]
public struct QueryRangeInfo
{
- public uint AesCtrKeyType;
- public uint SpeedEmulationType;
+ public int AesCtrKeyType;
+ public int SpeedEmulationType;
+
+ public void Clear()
+ {
+ this = default;
+ }
+
+ public void Merge(in QueryRangeInfo other)
+ {
+ AesCtrKeyType |= other.AesCtrKeyType;
+ SpeedEmulationType |= other.SpeedEmulationType;
+ }
+
+ [Flags]
+ public enum AesCtrKeyTypeFlag
+ {
+ InternalKeyForSoftwareAes = 1 << 0,
+ InternalKeyForHardwareAes = 1 << 1,
+ ExternalKeyForHardwareAes = 1 << 2,
+ }
}
}
diff --git a/src/LibHac/Fs/ResultFs.cs b/src/LibHac/Fs/ResultFs.cs
index 444ca0e4..9493ef5b 100644
--- a/src/LibHac/Fs/ResultFs.cs
+++ b/src/LibHac/Fs/ResultFs.cs
@@ -130,6 +130,8 @@ namespace LibHac.Fs
public static Result.Base AllocationFailureInAesXtsFileE => new Result.Base(ModuleFs, 3383);
/// In Create allocating AesXtsFileSystem
Error code: 2002-3394; Inner value: 0x1a8402
public static Result.Base AllocationFailureInEncryptedFileSystemCreatorA => new Result.Base(ModuleFs, 3394);
+ /// In OpenFile or OpenDirectory
Error code: 2002-3407; Inner value: 0x1a9e02
+ public static Result.Base AllocationFailureInFileSystemInterfaceAdapter => new Result.Base(ModuleFs, 3407);
/// Error code: 2002-3420; Inner value: 0x1ab802
public static Result.Base AllocationFailureInNew => new Result.Base(ModuleFs, 3420);
/// Error code: 2002-3421; Inner value: 0x1aba02
@@ -370,7 +372,7 @@ namespace LibHac.Fs
/// Error code: 2002-6031; Inner value: 0x2f1e02
public static Result.Base DirectoryNotDeletable => new Result.Base(ModuleFs, 6031);
/// Error code: 2002-6032; Inner value: 0x2f2002
- public static Result.Base DestinationIsSubPathOfSource => new Result.Base(ModuleFs, 6032);
+ public static Result.Base DirectoryNotRenamable => new Result.Base(ModuleFs, 6032);
/// Error code: 2002-6033; Inner value: 0x2f2202
public static Result.Base PathNotFoundInSaveDataFileTable => new Result.Base(ModuleFs, 6033);
/// Error code: 2002-6034; Inner value: 0x2f2402
@@ -443,6 +445,8 @@ namespace LibHac.Fs
public static Result.Base UnsupportedOperationInRoGameCardStorageSetSize => new Result.Base(ModuleFs, 6351);
/// Error code: 2002-6359; Inner value: 0x31ae02
public static Result.Base UnsupportedOperationInConcatFsQueryEntry => new Result.Base(ModuleFs, 6359);
+ /// Called OperateRange with an invalid operation ID.
Error code: 2002-6362; Inner value: 0x31b402
+ public static Result.Base UnsupportedOperationInFileServiceObjectAdapterA => new Result.Base(ModuleFs, 6362);
/// Error code: 2002-6364; Inner value: 0x31b802
public static Result.Base UnsupportedOperationModifyRomFsFileSystem => new Result.Base(ModuleFs, 6364);
/// Called RomFsFileSystem::CommitProvisionally.
Error code: 2002-6365; Inner value: 0x31ba02
diff --git a/src/LibHac/Fs/Shim/Application.cs b/src/LibHac/Fs/Shim/Application.cs
index 25d87ca0..094f2429 100644
--- a/src/LibHac/Fs/Shim/Application.cs
+++ b/src/LibHac/Fs/Shim/Application.cs
@@ -1,6 +1,6 @@
using System;
using LibHac.Common;
-using LibHac.Fs.Fsa;
+using LibHac.Fs.Impl;
using LibHac.FsSrv;
using LibHac.FsSrv.Sf;
@@ -44,11 +44,16 @@ namespace LibHac.Fs.Shim
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
- rc = fsProxy.OpenFileSystemWithId(out ReferenceCountedDisposable fileSystem, in sfPath,
+ rc = fsProxy.OpenFileSystemWithId(out ReferenceCountedDisposable fileSystem, in sfPath,
default, FileSystemProxyType.Package);
if (rc.IsFailure()) return rc;
- return fs.Register(mountName, fileSystem.Target);
+ using (fileSystem)
+ {
+ var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem);
+
+ return fs.Register(mountName, fileSystemAdapter);
+ }
}
}
}
diff --git a/src/LibHac/Fs/Shim/Bis.cs b/src/LibHac/Fs/Shim/Bis.cs
index 881f4009..f3765a9c 100644
--- a/src/LibHac/Fs/Shim/Bis.cs
+++ b/src/LibHac/Fs/Shim/Bis.cs
@@ -1,7 +1,7 @@
using System;
using System.Diagnostics;
using LibHac.Common;
-using LibHac.Fs.Fsa;
+using LibHac.Fs.Impl;
using LibHac.FsSrv;
using LibHac.FsSrv.Sf;
using LibHac.FsSystem;
@@ -91,13 +91,17 @@ namespace LibHac.Fs.Shim
// Nintendo doesn't use the provided rootPath
FspPath.CreateEmpty(out FspPath sfPath);
- rc = fsProxy.OpenBisFileSystem(out ReferenceCountedDisposable fileSystem, in sfPath,
+ rc = fsProxy.OpenBisFileSystem(out ReferenceCountedDisposable fileSystem, in sfPath,
partitionId);
if (rc.IsFailure()) return rc;
- var nameGenerator = new BisCommonMountNameGenerator(partitionId);
+ using (fileSystem)
+ {
+ var nameGenerator = new BisCommonMountNameGenerator(partitionId);
+ var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem);
- return fs.Register(mountName, fileSystem.Target, nameGenerator);
+ return fs.Register(mountName, fileSystemAdapter, nameGenerator);
+ }
}
public static U8Span GetBisMountName(BisPartitionId partitionId)
@@ -165,11 +169,16 @@ namespace LibHac.Fs.Shim
partitionStorage = default;
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
- Result rc = fsProxy.OpenBisStorage(out IStorage storage, partitionId);
+ Result rc = fsProxy.OpenBisStorage(out ReferenceCountedDisposable storage, partitionId);
if (rc.IsFailure()) return rc;
- partitionStorage = storage;
- return Result.Success;
+ using (storage)
+ {
+ var storageAdapter = new StorageServiceObjectAdapter(storage);
+
+ partitionStorage = storageAdapter;
+ return Result.Success;
+ }
}
public static Result InvalidateBisCache(this FileSystemClient fs)
diff --git a/src/LibHac/Fs/Shim/Code.cs b/src/LibHac/Fs/Shim/Code.cs
index acad3c6e..3585385c 100644
--- a/src/LibHac/Fs/Shim/Code.cs
+++ b/src/LibHac/Fs/Shim/Code.cs
@@ -1,7 +1,7 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
-using LibHac.Fs.Fsa;
+using LibHac.Fs.Impl;
using LibHac.FsSrv.Sf;
using LibHac.Ncm;
@@ -49,11 +49,16 @@ namespace LibHac.Fs.Shim
IFileSystemProxyForLoader fsProxy = fs.GetFileSystemProxyForLoaderServiceObject();
- rc = fsProxy.OpenCodeFileSystem(out ReferenceCountedDisposable codeFs, out verificationData,
+ rc = fsProxy.OpenCodeFileSystem(out ReferenceCountedDisposable codeFs, out verificationData,
in fsPath, programId);
if (rc.IsFailure()) return rc;
- return fs.Register(mountName, codeFs.Target);
+ using (codeFs)
+ {
+ var fileSystemAdapter = new FileSystemServiceObjectAdapter(codeFs);
+
+ return fs.Register(mountName, fileSystemAdapter);
+ }
}
}
}
diff --git a/src/LibHac/Fs/Shim/Content.cs b/src/LibHac/Fs/Shim/Content.cs
index f22512bc..46870fdf 100644
--- a/src/LibHac/Fs/Shim/Content.cs
+++ b/src/LibHac/Fs/Shim/Content.cs
@@ -1,6 +1,6 @@
using System;
using LibHac.Common;
-using LibHac.Fs.Fsa;
+using LibHac.Fs.Impl;
using LibHac.FsSrv;
using LibHac.FsSrv.Sf;
using LibHac.Ncm;
@@ -27,11 +27,16 @@ namespace LibHac.Fs.Shim
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
- rc = fsProxy.OpenFileSystemWithPatch(out ReferenceCountedDisposable fileSystem, programId,
+ rc = fsProxy.OpenFileSystemWithPatch(out ReferenceCountedDisposable fileSystem, programId,
fspType);
if (rc.IsFailure()) return rc;
- return fs.Register(mountName, fileSystem.Target);
+ using (fileSystem)
+ {
+ var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem);
+
+ return fs.Register(mountName, fileSystemAdapter);
+ }
}
public static Result MountContent(this FileSystemClient fs, U8Span mountName, U8Span path, ProgramId programId, ContentType type)
@@ -60,11 +65,16 @@ namespace LibHac.Fs.Shim
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
- Result rc = fsProxy.OpenFileSystemWithId(out ReferenceCountedDisposable fileSystem, in fsPath,
+ Result rc = fsProxy.OpenFileSystemWithId(out ReferenceCountedDisposable fileSystem, in fsPath,
id, type);
if (rc.IsFailure()) return rc;
- return fs.Register(mountName, fileSystem.Target);
+ using (fileSystem)
+ {
+ var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem);
+
+ return fs.Register(mountName, fileSystemAdapter);
+ }
}
private static FileSystemProxyType ConvertToFileSystemProxyType(ContentType type) => type switch
diff --git a/src/LibHac/Fs/Shim/ContentStorage.cs b/src/LibHac/Fs/Shim/ContentStorage.cs
index 98597854..eabfaedb 100644
--- a/src/LibHac/Fs/Shim/ContentStorage.cs
+++ b/src/LibHac/Fs/Shim/ContentStorage.cs
@@ -1,7 +1,8 @@
using System;
using LibHac.Common;
-using LibHac.Fs.Fsa;
+using LibHac.Fs.Impl;
using LibHac.FsSrv;
+using LibHac.FsSrv.Sf;
using LibHac.Util;
namespace LibHac.Fs.Shim
@@ -20,12 +21,17 @@ namespace LibHac.Fs.Shim
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
- rc = fsProxy.OpenContentStorageFileSystem(out ReferenceCountedDisposable contentFs, storageId);
+ rc = fsProxy.OpenContentStorageFileSystem(out ReferenceCountedDisposable contentFs, storageId);
if (rc.IsFailure()) return rc;
- var mountNameGenerator = new ContentStorageCommonMountNameGenerator(storageId);
+ using (contentFs)
+ {
+ var mountNameGenerator = new ContentStorageCommonMountNameGenerator(storageId);
- return fs.Register(mountName, contentFs.Target, mountNameGenerator);
+ var fileSystemAdapter = new FileSystemServiceObjectAdapter(contentFs);
+
+ return fs.Register(mountName, fileSystemAdapter, mountNameGenerator);
+ }
}
public static U8String GetContentStorageMountName(ContentStorageId storageId)
diff --git a/src/LibHac/Fs/Shim/GameCard.cs b/src/LibHac/Fs/Shim/GameCard.cs
index 52b7d8c0..8af2e991 100644
--- a/src/LibHac/Fs/Shim/GameCard.cs
+++ b/src/LibHac/Fs/Shim/GameCard.cs
@@ -1,7 +1,8 @@
using System;
using LibHac.Common;
-using LibHac.Fs.Fsa;
+using LibHac.Fs.Impl;
using LibHac.FsSrv;
+using LibHac.FsSrv.Sf;
namespace LibHac.Fs.Shim
{
@@ -48,12 +49,16 @@ namespace LibHac.Fs.Shim
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
- rc = fsProxy.OpenGameCardFileSystem(out ReferenceCountedDisposable cardFs, handle, partitionId);
+ rc = fsProxy.OpenGameCardFileSystem(out ReferenceCountedDisposable cardFs, handle, partitionId);
if (rc.IsFailure()) return rc;
- var mountNameGenerator = new GameCardCommonMountNameGenerator(handle, partitionId);
+ using (cardFs)
+ {
+ var mountNameGenerator = new GameCardCommonMountNameGenerator(handle, partitionId);
+ var fileSystemAdapter = new FileSystemServiceObjectAdapter(cardFs);
- return fs.Register(mountName, cardFs.Target, mountNameGenerator);
+ return fs.Register(mountName, fileSystemAdapter, mountNameGenerator);
+ }
}
private class GameCardCommonMountNameGenerator : ICommonMountNameGenerator
diff --git a/src/LibHac/Fs/Shim/SdCard.cs b/src/LibHac/Fs/Shim/SdCard.cs
index e18de776..73e4cd2d 100644
--- a/src/LibHac/Fs/Shim/SdCard.cs
+++ b/src/LibHac/Fs/Shim/SdCard.cs
@@ -1,7 +1,8 @@
using System;
using LibHac.Common;
-using LibHac.Fs.Fsa;
+using LibHac.Fs.Impl;
using LibHac.FsSrv;
+using LibHac.FsSrv.Sf;
namespace LibHac.Fs.Shim
{
@@ -41,10 +42,15 @@ namespace LibHac.Fs.Shim
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
- rc = fsProxy.OpenSdCardFileSystem(out ReferenceCountedDisposable fileSystem);
+ rc = fsProxy.OpenSdCardFileSystem(out ReferenceCountedDisposable fileSystem);
if (rc.IsFailure()) return rc;
- return fs.Register(mountName, fileSystem.Target);
+ using (fileSystem)
+ {
+ var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem);
+
+ return fs.Register(mountName, fileSystemAdapter);
+ }
}
}
diff --git a/src/LibHac/FsSrv/BaseFileSystemService.cs b/src/LibHac/FsSrv/BaseFileSystemService.cs
index ee5fa3c8..f3004b86 100644
--- a/src/LibHac/FsSrv/BaseFileSystemService.cs
+++ b/src/LibHac/FsSrv/BaseFileSystemService.cs
@@ -17,7 +17,7 @@ namespace LibHac.FsSrv
_processId = processId;
}
- public Result OpenBisFileSystem(out ReferenceCountedDisposable fileSystem, in FspPath rootPath,
+ public Result OpenBisFileSystem(out ReferenceCountedDisposable fileSystem, in FspPath rootPath,
BisPartitionId partitionId)
{
fileSystem = default;
@@ -50,12 +50,24 @@ namespace LibHac.FsSrv
var normalizer = new PathNormalizer(rootPath, PathNormalizer.Option.AcceptEmpty);
if (normalizer.Result.IsFailure()) return normalizer.Result;
- rc = _serviceImpl.OpenBisFileSystem(out ReferenceCountedDisposable bisFs, normalizer.Path,
- partitionId);
- if (rc.IsFailure()) return rc;
+ ReferenceCountedDisposable fs = null;
- fileSystem = bisFs;
- return Result.Success;
+ try
+ {
+ // Open the file system
+ rc = _serviceImpl.OpenBisFileSystem(out fs, normalizer.Path,
+ partitionId);
+ if (rc.IsFailure()) return rc;
+
+ // Create an SF adapter for the file system
+ fileSystem = FileSystemInterfaceAdapter.CreateSharedSfFileSystem(ref fs);
+
+ return Result.Success;
+ }
+ finally
+ {
+ fs?.Dispose();
+ }
}
public Result CreatePaddingFile(long size)
@@ -86,7 +98,7 @@ namespace LibHac.FsSrv
return _serviceImpl.DeleteAllPaddingFiles();
}
- public Result OpenGameCardFileSystem(out ReferenceCountedDisposable fileSystem, GameCardHandle handle,
+ public Result OpenGameCardFileSystem(out ReferenceCountedDisposable fileSystem, GameCardHandle handle,
GameCardPartition partitionId)
{
fileSystem = default;
@@ -97,14 +109,25 @@ namespace LibHac.FsSrv
if (!programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountGameCard).CanRead)
return ResultFs.PermissionDenied.Log();
- rc = _serviceImpl.OpenGameCardFileSystem(out ReferenceCountedDisposable gcFs, handle, partitionId);
- if (rc.IsFailure()) return rc;
+ ReferenceCountedDisposable fs = null;
- fileSystem = gcFs;
- return Result.Success;
+ try
+ {
+ rc = _serviceImpl.OpenGameCardFileSystem(out fs, handle, partitionId);
+ if (rc.IsFailure()) return rc;
+
+ // Create an SF adapter for the file system
+ fileSystem = FileSystemInterfaceAdapter.CreateSharedSfFileSystem(ref fs);
+
+ return Result.Success;
+ }
+ finally
+ {
+ fs?.Dispose();
+ }
}
- public Result OpenSdCardFileSystem(out ReferenceCountedDisposable fileSystem)
+ public Result OpenSdCardFileSystem(out ReferenceCountedDisposable fileSystem)
{
fileSystem = default;
@@ -116,11 +139,22 @@ namespace LibHac.FsSrv
if (!accessibility.CanRead || !accessibility.CanWrite)
return ResultFs.PermissionDenied.Log();
- rc = _serviceImpl.OpenSdCardProxyFileSystem(out ReferenceCountedDisposable sdCardFs);
- if (rc.IsFailure()) return rc;
+ ReferenceCountedDisposable fs = null;
- fileSystem = sdCardFs;
- return Result.Success;
+ try
+ {
+ rc = _serviceImpl.OpenSdCardProxyFileSystem(out fs);
+ if (rc.IsFailure()) return rc;
+
+ // Create an SF adapter for the file system
+ fileSystem = FileSystemInterfaceAdapter.CreateSharedSfFileSystem(ref fs);
+
+ return Result.Success;
+ }
+ finally
+ {
+ fs?.Dispose();
+ }
}
public Result FormatSdCardFileSystem()
@@ -150,7 +184,7 @@ namespace LibHac.FsSrv
return Result.Success;
}
- public Result OpenImageDirectoryFileSystem(out ReferenceCountedDisposable fileSystem,
+ public Result OpenImageDirectoryFileSystem(out ReferenceCountedDisposable fileSystem,
ImageDirectoryId directoryId)
{
fileSystem = default;
@@ -178,15 +212,25 @@ namespace LibHac.FsSrv
default:
return ResultFs.InvalidArgument.Log();
}
+ ReferenceCountedDisposable fs = null;
- rc = _serviceImpl.OpenBaseFileSystem(out ReferenceCountedDisposable imageFs, id);
- if (rc.IsFailure()) return rc;
+ try
+ {
+ rc = _serviceImpl.OpenBaseFileSystem(out fs, id);
+ if (rc.IsFailure()) return rc;
- fileSystem = imageFs;
- return Result.Success;
+ // Create an SF adapter for the file system
+ fileSystem = FileSystemInterfaceAdapter.CreateSharedSfFileSystem(ref fs);
+
+ return Result.Success;
+ }
+ finally
+ {
+ fs?.Dispose();
+ }
}
- public Result OpenBisWiper(out IWiper bisWiper, NativeHandle transferMemoryHandle, ulong transferMemorySize)
+ public Result OpenBisWiper(out ReferenceCountedDisposable bisWiper, NativeHandle transferMemoryHandle, ulong transferMemorySize)
{
bisWiper = default;
@@ -200,7 +244,7 @@ namespace LibHac.FsSrv
rc = _serviceImpl.OpenBisWiper(out IWiper wiper, transferMemoryHandle, transferMemorySize);
if (rc.IsFailure()) return rc;
- bisWiper = wiper;
+ bisWiper = new ReferenceCountedDisposable(wiper);
return Result.Success;
}
diff --git a/src/LibHac/FsSrv/FileSystemProxyCoreImpl.cs b/src/LibHac/FsSrv/FileSystemProxyCoreImpl.cs
index 6c428afb..a2591dac 100644
--- a/src/LibHac/FsSrv/FileSystemProxyCoreImpl.cs
+++ b/src/LibHac/FsSrv/FileSystemProxyCoreImpl.cs
@@ -14,7 +14,6 @@ namespace LibHac.FsSrv
private FileSystemCreators FsCreators => Config.FsCreatorInterfaces;
internal ProgramRegistryImpl ProgramRegistry { get; }
- private ExternalKeySet ExternalKeys { get; }
private IDeviceOperator DeviceOperator { get; }
private byte[] SdEncryptionSeed { get; } = new byte[0x10];
@@ -26,11 +25,10 @@ namespace LibHac.FsSrv
internal ISaveDataIndexerManager SaveDataIndexerManager { get; private set; }
- public FileSystemProxyCoreImpl(FileSystemProxyConfiguration config, ExternalKeySet externalKeys, IDeviceOperator deviceOperator)
+ public FileSystemProxyCoreImpl(FileSystemProxyConfiguration config, IDeviceOperator deviceOperator)
{
Config = config;
ProgramRegistry = new ProgramRegistryImpl(Config.ProgramRegistryService);
- ExternalKeys = externalKeys ?? new ExternalKeySet();
DeviceOperator = deviceOperator;
}
diff --git a/src/LibHac/FsSrv/FileSystemProxyImpl.cs b/src/LibHac/FsSrv/FileSystemProxyImpl.cs
index 904025c6..0d46b19c 100644
--- a/src/LibHac/FsSrv/FileSystemProxyImpl.cs
+++ b/src/LibHac/FsSrv/FileSystemProxyImpl.cs
@@ -18,7 +18,7 @@ namespace LibHac.FsSrv
public class FileSystemProxyImpl : IFileSystemProxy, IFileSystemProxyForLoader
{
private FileSystemProxyCoreImpl FsProxyCore { get; }
- private ReferenceCountedDisposable NcaFileSystemService { get; set; }
+ private ReferenceCountedDisposable NcaFsService { get; set; }
internal HorizonClient Hos { get; }
public ulong CurrentProcess { get; private set; }
@@ -44,7 +44,7 @@ namespace LibHac.FsSrv
return new ProgramRegistryService(FsProxyCore.Config.ProgramRegistryService, CurrentProcess);
}
- public Result OpenFileSystemWithId(out ReferenceCountedDisposable fileSystem, in FspPath path,
+ public Result OpenFileSystemWithId(out ReferenceCountedDisposable fileSystem, in FspPath path,
ulong id, FileSystemProxyType fsType)
{
@@ -58,7 +58,7 @@ namespace LibHac.FsSrv
return ncaFsService.OpenFileSystemWithId(out fileSystem, in path, id, fsType);
}
- public Result OpenFileSystemWithPatch(out ReferenceCountedDisposable fileSystem,
+ public Result OpenFileSystemWithPatch(out ReferenceCountedDisposable fileSystem,
ProgramId programId, FileSystemProxyType fsType)
{
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
@@ -71,7 +71,7 @@ namespace LibHac.FsSrv
return ncaFsService.OpenFileSystemWithPatch(out fileSystem, programId, fsType);
}
- public Result OpenCodeFileSystem(out ReferenceCountedDisposable fileSystem,
+ public Result OpenCodeFileSystem(out ReferenceCountedDisposable fileSystem,
out CodeVerificationData verificationData, in FspPath path, ProgramId programId)
{
Unsafe.SkipInit(out verificationData);
@@ -101,9 +101,7 @@ namespace LibHac.FsSrv
CurrentProcess = processId;
// Initialize the NCA file system service
- var ncaService = new NcaFileSystemService(FsProxyCore.Config.NcaFileSystemService, processId);
- NcaFileSystemService = new ReferenceCountedDisposable(ncaService);
- NcaFileSystemService.Target.SetSelfReference(NcaFileSystemService);
+ NcaFsService = NcaFileSystemService.Create(FsProxyCore.Config.NcaFileSystemService, processId);
return Result.Success;
}
@@ -113,7 +111,7 @@ namespace LibHac.FsSrv
throw new NotImplementedException();
}
- public Result OpenDataFileSystemByCurrentProcess(out ReferenceCountedDisposable fileSystem)
+ public Result OpenDataFileSystemByCurrentProcess(out ReferenceCountedDisposable fileSystem)
{
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
if (rc.IsFailure())
@@ -125,7 +123,7 @@ namespace LibHac.FsSrv
return ncaFsService.OpenDataFileSystemByCurrentProcess(out fileSystem);
}
- public Result OpenDataFileSystemByProgramId(out ReferenceCountedDisposable fileSystem,
+ public Result OpenDataFileSystemByProgramId(out ReferenceCountedDisposable fileSystem,
ProgramId programId)
{
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
@@ -138,7 +136,7 @@ namespace LibHac.FsSrv
return ncaFsService.OpenDataFileSystemByProgramId(out fileSystem, programId);
}
- public Result OpenDataStorageByCurrentProcess(out ReferenceCountedDisposable storage)
+ public Result OpenDataStorageByCurrentProcess(out ReferenceCountedDisposable storage)
{
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
if (rc.IsFailure())
@@ -150,7 +148,7 @@ namespace LibHac.FsSrv
return ncaFsService.OpenDataStorageByCurrentProcess(out storage);
}
- public Result OpenDataStorageByProgramId(out ReferenceCountedDisposable storage, ProgramId programId)
+ public Result OpenDataStorageByProgramId(out ReferenceCountedDisposable storage, ProgramId programId)
{
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
if (rc.IsFailure())
@@ -162,7 +160,7 @@ namespace LibHac.FsSrv
return ncaFsService.OpenDataStorageByProgramId(out storage, programId);
}
- public Result OpenDataStorageByDataId(out ReferenceCountedDisposable storage, DataId dataId, StorageId storageId)
+ public Result OpenDataStorageByDataId(out ReferenceCountedDisposable storage, DataId dataId, StorageId storageId)
{
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
if (rc.IsFailure())
@@ -174,13 +172,13 @@ namespace LibHac.FsSrv
return ncaFsService.OpenDataStorageByDataId(out storage, dataId, storageId);
}
- public Result OpenPatchDataStorageByCurrentProcess(out IStorage storage)
+ public Result OpenPatchDataStorageByCurrentProcess(out ReferenceCountedDisposable storage)
{
storage = default;
return ResultFs.TargetNotFound.Log();
}
- public Result OpenDataFileSystemWithProgramIndex(out ReferenceCountedDisposable fileSystem,
+ public Result OpenDataFileSystemWithProgramIndex(out ReferenceCountedDisposable fileSystem,
byte programIndex)
{
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
@@ -193,7 +191,7 @@ namespace LibHac.FsSrv
return ncaFsService.OpenDataFileSystemWithProgramIndex(out fileSystem, programIndex);
}
- public Result OpenDataStorageWithProgramIndex(out ReferenceCountedDisposable storage, byte programIndex)
+ public Result OpenDataStorageWithProgramIndex(out ReferenceCountedDisposable storage, byte programIndex)
{
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
if (rc.IsFailure())
@@ -744,7 +742,7 @@ namespace LibHac.FsSrv
throw new NotImplementedException();
}
- public Result OpenImageDirectoryFileSystem(out ReferenceCountedDisposable fileSystem,
+ public Result OpenImageDirectoryFileSystem(out ReferenceCountedDisposable fileSystem,
ImageDirectoryId directoryId)
{
return GetBaseFileSystemService().OpenImageDirectoryFileSystem(out fileSystem, directoryId);
@@ -760,13 +758,13 @@ namespace LibHac.FsSrv
throw new NotImplementedException();
}
- public Result OpenBisFileSystem(out ReferenceCountedDisposable fileSystem, in FspPath rootPath,
+ public Result OpenBisFileSystem(out ReferenceCountedDisposable fileSystem, in FspPath rootPath,
BisPartitionId partitionId)
{
return GetBaseFileSystemService().OpenBisFileSystem(out fileSystem, in rootPath, partitionId);
}
- public Result OpenBisStorage(out IStorage storage, BisPartitionId partitionId)
+ public Result OpenBisStorage(out ReferenceCountedDisposable storage, BisPartitionId partitionId)
{
throw new NotImplementedException();
}
@@ -788,7 +786,7 @@ namespace LibHac.FsSrv
return OpenHostFileSystemWithOption(out fileSystem, ref path, MountHostOption.None);
}
- public Result OpenSdCardFileSystem(out ReferenceCountedDisposable fileSystem)
+ public Result OpenSdCardFileSystem(out ReferenceCountedDisposable fileSystem)
{
return GetBaseFileSystemService().OpenSdCardFileSystem(out fileSystem);
}
@@ -952,7 +950,8 @@ namespace LibHac.FsSrv
}
}
- public Result OpenSaveDataInternalStorageFileSystem(out IFileSystem fileSystem, SaveDataSpaceId spaceId, ulong saveDataId)
+ public Result OpenSaveDataInternalStorageFileSystem(out ReferenceCountedDisposable fileSystem,
+ SaveDataSpaceId spaceId, ulong saveDataId)
{
throw new NotImplementedException();
}
@@ -1062,7 +1061,7 @@ namespace LibHac.FsSrv
return Result.Success;
}
- public Result OpenContentStorageFileSystem(out ReferenceCountedDisposable fileSystem, ContentStorageId storageId)
+ public Result OpenContentStorageFileSystem(out ReferenceCountedDisposable fileSystem, ContentStorageId storageId)
{
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
if (rc.IsFailure())
@@ -1074,7 +1073,8 @@ namespace LibHac.FsSrv
return ncaFsService.OpenContentStorageFileSystem(out fileSystem, storageId);
}
- public Result OpenCloudBackupWorkStorageFileSystem(out IFileSystem fileSystem, CloudBackupWorkStorageId storageId)
+ public Result OpenCloudBackupWorkStorageFileSystem(out ReferenceCountedDisposable fileSystem,
+ CloudBackupWorkStorageId storageId)
{
throw new NotImplementedException();
}
@@ -1086,7 +1086,7 @@ namespace LibHac.FsSrv
return FsProxyCore.OpenCustomStorageFileSystem(out fileSystem, storageId);
}
- public Result OpenGameCardFileSystem(out ReferenceCountedDisposable fileSystem,
+ public Result OpenGameCardFileSystem(out ReferenceCountedDisposable fileSystem,
GameCardHandle handle, GameCardPartition partitionId)
{
return GetBaseFileSystemService().OpenGameCardFileSystem(out fileSystem, handle, partitionId);
@@ -1245,7 +1245,7 @@ namespace LibHac.FsSrv
return ncaFsService.RegisterUpdatePartition();
}
- public Result OpenRegisteredUpdatePartition(out ReferenceCountedDisposable fileSystem)
+ public Result OpenRegisteredUpdatePartition(out ReferenceCountedDisposable fileSystem)
{
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
if (rc.IsFailure())
@@ -1296,7 +1296,8 @@ namespace LibHac.FsSrv
return Result.Success;
}
- public Result OpenBisWiper(out IWiper bisWiper, NativeHandle transferMemoryHandle, ulong transferMemorySize)
+ public Result OpenBisWiper(out ReferenceCountedDisposable bisWiper, NativeHandle transferMemoryHandle,
+ ulong transferMemorySize)
{
return GetBaseFileSystemService().OpenBisWiper(out bisWiper, transferMemoryHandle, transferMemorySize);
}
@@ -1357,13 +1358,13 @@ namespace LibHac.FsSrv
private Result GetNcaFileSystemService(out NcaFileSystemService ncaFsService)
{
- if (NcaFileSystemService is null)
+ if (NcaFsService is null)
{
ncaFsService = null;
return ResultFs.PreconditionViolation.Log();
}
- ncaFsService = NcaFileSystemService.Target;
+ ncaFsService = NcaFsService.Target;
return Result.Success;
}
diff --git a/src/LibHac/FsSrv/FileSystemServer.cs b/src/LibHac/FsSrv/FileSystemServer.cs
index d9780c86..a9a49e8c 100644
--- a/src/LibHac/FsSrv/FileSystemServer.cs
+++ b/src/LibHac/FsSrv/FileSystemServer.cs
@@ -40,12 +40,11 @@ namespace LibHac.FsSrv
IsDebugMode = false;
- ExternalKeySet externalKeySet = config.ExternalKeySet ?? new ExternalKeySet();
Timer = config.TimeSpanGenerator ?? new StopWatchTimeSpanGenerator();
FileSystemProxyConfiguration fspConfig = InitializeFileSystemProxyConfiguration(config);
- FsProxyCore = new FileSystemProxyCoreImpl(fspConfig, externalKeySet, config.DeviceOperator);
+ FsProxyCore = new FileSystemProxyCoreImpl(fspConfig, config.DeviceOperator);
FsProxyCore.SetSaveDataIndexerManager(new SaveDataIndexerManager(Hos.Fs, SaveIndexerId,
new ArrayPoolMemoryResource(), new SdHandleManager(), false));
diff --git a/src/LibHac/FsSrv/IFileSystemProxy.cs b/src/LibHac/FsSrv/IFileSystemProxy.cs
index 8ad41c87..f400674e 100644
--- a/src/LibHac/FsSrv/IFileSystemProxy.cs
+++ b/src/LibHac/FsSrv/IFileSystemProxy.cs
@@ -13,16 +13,16 @@ namespace LibHac.FsSrv
public interface IFileSystemProxy
{
Result SetCurrentProcess(ulong processId);
- Result OpenDataFileSystemByCurrentProcess(out ReferenceCountedDisposable fileSystem);
- Result OpenFileSystemWithPatch(out ReferenceCountedDisposable fileSystem, ProgramId programId, FileSystemProxyType fsType);
- Result OpenFileSystemWithId(out ReferenceCountedDisposable fileSystem, in FspPath path, ulong id, FileSystemProxyType fsType);
- Result OpenDataFileSystemByProgramId(out ReferenceCountedDisposable fileSystem, ProgramId programId);
- Result OpenBisFileSystem(out ReferenceCountedDisposable fileSystem, in FspPath rootPath, BisPartitionId partitionId);
- Result OpenBisStorage(out IStorage storage, BisPartitionId partitionId);
+ Result OpenDataFileSystemByCurrentProcess(out ReferenceCountedDisposable fileSystem);
+ Result OpenFileSystemWithPatch(out ReferenceCountedDisposable fileSystem, ProgramId programId, FileSystemProxyType fsType);
+ Result OpenFileSystemWithId(out ReferenceCountedDisposable fileSystem, in FspPath path, ulong id, FileSystemProxyType fsType);
+ Result OpenDataFileSystemByProgramId(out ReferenceCountedDisposable fileSystem, ProgramId programId);
+ Result OpenBisFileSystem(out ReferenceCountedDisposable fileSystem, in FspPath rootPath, BisPartitionId partitionId);
+ Result OpenBisStorage(out ReferenceCountedDisposable storage, BisPartitionId partitionId);
Result InvalidateBisCache();
Result OpenHostFileSystemWithOption(out IFileSystem fileSystem, ref FsPath path, MountHostOption option);
Result OpenHostFileSystem(out IFileSystem fileSystem, ref FsPath path);
- Result OpenSdCardFileSystem(out ReferenceCountedDisposable fileSystem);
+ Result OpenSdCardFileSystem(out ReferenceCountedDisposable fileSystem);
Result FormatSdCardFileSystem();
Result DeleteSaveDataFileSystem(ulong saveDataId);
Result CreateSaveDataFileSystem(ref SaveDataAttribute attribute, ref SaveDataCreationInfo creationInfo, ref SaveMetaCreateInfo metaCreateInfo);
@@ -33,7 +33,7 @@ namespace LibHac.FsSrv
Result IsExFatSupported(out bool isSupported);
Result DeleteSaveDataFileSystemBySaveDataAttribute(SaveDataSpaceId spaceId, ref SaveDataAttribute attribute);
Result OpenGameCardStorage(out IStorage storage, GameCardHandle handle, GameCardPartitionRaw partitionId);
- Result OpenGameCardFileSystem(out ReferenceCountedDisposable fileSystem, GameCardHandle handle, GameCardPartition partitionId);
+ Result OpenGameCardFileSystem(out ReferenceCountedDisposable fileSystem, GameCardHandle handle, GameCardPartition partitionId);
Result ExtendSaveDataFileSystem(SaveDataSpaceId spaceId, ulong saveDataId, long dataSize, long journalSize);
Result DeleteCacheStorage(short index);
Result GetCacheStorageSize(out long dataSize, out long journalSize, short index);
@@ -47,7 +47,7 @@ namespace LibHac.FsSrv
Result OpenSaveDataInfoReader(out ReferenceCountedDisposable infoReader);
Result OpenSaveDataInfoReaderBySaveDataSpaceId(out ReferenceCountedDisposable infoReader, SaveDataSpaceId spaceId);
Result OpenSaveDataInfoReaderOnlyCacheStorage(out ReferenceCountedDisposable infoReader);
- Result OpenSaveDataInternalStorageFileSystem(out IFileSystem fileSystem, SaveDataSpaceId spaceId, ulong saveDataId);
+ Result OpenSaveDataInternalStorageFileSystem(out ReferenceCountedDisposable fileSystem, SaveDataSpaceId spaceId, ulong saveDataId);
Result UpdateSaveDataMacForDebug(SaveDataSpaceId spaceId, ulong saveDataId);
Result WriteSaveDataFileSystemExtraDataWithMask(ulong saveDataId, SaveDataSpaceId spaceId, ReadOnlySpan extraDataBuffer, ReadOnlySpan maskBuffer);
Result FindSaveDataWithFilter(out long count, Span saveDataInfoBuffer, SaveDataSpaceId spaceId, ref SaveDataFilter filter);
@@ -57,16 +57,16 @@ namespace LibHac.FsSrv
Result OpenSaveDataMetaFile(out IFile file, SaveDataSpaceId spaceId, ref SaveDataAttribute attribute, SaveDataMetaType type);
Result ListAccessibleSaveDataOwnerId(out int readCount, Span idBuffer, ProgramId programId, int startIndex, int bufferIdCount);
- Result OpenImageDirectoryFileSystem(out ReferenceCountedDisposable fileSystem, ImageDirectoryId directoryId);
- Result OpenContentStorageFileSystem(out ReferenceCountedDisposable fileSystem, ContentStorageId storageId);
- Result OpenCloudBackupWorkStorageFileSystem(out IFileSystem fileSystem, CloudBackupWorkStorageId storageId);
+ Result OpenImageDirectoryFileSystem(out ReferenceCountedDisposable fileSystem, ImageDirectoryId directoryId);
+ Result OpenContentStorageFileSystem(out ReferenceCountedDisposable fileSystem, ContentStorageId storageId);
+ Result OpenCloudBackupWorkStorageFileSystem(out ReferenceCountedDisposable fileSystem, CloudBackupWorkStorageId storageId);
Result OpenCustomStorageFileSystem(out IFileSystem fileSystem, CustomStorageId storageId);
- Result OpenDataStorageByCurrentProcess(out ReferenceCountedDisposable storage);
- Result OpenDataStorageByProgramId(out ReferenceCountedDisposable storage, ProgramId programId);
- Result OpenDataStorageByDataId(out ReferenceCountedDisposable storage, DataId dataId, StorageId storageId);
- Result OpenPatchDataStorageByCurrentProcess(out IStorage storage);
- Result OpenDataFileSystemWithProgramIndex(out ReferenceCountedDisposable fileSystem, byte programIndex);
- Result OpenDataStorageWithProgramIndex(out ReferenceCountedDisposable storage, byte programIndex);
+ Result OpenDataStorageByCurrentProcess(out ReferenceCountedDisposable storage);
+ Result OpenDataStorageByProgramId(out ReferenceCountedDisposable storage, ProgramId programId);
+ Result OpenDataStorageByDataId(out ReferenceCountedDisposable storage, DataId dataId, StorageId storageId);
+ Result OpenPatchDataStorageByCurrentProcess(out ReferenceCountedDisposable storage);
+ Result OpenDataFileSystemWithProgramIndex(out ReferenceCountedDisposable fileSystem, byte programIndex);
+ Result OpenDataStorageWithProgramIndex(out ReferenceCountedDisposable storage, byte programIndex);
Result OpenDeviceOperator(out IDeviceOperator deviceOperator);
Result OpenSystemDataUpdateEventNotifier(out ReferenceCountedDisposable eventNotifier);
@@ -102,12 +102,12 @@ namespace LibHac.FsSrv
Result GetGlobalAccessLogMode(out GlobalAccessLogMode mode);
Result OutputAccessLogToSdCard(U8Span logString);
Result RegisterUpdatePartition();
- Result OpenRegisteredUpdatePartition(out ReferenceCountedDisposable fileSystem);
+ Result OpenRegisteredUpdatePartition(out ReferenceCountedDisposable fileSystem);
Result GetProgramIndexForAccessLog(out int programIndex, out int programCount);
Result OverrideSaveDataTransferTokenSignVerificationKey(ReadOnlySpan key);
Result CorruptSaveDataFileSystemByOffset(SaveDataSpaceId spaceId, ulong saveDataId, long offset);
Result OpenMultiCommitManager(out IMultiCommitManager commitManager);
- Result OpenBisWiper(out IWiper bisWiper, NativeHandle transferMemoryHandle, ulong transferMemorySize);
+ Result OpenBisWiper(out ReferenceCountedDisposable bisWiper, NativeHandle transferMemoryHandle, ulong transferMemorySize);
}
}
\ No newline at end of file
diff --git a/src/LibHac/FsSrv/Impl/BisWiper.cs b/src/LibHac/FsSrv/Impl/BisWiper.cs
index d82ccf77..fe04a44f 100644
--- a/src/LibHac/FsSrv/Impl/BisWiper.cs
+++ b/src/LibHac/FsSrv/Impl/BisWiper.cs
@@ -25,5 +25,9 @@ namespace LibHac.FsSrv.Impl
wiper = new BisWiper(memoryHandle, memorySize);
return Result.Success;
}
+
+ public void Dispose()
+ {
+ }
}
}
diff --git a/src/LibHac/FsSrv/Impl/DirectoryInterfaceAdapter.cs b/src/LibHac/FsSrv/Impl/DirectoryInterfaceAdapter.cs
new file mode 100644
index 00000000..c01706eb
--- /dev/null
+++ b/src/LibHac/FsSrv/Impl/DirectoryInterfaceAdapter.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Runtime.InteropServices;
+using LibHac.Fs;
+using LibHac.Fs.Fsa;
+using LibHac.FsSrv.Sf;
+
+namespace LibHac.FsSrv.Impl
+{
+ internal class DirectoryInterfaceAdapter : IDirectorySf
+ {
+ private ReferenceCountedDisposable ParentFs { get; }
+ private IDirectory BaseDirectory { get; }
+
+ public DirectoryInterfaceAdapter(IDirectory baseDirectory,
+ ref ReferenceCountedDisposable parentFileSystem)
+ {
+ BaseDirectory = baseDirectory;
+ ParentFs = parentFileSystem;
+ parentFileSystem = null;
+ }
+
+ public Result Read(out long entriesRead, Span entryBuffer)
+ {
+ const int maxTryCount = 2;
+ entriesRead = default;
+
+ Span entries = MemoryMarshal.Cast(entryBuffer);
+
+ Result rc = Result.Success;
+ long tmpEntriesRead = 0;
+
+ for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
+ {
+ rc = BaseDirectory.Read(out tmpEntriesRead, entries);
+
+ // Retry on ResultDataCorrupted
+ if (!ResultFs.DataCorrupted.Includes(rc))
+ break;
+ }
+
+ if (rc.IsFailure()) return rc;
+
+ entriesRead = tmpEntriesRead;
+ return Result.Success;
+ }
+
+ public Result GetEntryCount(out long entryCount)
+ {
+ entryCount = default;
+
+ Result rc = BaseDirectory.GetEntryCount(out long tmpEntryCount);
+ if (rc.IsFailure()) return rc;
+
+ entryCount = tmpEntryCount;
+ return Result.Success;
+ }
+
+ public void Dispose()
+ {
+ BaseDirectory?.Dispose();
+ ParentFs?.Dispose();
+ }
+ }
+}
diff --git a/src/LibHac/FsSrv/Impl/FileInterfaceAdapter.cs b/src/LibHac/FsSrv/Impl/FileInterfaceAdapter.cs
new file mode 100644
index 00000000..65bf4ec8
--- /dev/null
+++ b/src/LibHac/FsSrv/Impl/FileInterfaceAdapter.cs
@@ -0,0 +1,131 @@
+using System;
+using System.Runtime.CompilerServices;
+using LibHac.Common;
+using LibHac.Fs;
+using LibHac.Fs.Fsa;
+using LibHac.FsSrv.Sf;
+
+namespace LibHac.FsSrv.Impl
+{
+ internal class FileInterfaceAdapter : IFileSf
+ {
+ private ReferenceCountedDisposable ParentFs { get; }
+ private IFile BaseFile { get; }
+
+ public FileInterfaceAdapter(IFile baseFile,
+ ref ReferenceCountedDisposable parentFileSystem)
+ {
+ BaseFile = baseFile;
+ ParentFs = parentFileSystem;
+ parentFileSystem = null;
+ }
+
+ public Result Read(out long bytesRead, long offset, Span destination, ReadOption option)
+ {
+ const int maxTryCount = 2;
+ bytesRead = default;
+
+ if (offset < 0)
+ return ResultFs.InvalidOffset.Log();
+
+ if (destination.Length < 0)
+ return ResultFs.InvalidSize.Log();
+
+ Result rc = Result.Success;
+ long tmpBytesRead = 0;
+
+ for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
+ {
+ rc = BaseFile.Read(out tmpBytesRead, offset, destination, option);
+
+ // Retry on ResultDataCorrupted
+ if (!ResultFs.DataCorrupted.Includes(rc))
+ break;
+ }
+
+ if (rc.IsFailure()) return rc;
+
+ bytesRead = tmpBytesRead;
+ return Result.Success;
+ }
+
+ public Result Write(long offset, ReadOnlySpan source, WriteOption option)
+ {
+ if (offset < 0)
+ return ResultFs.InvalidOffset.Log();
+
+ if (source.Length < 0)
+ return ResultFs.InvalidSize.Log();
+
+ // Note: Thread priority is temporarily when writing in FS
+
+ return BaseFile.Write(offset, source, option);
+ }
+
+ public Result Flush()
+ {
+ return BaseFile.Flush();
+ }
+
+ public Result SetSize(long size)
+ {
+ if (size < 0)
+ return ResultFs.InvalidSize.Log();
+
+ return BaseFile.SetSize(size);
+ }
+
+ public Result GetSize(out long size)
+ {
+ const int maxTryCount = 2;
+ size = default;
+
+ Result rc = Result.Success;
+ long tmpSize = 0;
+
+ for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
+ {
+ rc = BaseFile.GetSize(out tmpSize);
+
+ // Retry on ResultDataCorrupted
+ if (!ResultFs.DataCorrupted.Includes(rc))
+ break;
+ }
+
+ if (rc.IsFailure()) return rc;
+
+ size = tmpSize;
+ return Result.Success;
+ }
+
+ public Result OperateRange(out QueryRangeInfo rangeInfo, int operationId, long offset, long size)
+ {
+ rangeInfo = new QueryRangeInfo();
+
+ if (operationId == (int)OperationId.InvalidateCache)
+ {
+ Result rc = BaseFile.OperateRange(Span.Empty, OperationId.InvalidateCache, offset, size,
+ ReadOnlySpan.Empty);
+ if (rc.IsFailure()) return rc;
+ }
+ else if (operationId == (int)OperationId.QueryRange)
+ {
+ Unsafe.SkipInit(out QueryRangeInfo info);
+
+ Result rc = BaseFile.OperateRange(SpanHelpers.AsByteSpan(ref info), OperationId.QueryRange, offset, size,
+ ReadOnlySpan.Empty);
+ if (rc.IsFailure()) return rc;
+
+ rangeInfo.Merge(in info);
+ }
+
+ return Result.Success;
+ }
+
+ public void Dispose()
+ {
+ BaseFile?.Dispose();
+ ParentFs?.Dispose();
+ }
+ }
+}
diff --git a/src/LibHac/FsSrv/Impl/FileSystemInterfaceAdapter.cs b/src/LibHac/FsSrv/Impl/FileSystemInterfaceAdapter.cs
new file mode 100644
index 00000000..f4580a0d
--- /dev/null
+++ b/src/LibHac/FsSrv/Impl/FileSystemInterfaceAdapter.cs
@@ -0,0 +1,288 @@
+using System;
+using System.Runtime.CompilerServices;
+using LibHac.Common;
+using LibHac.Fs;
+using LibHac.Fs.Fsa;
+using LibHac.FsSrv.Sf;
+using LibHac.Util;
+
+namespace LibHac.FsSrv.Impl
+{
+ internal class FileSystemInterfaceAdapter : IFileSystemSf
+ {
+ private ReferenceCountedDisposable BaseFileSystem { get; }
+ private bool IsHostFsRoot { get; }
+
+ // In FS, FileSystemInterfaceAdapter is derived from ISharedObject, so that's used for ref-counting when
+ // creating files and directories. We don't have an ISharedObject, so a self-reference is used instead.
+ private ReferenceCountedDisposable.WeakReference _selfReference;
+
+ ///
+ /// Initializes a new by creating
+ /// a new reference to .
+ ///
+ /// The base file system.
+ /// Does the base file system come from the root directory of a host file system?
+ private FileSystemInterfaceAdapter(ReferenceCountedDisposable fileSystem,
+ bool isHostFsRoot = false)
+ {
+ BaseFileSystem = fileSystem.AddReference();
+ IsHostFsRoot = isHostFsRoot;
+ }
+
+ ///
+ /// Initializes a new by moving the file system object.
+ /// Avoids allocations from incrementing and then decrementing the ref-count.
+ ///
+ /// The base file system. Will be null upon returning.
+ /// Does the base file system come from the root directory of a host file system?
+ private FileSystemInterfaceAdapter(ref ReferenceCountedDisposable fileSystem,
+ bool isHostFsRoot = false)
+ {
+ BaseFileSystem = fileSystem;
+ fileSystem = null;
+ IsHostFsRoot = isHostFsRoot;
+ }
+
+ ///
+ /// Initializes a new , creating a copy of the input file system object.
+ ///
+ /// The base file system.
+ /// Does the base file system come from the root directory of a host file system?
+ public static ReferenceCountedDisposable CreateShared(
+ ReferenceCountedDisposable baseFileSystem, bool isHostFsRoot = false)
+ {
+ var adapter = new FileSystemInterfaceAdapter(baseFileSystem, isHostFsRoot);
+
+ return ReferenceCountedDisposable.Create(adapter, out adapter._selfReference);
+ }
+
+ ///
+ /// Initializes a new cast to an
+ /// by moving the input file system object. Avoids allocations from incrementing and then decrementing the ref-count.
+ ///
+ /// The base file system. Will be null upon returning.
+ /// Does the base file system come from the root directory of a host file system?
+ public static ReferenceCountedDisposable CreateSharedSfFileSystem(
+ ref ReferenceCountedDisposable baseFileSystem, bool isHostFsRoot = false)
+ {
+ var adapter = new FileSystemInterfaceAdapter(ref baseFileSystem, isHostFsRoot);
+
+ return ReferenceCountedDisposable.Create(adapter, out adapter._selfReference);
+ }
+
+ private static ReadOnlySpan RootDir => new[] { (byte)'/' };
+
+ public Result GetImpl(out ReferenceCountedDisposable fileSystem)
+ {
+ fileSystem = BaseFileSystem.AddReference();
+ return Result.Success;
+ }
+
+ public Result CreateFile(in Path path, long size, int option)
+ {
+ if (size < 0)
+ return ResultFs.InvalidSize.Log();
+
+ var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
+ if (normalizer.Result.IsFailure()) return normalizer.Result;
+
+ return BaseFileSystem.Target.CreateFile(normalizer.Path, size, (CreateFileOptions)option);
+ }
+
+ public Result DeleteFile(in Path path)
+ {
+ var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
+ if (normalizer.Result.IsFailure()) return normalizer.Result;
+
+ return BaseFileSystem.Target.DeleteFile(normalizer.Path);
+ }
+
+ public Result CreateDirectory(in Path path)
+ {
+ var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
+ if (normalizer.Result.IsFailure()) return normalizer.Result;
+
+ if (StringUtils.Compare(RootDir, normalizer.Path) != 0)
+ return ResultFs.PathAlreadyExists.Log();
+
+ return BaseFileSystem.Target.CreateDirectory(normalizer.Path);
+ }
+
+ public Result DeleteDirectory(in Path path)
+ {
+ var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
+ if (normalizer.Result.IsFailure()) return normalizer.Result;
+
+ if (StringUtils.Compare(RootDir, normalizer.Path) != 0)
+ return ResultFs.DirectoryNotDeletable.Log();
+
+ return BaseFileSystem.Target.DeleteDirectory(normalizer.Path);
+ }
+
+ public Result DeleteDirectoryRecursively(in Path path)
+ {
+ var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
+ if (normalizer.Result.IsFailure()) return normalizer.Result;
+
+ if (StringUtils.Compare(RootDir, normalizer.Path) != 0)
+ return ResultFs.DirectoryNotDeletable.Log();
+
+ return BaseFileSystem.Target.DeleteDirectoryRecursively(normalizer.Path);
+ }
+
+ public Result RenameFile(in Path oldPath, in Path newPath)
+ {
+ var normalizerOldPath = new PathNormalizer(new U8Span(oldPath.Str), GetPathNormalizerOption());
+ if (normalizerOldPath.Result.IsFailure()) return normalizerOldPath.Result;
+
+ var normalizerNewPath = new PathNormalizer(new U8Span(newPath.Str), GetPathNormalizerOption());
+ if (normalizerNewPath.Result.IsFailure()) return normalizerNewPath.Result;
+
+ return BaseFileSystem.Target.RenameFile(new U8Span(normalizerOldPath.Path),
+ new U8Span(normalizerNewPath.Path));
+ }
+
+ public Result RenameDirectory(in Path oldPath, in Path newPath)
+ {
+ var normalizerOldPath = new PathNormalizer(new U8Span(oldPath.Str), GetPathNormalizerOption());
+ if (normalizerOldPath.Result.IsFailure()) return normalizerOldPath.Result;
+
+ var normalizerNewPath = new PathNormalizer(new U8Span(newPath.Str), GetPathNormalizerOption());
+ if (normalizerNewPath.Result.IsFailure()) return normalizerNewPath.Result;
+
+ if (PathTool.IsSubpath(normalizerOldPath.Path, normalizerNewPath.Path))
+ return ResultFs.DirectoryNotRenamable.Log();
+
+ return BaseFileSystem.Target.RenameDirectory(normalizerOldPath.Path, normalizerNewPath.Path);
+ }
+
+ public Result GetEntryType(out uint entryType, in Path path)
+ {
+ entryType = default;
+
+ var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
+ if (normalizer.Result.IsFailure()) return normalizer.Result;
+
+ ref DirectoryEntryType type = ref Unsafe.As(ref entryType);
+
+ return BaseFileSystem.Target.GetEntryType(out type, new U8Span(normalizer.Path));
+ }
+
+ public Result OpenFile(out ReferenceCountedDisposable file, in Path path, uint mode)
+ {
+ const int maxTryCount = 2;
+ file = default;
+
+ var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
+ if (normalizer.Result.IsFailure()) return normalizer.Result;
+
+ Result rc = Result.Success;
+ IFile fileInterface = null;
+
+ for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
+ {
+ rc = BaseFileSystem.Target.OpenFile(out fileInterface, new U8Span(normalizer.Path), (OpenMode)mode);
+
+ // Retry on ResultDataCorrupted
+ if (!ResultFs.DataCorrupted.Includes(rc))
+ break;
+ }
+
+ if (rc.IsFailure()) return rc;
+
+ ReferenceCountedDisposable selfReference = _selfReference.TryAddReference();
+ var adapter = new FileInterfaceAdapter(fileInterface, ref selfReference);
+ file = new ReferenceCountedDisposable(adapter);
+
+ return Result.Success;
+ }
+
+ public Result OpenDirectory(out ReferenceCountedDisposable directory, in Path path, uint mode)
+ {
+ const int maxTryCount = 2;
+ directory = default;
+
+ var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
+ if (normalizer.Result.IsFailure()) return normalizer.Result;
+
+ Result rc = Result.Success;
+ IDirectory dirInterface = null;
+
+ for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
+ {
+ rc = BaseFileSystem.Target.OpenDirectory(out dirInterface, new U8Span(normalizer.Path), (OpenDirectoryMode)mode);
+
+ // Retry on ResultDataCorrupted
+ if (!ResultFs.DataCorrupted.Includes(rc))
+ break;
+ }
+
+ if (rc.IsFailure()) return rc;
+
+ ReferenceCountedDisposable selfReference = _selfReference.TryAddReference();
+ var adapter = new DirectoryInterfaceAdapter(dirInterface, ref selfReference);
+ directory = new ReferenceCountedDisposable(adapter);
+
+ return Result.Success;
+ }
+
+ public Result Commit()
+ {
+ return BaseFileSystem.Target.Commit();
+ }
+
+ public Result GetFreeSpaceSize(out long freeSpace, in Path path)
+ {
+ freeSpace = default;
+
+ var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
+ if (normalizer.Result.IsFailure()) return normalizer.Result;
+
+ return BaseFileSystem.Target.GetFreeSpaceSize(out freeSpace, normalizer.Path);
+ }
+
+ public Result GetTotalSpaceSize(out long totalSpace, in Path path)
+ {
+ totalSpace = default;
+
+ var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
+ if (normalizer.Result.IsFailure()) return normalizer.Result;
+
+ return BaseFileSystem.Target.GetTotalSpaceSize(out totalSpace, normalizer.Path);
+ }
+
+ public Result CleanDirectoryRecursively(in Path path)
+ {
+ var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
+ if (normalizer.Result.IsFailure()) return normalizer.Result;
+
+ return BaseFileSystem.Target.CleanDirectoryRecursively(normalizer.Path);
+ }
+
+ public Result GetFileTimeStampRaw(out FileTimeStampRaw timeStamp, in Path path)
+ {
+ timeStamp = default;
+
+ var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
+ if (normalizer.Result.IsFailure()) return normalizer.Result;
+
+ return BaseFileSystem.Target.GetFileTimeStampRaw(out timeStamp, normalizer.Path);
+ }
+
+ public Result QueryEntry(Span outBuffer, ReadOnlySpan inBuffer, int queryId, in Path path)
+ {
+ return BaseFileSystem.Target.QueryEntry(outBuffer, inBuffer, (QueryId)queryId, new U8Span(path.Str));
+ }
+
+ public void Dispose()
+ {
+ BaseFileSystem?.Dispose();
+ }
+
+ private PathNormalizer.Option GetPathNormalizerOption()
+ {
+ return IsHostFsRoot ? PathNormalizer.Option.PreserveUnc : PathNormalizer.Option.None;
+ }
+ }
+}
diff --git a/src/LibHac/FsSrv/Impl/StorageInterfaceAdapter.cs b/src/LibHac/FsSrv/Impl/StorageInterfaceAdapter.cs
new file mode 100644
index 00000000..8c8c3528
--- /dev/null
+++ b/src/LibHac/FsSrv/Impl/StorageInterfaceAdapter.cs
@@ -0,0 +1,101 @@
+using System;
+using System.Runtime.CompilerServices;
+using LibHac.Common;
+using LibHac.Fs;
+using LibHac.FsSrv.Sf;
+
+namespace LibHac.FsSrv.Impl
+{
+ internal class StorageInterfaceAdapter : IStorageSf
+ {
+ private ReferenceCountedDisposable BaseStorage { get; }
+
+ public StorageInterfaceAdapter(ReferenceCountedDisposable baseStorage)
+ {
+ BaseStorage = baseStorage.AddReference();
+ }
+
+ public Result Read(long offset, Span destination)
+ {
+ const int maxTryCount = 2;
+
+ if (offset < 0)
+ return ResultFs.InvalidOffset.Log();
+
+ if (destination.Length < 0)
+ return ResultFs.InvalidSize.Log();
+
+ Result rc = Result.Success;
+
+ for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
+ {
+ rc = BaseStorage.Target.Read(offset, destination);
+
+ // Retry on ResultDataCorrupted
+ if (!ResultFs.DataCorrupted.Includes(rc))
+ break;
+ }
+
+ return rc;
+ }
+
+ public Result Write(long offset, ReadOnlySpan source)
+ {
+ if (offset < 0)
+ return ResultFs.InvalidOffset.Log();
+
+ if (source.Length < 0)
+ return ResultFs.InvalidSize.Log();
+
+ // Note: Thread priority is temporarily when writing in FS
+
+ return BaseStorage.Target.Write(offset, source);
+ }
+
+ public Result Flush()
+ {
+ return BaseStorage.Target.Flush();
+ }
+
+ public Result SetSize(long size)
+ {
+ if (size < 0)
+ return ResultFs.InvalidSize.Log();
+
+ return BaseStorage.Target.SetSize(size);
+ }
+
+ public Result GetSize(out long size)
+ {
+ return BaseStorage.Target.GetSize(out size);
+ }
+
+ public Result OperateRange(out QueryRangeInfo rangeInfo, int operationId, long offset, long size)
+ {
+ rangeInfo = new QueryRangeInfo();
+
+ if (operationId == (int)OperationId.InvalidateCache)
+ {
+ Result rc = BaseStorage.Target.OperateRange(Span.Empty, OperationId.InvalidateCache, offset, size,
+ ReadOnlySpan.Empty);
+ if (rc.IsFailure()) return rc;
+ }
+ else if (operationId == (int)OperationId.QueryRange)
+ {
+ Unsafe.SkipInit(out QueryRangeInfo info);
+
+ Result rc = BaseStorage.Target.OperateRange(SpanHelpers.AsByteSpan(ref info), OperationId.QueryRange,
+ offset, size, ReadOnlySpan.Empty);
+ if (rc.IsFailure()) return rc;
+
+ rangeInfo.Merge(in info);
+ }
+
+ return Result.Success;
+ }
+ public void Dispose()
+ {
+ BaseStorage?.Dispose();
+ }
+ }
+}
diff --git a/src/LibHac/FsSrv/NcaFileSystemService.cs b/src/LibHac/FsSrv/NcaFileSystemService.cs
index 2b3f04d2..7acaa93e 100644
--- a/src/LibHac/FsSrv/NcaFileSystemService.cs
+++ b/src/LibHac/FsSrv/NcaFileSystemService.cs
@@ -22,7 +22,7 @@ namespace LibHac.FsSrv
private SemaphoreAdaptor AocMountCountSemaphore { get; }
private SemaphoreAdaptor RomMountCountSemaphore { get; }
- public NcaFileSystemService(NcaFileSystemServiceImpl serviceImpl, ulong processId)
+ private NcaFileSystemService(NcaFileSystemServiceImpl serviceImpl, ulong processId)
{
ServiceImpl = serviceImpl;
ProcessId = processId;
@@ -30,19 +30,33 @@ namespace LibHac.FsSrv
RomMountCountSemaphore = new SemaphoreAdaptor(RomSemaphoreCount, RomSemaphoreCount);
}
- public Result OpenFileSystemWithPatch(out ReferenceCountedDisposable fileSystem,
+ public static ReferenceCountedDisposable Create(NcaFileSystemServiceImpl serviceImpl,
+ ulong processId)
+ {
+ // Create the service
+ var ncaService = new NcaFileSystemService(serviceImpl, processId);
+
+ // Wrap the service in a ref-counter and give the service a weak self-reference
+ var sharedService = new ReferenceCountedDisposable(ncaService);
+ ncaService.SelfReference =
+ new ReferenceCountedDisposable.WeakReference(sharedService);
+
+ return sharedService;
+ }
+
+ public Result OpenFileSystemWithPatch(out ReferenceCountedDisposable fileSystem,
ProgramId programId, FileSystemProxyType fsType)
{
throw new NotImplementedException();
}
- public Result OpenCodeFileSystem(out ReferenceCountedDisposable fileSystem,
+ public Result OpenCodeFileSystem(out ReferenceCountedDisposable fileSystem,
out CodeVerificationData verificationData, in FspPath path, ProgramId programId)
{
throw new NotImplementedException();
}
- public Result OpenDataFileSystemByCurrentProcess(out ReferenceCountedDisposable fileSystem)
+ public Result OpenDataFileSystemByCurrentProcess(out ReferenceCountedDisposable fileSystem)
{
throw new NotImplementedException();
}
@@ -53,17 +67,17 @@ namespace LibHac.FsSrv
throw new NotImplementedException();
}
- public Result OpenDataStorageByCurrentProcess(out ReferenceCountedDisposable storage)
+ public Result OpenDataStorageByCurrentProcess(out ReferenceCountedDisposable storage)
{
throw new NotImplementedException();
}
- public Result OpenDataStorageByProgramId(out ReferenceCountedDisposable storage, ProgramId programId)
+ public Result OpenDataStorageByProgramId(out ReferenceCountedDisposable storage, ProgramId programId)
{
throw new NotImplementedException();
}
- public Result OpenFileSystemWithId(out ReferenceCountedDisposable fileSystem, in FspPath path,
+ public Result OpenFileSystemWithId(out ReferenceCountedDisposable fileSystem, in FspPath path,
ulong id, FileSystemProxyType fsType)
{
fileSystem = default;
@@ -117,33 +131,44 @@ namespace LibHac.FsSrv
var normalizer = new PathNormalizer(path, GetPathNormalizerOptions(path));
if (normalizer.Result.IsFailure()) return normalizer.Result;
- rc = ServiceImpl.OpenFileSystem(out ReferenceCountedDisposable baseFs, out _, path, fsType,
- canMountSystemDataPrivate, id);
- if (rc.IsFailure()) return rc;
+ ReferenceCountedDisposable fs = null;
- fileSystem = baseFs;
- return Result.Success;
+ try
+ {
+ rc = ServiceImpl.OpenFileSystem(out fs, out _, path, fsType,
+ canMountSystemDataPrivate, id);
+ if (rc.IsFailure()) return rc;
+
+ // Create an SF adapter for the file system
+ fileSystem = FileSystemInterfaceAdapter.CreateSharedSfFileSystem(ref fs);
+
+ return Result.Success;
+ }
+ finally
+ {
+ fs?.Dispose();
+ }
}
- public Result OpenDataFileSystemByProgramId(out ReferenceCountedDisposable fileSystem,
+ public Result OpenDataFileSystemByProgramId(out ReferenceCountedDisposable fileSystem,
ProgramId programId)
{
throw new NotImplementedException();
}
- public Result OpenDataStorageByDataId(out ReferenceCountedDisposable storage, DataId dataId,
+ public Result OpenDataStorageByDataId(out ReferenceCountedDisposable storage, DataId dataId,
StorageId storageId)
{
throw new NotImplementedException();
}
- public Result OpenDataFileSystemWithProgramIndex(out ReferenceCountedDisposable fileSystem,
+ public Result OpenDataFileSystemWithProgramIndex(out ReferenceCountedDisposable fileSystem,
byte programIndex)
{
throw new NotImplementedException();
}
- public Result OpenDataStorageWithProgramIndex(out ReferenceCountedDisposable storage,
+ public Result OpenDataStorageWithProgramIndex(out ReferenceCountedDisposable storage,
byte programIndex)
{
throw new NotImplementedException();
@@ -165,7 +190,7 @@ namespace LibHac.FsSrv
throw new NotImplementedException();
}
- public Result OpenContentStorageFileSystem(out ReferenceCountedDisposable fileSystem,
+ public Result OpenContentStorageFileSystem(out ReferenceCountedDisposable fileSystem,
ContentStorageId contentStorageId)
{
fileSystem = default;
@@ -179,12 +204,22 @@ namespace LibHac.FsSrv
if (!accessibility.CanRead || !accessibility.CanWrite)
return ResultFs.PermissionDenied.Log();
- rc = ServiceImpl.OpenContentStorageFileSystem(out ReferenceCountedDisposable contentFs,
- contentStorageId);
- if (rc.IsFailure()) return rc;
+ ReferenceCountedDisposable fs = null;
- fileSystem = contentFs;
- return Result.Success;
+ try
+ {
+ rc = ServiceImpl.OpenContentStorageFileSystem(out fs, contentStorageId);
+ if (rc.IsFailure()) return rc;
+
+ // Create an SF adapter for the file system
+ fileSystem = FileSystemInterfaceAdapter.CreateSharedSfFileSystem(ref fs);
+
+ return Result.Success;
+ }
+ finally
+ {
+ fs?.Dispose();
+ }
}
public Result RegisterExternalKey(in RightsId rightsId, in AccessKey accessKey)
@@ -225,7 +260,7 @@ namespace LibHac.FsSrv
throw new NotImplementedException();
}
- public Result OpenRegisteredUpdatePartition(out ReferenceCountedDisposable fileSystem)
+ public Result OpenRegisteredUpdatePartition(out ReferenceCountedDisposable fileSystem)
{
throw new NotImplementedException();
}
@@ -282,11 +317,6 @@ namespace LibHac.FsSrv
return OpenDataStorageCore(out storage, out ncaHeaderDigest, id, storageId);
}
- internal void SetSelfReference(ReferenceCountedDisposable reference)
- {
- SelfReference = new ReferenceCountedDisposable.WeakReference(reference);
- }
-
private Result TryAcquireAddOnContentOpenCountSemaphore(out IUniqueLock semaphoreLock)
{
throw new NotImplementedException();
diff --git a/src/LibHac/FsSrv/Sf/IDirectory.cs b/src/LibHac/FsSrv/Sf/IDirectory.cs
new file mode 100644
index 00000000..a4db8748
--- /dev/null
+++ b/src/LibHac/FsSrv/Sf/IDirectory.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace LibHac.FsSrv.Sf
+{
+ public interface IDirectorySf : IDisposable
+ {
+ Result Read(out long entriesRead, Span entryBuffer);
+ Result GetEntryCount(out long entryCount);
+ }
+}
diff --git a/src/LibHac/FsSrv/Sf/IFile.cs b/src/LibHac/FsSrv/Sf/IFile.cs
new file mode 100644
index 00000000..8142d558
--- /dev/null
+++ b/src/LibHac/FsSrv/Sf/IFile.cs
@@ -0,0 +1,15 @@
+using System;
+using LibHac.Fs;
+
+namespace LibHac.FsSrv.Sf
+{
+ public interface IFileSf : IDisposable
+ {
+ Result Read(out long bytesRead, long offset, Span destination, ReadOption option);
+ Result Write(long offset, ReadOnlySpan source, WriteOption option);
+ Result Flush();
+ Result SetSize(long size);
+ Result GetSize(out long size);
+ Result OperateRange(out QueryRangeInfo rangeInfo, int operationId, long offset, long size);
+ }
+}
diff --git a/src/LibHac/FsSrv/Sf/IFileSystem.cs b/src/LibHac/FsSrv/Sf/IFileSystem.cs
new file mode 100644
index 00000000..22d7cfc5
--- /dev/null
+++ b/src/LibHac/FsSrv/Sf/IFileSystem.cs
@@ -0,0 +1,27 @@
+using System;
+using LibHac.Fs;
+using LibHac.Fs.Fsa;
+
+namespace LibHac.FsSrv.Sf
+{
+ public interface IFileSystemSf : IDisposable
+ {
+ Result GetImpl(out ReferenceCountedDisposable fileSystem);
+ Result CreateFile(in Path path, long size, int option);
+ Result DeleteFile(in Path path);
+ Result CreateDirectory(in Path path);
+ Result DeleteDirectory(in Path path);
+ Result DeleteDirectoryRecursively(in Path path);
+ Result RenameFile(in Path oldPath, in Path newPath);
+ Result RenameDirectory(in Path oldPath, in Path newPath);
+ Result GetEntryType(out uint entryType, in Path path);
+ Result OpenFile(out ReferenceCountedDisposable file, in Path path, uint mode);
+ Result OpenDirectory(out ReferenceCountedDisposable directory, in Path path, uint mode);
+ Result Commit();
+ Result GetFreeSpaceSize(out long freeSpace, in Path path);
+ Result GetTotalSpaceSize(out long totalSpace, in Path path);
+ Result CleanDirectoryRecursively(in Path path);
+ Result GetFileTimeStampRaw(out FileTimeStampRaw timeStamp, in Path path);
+ Result QueryEntry(Span outBuffer, ReadOnlySpan inBuffer, int queryId, in Path path);
+ }
+}
diff --git a/src/LibHac/FsSrv/Sf/IFileSystemProxyForLoader.cs b/src/LibHac/FsSrv/Sf/IFileSystemProxyForLoader.cs
index d4b57d17..240b30b0 100644
--- a/src/LibHac/FsSrv/Sf/IFileSystemProxyForLoader.cs
+++ b/src/LibHac/FsSrv/Sf/IFileSystemProxyForLoader.cs
@@ -1,12 +1,11 @@
using LibHac.Fs;
-using LibHac.Fs.Fsa;
using LibHac.Ncm;
namespace LibHac.FsSrv.Sf
{
public interface IFileSystemProxyForLoader
{
- Result OpenCodeFileSystem(out ReferenceCountedDisposable fileSystem,
+ Result OpenCodeFileSystem(out ReferenceCountedDisposable fileSystem,
out CodeVerificationData verificationData, in FspPath path, ProgramId programId);
Result IsArchivedProgram(out bool isArchived, ulong processId);
diff --git a/src/LibHac/FsSrv/Sf/IStorage.cs b/src/LibHac/FsSrv/Sf/IStorage.cs
new file mode 100644
index 00000000..74edb7c8
--- /dev/null
+++ b/src/LibHac/FsSrv/Sf/IStorage.cs
@@ -0,0 +1,15 @@
+using System;
+using LibHac.Fs;
+
+namespace LibHac.FsSrv.Sf
+{
+ public interface IStorageSf : IDisposable
+ {
+ Result Read(long offset, Span destination);
+ Result Write(long offset, ReadOnlySpan source);
+ Result Flush();
+ Result SetSize(long size);
+ Result GetSize(out long size);
+ Result OperateRange(out QueryRangeInfo rangeInfo, int operationId, long offset, long size);
+ }
+}
diff --git a/src/LibHac/FsSrv/Sf/IWiper.cs b/src/LibHac/FsSrv/Sf/IWiper.cs
index 08b550b4..b19b0a7b 100644
--- a/src/LibHac/FsSrv/Sf/IWiper.cs
+++ b/src/LibHac/FsSrv/Sf/IWiper.cs
@@ -1,6 +1,8 @@
-namespace LibHac.FsSrv.Sf
+using System;
+
+namespace LibHac.FsSrv.Sf
{
- public interface IWiper
+ public interface IWiper : IDisposable
{
public Result Startup(out long spaceToWipe);
public Result Process(out long remainingSpaceToWipe);
diff --git a/src/LibHac/FsSrv/Sf/Path.cs b/src/LibHac/FsSrv/Sf/Path.cs
new file mode 100644
index 00000000..c714a1d7
--- /dev/null
+++ b/src/LibHac/FsSrv/Sf/Path.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using LibHac.Common;
+using LibHac.Fs;
+
+namespace LibHac.FsSrv.Sf
+{
+ [StructLayout(LayoutKind.Sequential, Size = PathTool.EntryNameLengthMax + 1)]
+ public readonly struct Path
+ {
+#if DEBUG
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding000;
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding100;
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding200;
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly byte Padding300;
+#endif
+
+ public ReadOnlySpan Str => SpanHelpers.AsReadOnlyByteSpan(in this);
+ }
+}
diff --git a/src/LibHac/FsSrv/Storage/IGameCardDeviceManager.cs b/src/LibHac/FsSrv/Storage/IGameCardDeviceManager.cs
new file mode 100644
index 00000000..f2bf4720
--- /dev/null
+++ b/src/LibHac/FsSrv/Storage/IGameCardDeviceManager.cs
@@ -0,0 +1,15 @@
+using System;
+using LibHac.Fs.Impl;
+
+namespace LibHac.FsSrv.Storage
+{
+ internal interface IGameCardDeviceManager
+ {
+ Result AcquireReadLock(out UniqueLock locker, uint handle);
+ Result AcquireReadLockSecureMode(out UniqueLock locker, ref uint handle, ReadOnlySpan cardDeviceId, ReadOnlySpan cardImageHash);
+ Result AcquireWriteLock(out SharedLock locker);
+ Result HandleGameCardAccessResult(Result result);
+ Result GetHandle(out uint handle);
+ bool IsSecureMode();
+ }
+}
diff --git a/src/LibHac/FsSrv/Storage/IGameCardKeyManager.cs b/src/LibHac/FsSrv/Storage/IGameCardKeyManager.cs
new file mode 100644
index 00000000..3c9b507b
--- /dev/null
+++ b/src/LibHac/FsSrv/Storage/IGameCardKeyManager.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace LibHac.FsSrv.Storage
+{
+ public interface IGameCardKeyManager
+ {
+ void PresetInternalKeys(ReadOnlySpan gameCardKey, ReadOnlySpan gameCardCertificate);
+ }
+}
diff --git a/src/LibHac/FsSrv/Storage/ISdmmcDeviceManager.cs b/src/LibHac/FsSrv/Storage/ISdmmcDeviceManager.cs
new file mode 100644
index 00000000..dfa07375
--- /dev/null
+++ b/src/LibHac/FsSrv/Storage/ISdmmcDeviceManager.cs
@@ -0,0 +1,12 @@
+using LibHac.Fs;
+
+namespace LibHac.FsSrv.Storage
+{
+ internal interface ISdmmcDeviceManager
+ {
+ Result Lock(out object locker, uint handle);
+ IStorage GetStorage();
+ SdmmcPort GetPortId();
+ Result NotifyCloseStorageDevice(uint handle);
+ }
+}
diff --git a/src/LibHac/FsSrv/Storage/Sf/IStorageDevice.cs b/src/LibHac/FsSrv/Storage/Sf/IStorageDevice.cs
new file mode 100644
index 00000000..b133fc87
--- /dev/null
+++ b/src/LibHac/FsSrv/Storage/Sf/IStorageDevice.cs
@@ -0,0 +1,18 @@
+using System;
+using LibHac.Fs;
+
+namespace LibHac.FsSrv.Storage.Sf
+{
+ public interface IStorageDevice : IDisposable
+ {
+ Result GetHandle(out uint handle);
+ Result IsHandleValid(out bool isValid);
+ Result OpenOperator(out ReferenceCountedDisposable deviceOperator);
+ Result Read(long offset, Span destination);
+ Result Write(long offset, ReadOnlySpan source);
+ Result Flush();
+ Result SetSize(long size);
+ Result GetSize(out long size);
+ Result OperateRange(out QueryRangeInfo rangeInfo, int operationId, long offset, long size);
+ }
+}
diff --git a/src/LibHac/FsSrv/Storage/Sf/IStorageDeviceManager.cs b/src/LibHac/FsSrv/Storage/Sf/IStorageDeviceManager.cs
new file mode 100644
index 00000000..210fcfe4
--- /dev/null
+++ b/src/LibHac/FsSrv/Storage/Sf/IStorageDeviceManager.cs
@@ -0,0 +1,21 @@
+using System;
+using LibHac.Fs;
+using LibHac.FsSrv.Sf;
+
+namespace LibHac.FsSrv.Storage.Sf
+{
+ public interface IStorageDeviceManager : IDisposable
+ {
+ Result IsInserted(out bool isInserted);
+ Result IsHandleValid(out bool isValid, uint handle);
+ Result OpenDetectionEvent(out ReferenceCountedDisposable eventNotifier);
+ Result OpenOperator(out ReferenceCountedDisposable deviceOperator);
+ Result OpenDevice(out ReferenceCountedDisposable storageDevice, ulong attribute);
+ Result OpenStorage(out ReferenceCountedDisposable storage, ulong attribute);
+ Result PutToSleep();
+ Result Awaken();
+ Result Initialize();
+ Result Shutdown();
+ Result Invalidate();
+ }
+}
diff --git a/src/LibHac/FsSrv/Storage/Sf/IStorageDeviceOperator.cs b/src/LibHac/FsSrv/Storage/Sf/IStorageDeviceOperator.cs
new file mode 100644
index 00000000..33a4e449
--- /dev/null
+++ b/src/LibHac/FsSrv/Storage/Sf/IStorageDeviceOperator.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace LibHac.FsSrv.Storage.Sf
+{
+ public interface IStorageDeviceOperator : IDisposable
+ {
+ Result Operate(uint operationId);
+ Result OperateIn(ReadOnlySpan buffer, long offset, long size, uint operationId);
+ Result OperateOut(out long bytesWritten, Span buffer, long offset, long size, uint operationId);
+ Result OperateOut2(out long bytesWrittenBuffer1, Span buffer1, out long bytesWrittenBuffer2, Span buffer2, uint operationId);
+ Result OperateInOut(out long bytesWritten, Span outBuffer, ReadOnlySpan inBuffer, long offset, long size, uint operationId);
+ Result OperateIn2Out(out long bytesWritten, Span