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 outBuffer, ReadOnlySpan inBuffer1, ReadOnlySpan inBuffer2, long offset, long size, uint operationId); + } +} diff --git a/src/LibHac/FsSystem/LocalFileSystem.cs b/src/LibHac/FsSystem/LocalFileSystem.cs index 12906dfb..24664610 100644 --- a/src/LibHac/FsSystem/LocalFileSystem.cs +++ b/src/LibHac/FsSystem/LocalFileSystem.cs @@ -61,7 +61,7 @@ namespace LibHac.FsSystem if (PathTool.IsSubpath(normalizedPath1, normalizedPath2)) { - return ResultFs.DestinationIsSubPathOfSource.Log(); + return ResultFs.DirectoryNotRenamable.Log(); } return Result.Success; diff --git a/src/LibHac/FsSystem/Save/HierarchicalSaveFileTable.cs b/src/LibHac/FsSystem/Save/HierarchicalSaveFileTable.cs index 31a4764d..a79ee565 100644 --- a/src/LibHac/FsSystem/Save/HierarchicalSaveFileTable.cs +++ b/src/LibHac/FsSystem/Save/HierarchicalSaveFileTable.cs @@ -347,7 +347,7 @@ namespace LibHac.FsSystem.Save if (PathTools.IsSubPath(srcPath, dstPath)) { - return ResultFs.DestinationIsSubPathOfSource.Log(); + return ResultFs.DirectoryNotRenamable.Log(); } if (oldKey.Parent != newKey.Parent) diff --git a/src/LibHac/ReferenceCountedDisposable.cs b/src/LibHac/ReferenceCountedDisposable.cs index 4e820194..bd9beba4 100644 --- a/src/LibHac/ReferenceCountedDisposable.cs +++ b/src/LibHac/ReferenceCountedDisposable.cs @@ -8,6 +8,7 @@ #nullable enable using System; +using System.Diagnostics; using System.Runtime.CompilerServices; namespace LibHac @@ -26,14 +27,14 @@ namespace LibHac /// released through either of the following actions: /// /// - /// The reference is explicitly released by a call to . + /// The reference is explicitly released by a call to . /// The reference is no longer in use by managed code and gets reclaimed by the garbage collector. /// /// /// While each instance of should be explicitly disposed when /// the object is no longer needed by the code owning the reference, this implementation will not leak resources /// in the event one or more callers fail to do so. When all references to an object are explicitly released - /// (i.e. by calling ), the target object will itself be deterministically released by a + /// (i.e. by calling ), the target object will itself be deterministically released by a /// call to when the last reference to it is released. However, in the event /// one or more references is not explicitly released, the underlying object will still become eligible for /// non-deterministic release (i.e. finalization) as soon as each reference to it is released by one of the @@ -65,7 +66,7 @@ namespace LibHac /// /// /// This value is only cleared in order to support cases where one or more references is garbage - /// collected without having called. + /// collected without having called. /// private T? _instance; @@ -110,16 +111,37 @@ namespace LibHac /// Gets the target object. /// /// - /// This call is not valid after is called. If this property or the target - /// object is used concurrently with a call to , it is possible for the code to be + /// This call is not valid after is called. If this property or the target + /// object is used concurrently with a call to , it is possible for the code to be /// using a disposed object. After the current instance is disposed, this property throws /// . However, the exact time when this property starts throwing after - /// is called is unspecified; code is expected to not use this property or the object - /// it returns after any code invokes . + /// is called is unspecified; code is expected to not use this property or the object + /// it returns after any code invokes . /// /// The target object. public T Target => _instance ?? throw new ObjectDisposedException(nameof(ReferenceCountedDisposable)); + /// + /// Initializes a new reference counting wrapper around an object. + /// Returns a reference counting wrapper of the base type of the input object, and creates a + /// of the object's derived type. + /// + /// The derived type of the object to be wrapped. + /// The object owned by this wrapper. + /// The weak reference of the derived type. + /// + public static ReferenceCountedDisposable Create(TDerived instance, + out ReferenceCountedDisposable.WeakReference derivedWeakReference) + where TDerived : class, IDisposable, T + { + var baseStrongRef = new ReferenceCountedDisposable(instance); + + derivedWeakReference = + new ReferenceCountedDisposable.WeakReference(instance, baseStrongRef._boxedReferenceCount); + + return baseStrongRef; + } + /// /// Increments the reference count for the disposable object, and returns a new disposable reference to it. /// @@ -272,6 +294,11 @@ namespace LibHac } } + // Print info on non-disposed references in debug mode +#if DEBUG + ~ReferenceCountedDisposable() => Dispose(false); +#endif + /// /// Releases the current reference, causing the underlying object to be disposed if this was the last /// reference. @@ -283,26 +310,42 @@ namespace LibHac /// public void Dispose() { - T? instanceToDispose = null; - lock (_boxedReferenceCount) + Dispose(true); + +#if DEBUG + GC.SuppressFinalize(this); +#endif + } + + private void Dispose(bool disposing) + { + if (disposing) { - if (_instance == null) + T? instanceToDispose = null; + lock (_boxedReferenceCount) { - // Already disposed; allow multiple without error. - return; + if (_instance == null) + { + // Already disposed; allow multiple without error. + return; + } + + _boxedReferenceCount.Value--; + if (_boxedReferenceCount.Value == 0) + { + instanceToDispose = _instance; + } + + // Ensure multiple calls to Dispose for this instance are a NOP. + _instance = null; } - _boxedReferenceCount.Value--; - if (_boxedReferenceCount.Value == 0) - { - instanceToDispose = _instance; - } - - // Ensure multiple calls to Dispose for this instance are a NOP. - _instance = null; + instanceToDispose?.Dispose(); + } + else + { + Trace.WriteLine($"Failed to dispose object with type {GetType().FullName}."); } - - instanceToDispose?.Dispose(); } /// @@ -349,6 +392,24 @@ namespace LibHac _boxedReferenceCount = referenceCount; } + /// + /// + /// + /// + /// + internal WeakReference(T instance, StrongBox referenceCount) + { + // This constructor is meant for internal use when creating a weak reference + // to an instance that is already wrapped by a ReferenceCountedDisposable. + if (instance == null) + { + throw new ArgumentNullException(nameof(instance)); + } + + _weakInstance = new WeakReference(instance); + _boxedReferenceCount = referenceCount; + } + /// /// Increments the reference count for the disposable object, and returns a new disposable reference to /// it.