diff --git a/src/LibHac/Fs/Common/Path.cs b/src/LibHac/Fs/Common/Path.cs index b38a7818..5a77d858 100644 --- a/src/LibHac/Fs/Common/Path.cs +++ b/src/LibHac/Fs/Common/Path.cs @@ -29,6 +29,76 @@ namespace LibHac.Fs [DebuggerDisplay("{" + nameof(ToString) + "(),nq}")] public ref struct Path { + [DebuggerDisplay("{" + nameof(ToString) + "(),nq}")] + public struct Stored : IDisposable + { + private byte[] _buffer; + private int _length; + + public void Dispose() + { + byte[] buffer = Shared.Move(ref _buffer); + if (buffer is not null) + { + ArrayPool.Shared.Return(buffer); + } + } + + public Result Initialize(in Path path) + { + if (!path._isNormalized) + return ResultFs.NotNormalized.Log(); + + _length = path.GetLength(); + + Result rc = Preallocate(_length + NullTerminatorLength); + if (rc.IsFailure()) return rc; + + int bytesCopied = StringUtils.Copy(_buffer, path._string, _length + NullTerminatorLength); + + if (bytesCopied != _length) + return ResultFs.UnexpectedInPathA.Log(); + + return Result.Success; + } + + public readonly int GetLength() => _length; + public readonly ReadOnlySpan GetString() => _buffer; + + /// + /// Creates a from this . This + /// must not be reinitialized or disposed for the lifetime of the created . + /// + /// The created . + public readonly Path GetPath() + { + return new Path + { + _string = _buffer, + _isNormalized = true + }; + } + + private Result Preallocate(int length) + { + if (_buffer is not null && _buffer.Length > length) + return Result.Success; + + int alignedLength = Alignment.AlignUpPow2(length, WriteBufferAlignmentLength); + byte[] buffer = ArrayPool.Shared.Rent(alignedLength); + + byte[] oldBuffer = _buffer; + _buffer = buffer; + + if (oldBuffer is not null) + ArrayPool.Shared.Return(oldBuffer); + + return Result.Success; + } + + public override string ToString() => StringUtils.Utf8ZToString(_buffer); + } + private const int SeparatorLength = 1; private const int NullTerminatorLength = 1; private const int WriteBufferAlignmentLength = 8; @@ -49,7 +119,7 @@ namespace LibHac.Fs } } - private Span GetWriteBuffer() + internal Span GetWriteBuffer() { Assert.SdkRequires(_writeBuffer is not null); return _writeBuffer.AsSpan(); @@ -191,6 +261,22 @@ namespace LibHac.Fs return Result.Success; } + public Result Initialize(in Stored other) + { + int otherLength = other.GetLength(); + + Result rc = Preallocate(otherLength + NullTerminatorLength); + if (rc.IsFailure()) return rc; + + int bytesCopied = StringUtils.Copy(_writeBuffer, other.GetString(), otherLength + NullTerminatorLength); + + if (bytesCopied != otherLength) + return ResultFs.UnexpectedInPathA.Log(); + + _isNormalized = true; + return Result.Success; + } + private Result InitializeImpl(ReadOnlySpan path, int length) { if (length == 0 || path.At(0) == NullTerminator) diff --git a/src/LibHac/Fs/InMemoryFileSystem.cs b/src/LibHac/Fs/InMemoryFileSystem.cs index 7b7ae3e0..cacf15e6 100644 --- a/src/LibHac/Fs/InMemoryFileSystem.cs +++ b/src/LibHac/Fs/InMemoryFileSystem.cs @@ -1,7 +1,6 @@ using System; using System.Diagnostics; using System.IO; -using System.Runtime.CompilerServices; using LibHac.Common; using LibHac.Fs.Fsa; using LibHac.FsSystem; @@ -23,23 +22,15 @@ namespace LibHac.Fs protected override Result DoCreateDirectory(in Path path) { - Unsafe.SkipInit(out FsPath normalizedPath); - Result rc = PathNormalizer.Normalize(normalizedPath.Str, out _, path, false, false); - if (rc.IsFailure()) return rc; - - return FsTable.AddDirectory(normalizedPath); + return FsTable.AddDirectory(new U8Span(path.GetString())); } - protected override Result DoCreateDirectory(U8Span path, NxFileAttributes archiveAttribute) + protected override Result DoCreateDirectory(in Path path, NxFileAttributes archiveAttribute) { - Unsafe.SkipInit(out FsPath normalizedPath); - Result rc = PathNormalizer.Normalize(normalizedPath.Str, out _, path, false, false); + Result rc = FsTable.AddDirectory(new U8Span(path.GetString())); if (rc.IsFailure()) return rc; - rc = FsTable.AddDirectory(normalizedPath); - if (rc.IsFailure()) return rc; - - rc = FsTable.GetDirectory(normalizedPath, out DirectoryNode dir); + rc = FsTable.GetDirectory(new U8Span(path.GetString()), out DirectoryNode dir); if (rc.IsFailure()) return rc; dir.Attributes = archiveAttribute; @@ -48,14 +39,10 @@ namespace LibHac.Fs protected override Result DoCreateFile(in Path path, long size, CreateFileOptions option) { - Unsafe.SkipInit(out FsPath normalizedPath); - Result rc = PathNormalizer.Normalize(normalizedPath.Str, out _, path, false, false); + Result rc = FsTable.AddFile(new U8Span(path.GetString())); if (rc.IsFailure()) return rc; - rc = FsTable.AddFile(normalizedPath); - if (rc.IsFailure()) return rc; - - rc = FsTable.GetFile(normalizedPath, out FileNode file); + rc = FsTable.GetFile(new U8Span(path.GetString()), out FileNode file); if (rc.IsFailure()) return rc; return file.File.SetSize(size); @@ -63,49 +50,29 @@ namespace LibHac.Fs protected override Result DoDeleteDirectory(in Path path) { - Unsafe.SkipInit(out FsPath normalizedPath); - Result rc = PathNormalizer.Normalize(normalizedPath.Str, out _, path, false, false); - if (rc.IsFailure()) return rc; - - return FsTable.DeleteDirectory(normalizedPath, false); + return FsTable.DeleteDirectory(new U8Span(path.GetString()), false); } protected override Result DoDeleteDirectoryRecursively(in Path path) { - Unsafe.SkipInit(out FsPath normalizedPath); - Result rc = PathNormalizer.Normalize(normalizedPath.Str, out _, path, false, false); - if (rc.IsFailure()) return rc; - - return FsTable.DeleteDirectory(normalizedPath, true); + return FsTable.DeleteDirectory(new U8Span(path.GetString()), true); } protected override Result DoCleanDirectoryRecursively(in Path path) { - Unsafe.SkipInit(out FsPath normalizedPath); - Result rc = PathNormalizer.Normalize(normalizedPath.Str, out _, path, false, false); - if (rc.IsFailure()) return rc; - - return FsTable.CleanDirectory(normalizedPath); + return FsTable.CleanDirectory(new U8Span(path.GetString())); } protected override Result DoDeleteFile(in Path path) { - Unsafe.SkipInit(out FsPath normalizedPath); - Result rc = PathNormalizer.Normalize(normalizedPath.Str, out _, path, false, false); - if (rc.IsFailure()) return rc; - - return FsTable.DeleteFile(normalizedPath); + return FsTable.DeleteFile(new U8Span(path.GetString())); } protected override Result DoOpenDirectory(out IDirectory directory, in Path path, OpenDirectoryMode mode) { UnsafeHelpers.SkipParamInit(out directory); - Unsafe.SkipInit(out FsPath normalizedPath); - Result rc = PathNormalizer.Normalize(normalizedPath.Str, out _, path, false, false); - if (rc.IsFailure()) return rc; - - rc = FsTable.GetDirectory(normalizedPath, out DirectoryNode dirNode); + Result rc = FsTable.GetDirectory(new U8Span(path.GetString()), out DirectoryNode dirNode); if (rc.IsFailure()) return rc; directory = new MemoryDirectory(dirNode, mode); @@ -116,11 +83,7 @@ namespace LibHac.Fs { UnsafeHelpers.SkipParamInit(out file); - Unsafe.SkipInit(out FsPath normalizedPath); - Result rc = PathNormalizer.Normalize(normalizedPath.Str, out _, path, false, false); - if (rc.IsFailure()) return rc; - - rc = FsTable.GetFile(normalizedPath, out FileNode fileNode); + Result rc = FsTable.GetFile(new U8Span(path.GetString()), out FileNode fileNode); if (rc.IsFailure()) return rc; file = new MemoryFile(mode, fileNode.File); @@ -130,47 +93,25 @@ namespace LibHac.Fs protected override Result DoRenameDirectory(in Path currentPath, in Path newPath) { - Unsafe.SkipInit(out FsPath normalizedCurrentPath); - Unsafe.SkipInit(out FsPath normalizedNewPath); - - Result rc = PathNormalizer.Normalize(normalizedCurrentPath.Str, out _, currentPath, false, false); - if (rc.IsFailure()) return rc; - - rc = PathNormalizer.Normalize(normalizedNewPath.Str, out _, newPath, false, false); - if (rc.IsFailure()) return rc; - - return FsTable.RenameDirectory(normalizedCurrentPath, normalizedNewPath); + return FsTable.RenameDirectory(new U8Span(currentPath.GetString()), new U8Span(newPath.GetString())); } protected override Result DoRenameFile(in Path currentPath, in Path newPath) { - Unsafe.SkipInit(out FsPath normalizedCurrentPath); - Unsafe.SkipInit(out FsPath normalizedNewPath); - - Result rc = PathNormalizer.Normalize(normalizedCurrentPath.Str, out _, currentPath, false, false); - if (rc.IsFailure()) return rc; - - rc = PathNormalizer.Normalize(normalizedNewPath.Str, out _, newPath, false, false); - if (rc.IsFailure()) return rc; - - return FsTable.RenameFile(normalizedCurrentPath, normalizedNewPath); + return FsTable.RenameFile(new U8Span(currentPath.GetString()), new U8Span(newPath.GetString())); } protected override Result DoGetEntryType(out DirectoryEntryType entryType, in Path path) { UnsafeHelpers.SkipParamInit(out entryType); - Unsafe.SkipInit(out FsPath normalizedPath); - Result rc = PathNormalizer.Normalize(normalizedPath.Str, out _, path, false, false); - if (rc.IsFailure()) return rc; - - if (FsTable.GetFile(normalizedPath, out _).IsSuccess()) + if (FsTable.GetFile(new U8Span(path.GetString()), out _).IsSuccess()) { entryType = DirectoryEntryType.File; return Result.Success; } - if (FsTable.GetDirectory(normalizedPath, out _).IsSuccess()) + if (FsTable.GetDirectory(new U8Span(path.GetString()), out _).IsSuccess()) { entryType = DirectoryEntryType.Directory; return Result.Success; @@ -184,21 +125,17 @@ namespace LibHac.Fs return Result.Success; } - protected override Result DoGetFileAttributes(out NxFileAttributes attributes, U8Span path) + protected override Result DoGetFileAttributes(out NxFileAttributes attributes, in Path path) { UnsafeHelpers.SkipParamInit(out attributes); - Unsafe.SkipInit(out FsPath normalizedPath); - Result rc = PathNormalizer.Normalize(normalizedPath.Str, out _, path, false, false); - if (rc.IsFailure()) return rc; - - if (FsTable.GetFile(normalizedPath, out FileNode file).IsSuccess()) + if (FsTable.GetFile(new U8Span(path.GetString()), out FileNode file).IsSuccess()) { attributes = file.Attributes; return Result.Success; } - if (FsTable.GetDirectory(normalizedPath, out DirectoryNode dir).IsSuccess()) + if (FsTable.GetDirectory(new U8Span(path.GetString()), out DirectoryNode dir).IsSuccess()) { attributes = dir.Attributes; return Result.Success; @@ -207,19 +144,15 @@ namespace LibHac.Fs return ResultFs.PathNotFound.Log(); } - protected override Result DoSetFileAttributes(U8Span path, NxFileAttributes attributes) + protected override Result DoSetFileAttributes(in Path path, NxFileAttributes attributes) { - Unsafe.SkipInit(out FsPath normalizedPath); - Result rc = PathNormalizer.Normalize(normalizedPath.Str, out _, path, false, false); - if (rc.IsFailure()) return rc; - - if (FsTable.GetFile(normalizedPath, out FileNode file).IsSuccess()) + if (FsTable.GetFile(new U8Span(path.GetString()), out FileNode file).IsSuccess()) { file.Attributes = attributes; return Result.Success; } - if (FsTable.GetDirectory(normalizedPath, out DirectoryNode dir).IsSuccess()) + if (FsTable.GetDirectory(new U8Span(path.GetString()), out DirectoryNode dir).IsSuccess()) { dir.Attributes = attributes; return Result.Success; @@ -228,15 +161,11 @@ namespace LibHac.Fs return ResultFs.PathNotFound.Log(); } - protected override Result DoGetFileSize(out long fileSize, U8Span path) + protected override Result DoGetFileSize(out long fileSize, in Path path) { UnsafeHelpers.SkipParamInit(out fileSize); - Unsafe.SkipInit(out FsPath normalizedPath); - Result rc = PathNormalizer.Normalize(normalizedPath.Str, out _, path, false, false); - if (rc.IsFailure()) return rc; - - if (FsTable.GetFile(normalizedPath, out FileNode file).IsSuccess()) + if (FsTable.GetFile(new U8Span(path.GetString()), out FileNode file).IsSuccess()) { return file.File.GetSize(out fileSize); } diff --git a/src/LibHac/FsSystem/SubdirectoryFileSystem.cs b/src/LibHac/FsSystem/SubdirectoryFileSystem.cs index 0f84a538..e527a018 100644 --- a/src/LibHac/FsSystem/SubdirectoryFileSystem.cs +++ b/src/LibHac/FsSystem/SubdirectoryFileSystem.cs @@ -1,266 +1,280 @@ using System; -using System.Diagnostics; -using System.Runtime.CompilerServices; using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; -using LibHac.Util; namespace LibHac.FsSystem { + /// + /// An that uses a directory of another as its root directory. + /// + /// Based on FS 12.0.3 (nnSdk 12.3.1) public class SubdirectoryFileSystem : IFileSystem { - private IFileSystem BaseFileSystem { get; } - private ReferenceCountedDisposable BaseFileSystemShared { get; } - private U8String RootPath { get; set; } - private bool PreserveUnc { get; } + private IFileSystem _baseFileSystem; + private ReferenceCountedDisposable _baseFileSystemShared; + private Path.Stored _rootPath; - public static Result CreateNew(out SubdirectoryFileSystem created, IFileSystem baseFileSystem, U8Span rootPath, bool preserveUnc = false) + public SubdirectoryFileSystem(IFileSystem baseFileSystem) { - var obj = new SubdirectoryFileSystem(baseFileSystem, preserveUnc); - Result rc = obj.Initialize(rootPath); - - if (rc.IsSuccess()) - { - created = obj; - return Result.Success; - } - - obj.Dispose(); - UnsafeHelpers.SkipParamInit(out created); - return rc; + _baseFileSystem = baseFileSystem; } - public SubdirectoryFileSystem(IFileSystem baseFileSystem, bool preserveUnc = false) + public SubdirectoryFileSystem(ref ReferenceCountedDisposable baseFileSystem) { - BaseFileSystem = baseFileSystem; - PreserveUnc = preserveUnc; - } - - public SubdirectoryFileSystem(ref ReferenceCountedDisposable baseFileSystem, bool preserveUnc = false) - { - BaseFileSystemShared = Shared.Move(ref baseFileSystem); - BaseFileSystem = BaseFileSystemShared.Target; - PreserveUnc = preserveUnc; + _baseFileSystemShared = Shared.Move(ref baseFileSystem); + _baseFileSystem = _baseFileSystemShared.Target; } public override void Dispose() { - BaseFileSystemShared?.Dispose(); + ReferenceCountedDisposable sharedFs = Shared.Move(ref _baseFileSystemShared); + sharedFs?.Dispose(); base.Dispose(); } - public Result Initialize(U8Span rootPath) + public Result Initialize(in Path rootPath) { - if (StringUtils.GetLength(rootPath, PathTools.MaxPathLength + 1) > PathTools.MaxPathLength) - return ResultFs.TooLongPath.Log(); - - Span normalizedPath = stackalloc byte[PathTools.MaxPathLength + 2]; - - Result rc = PathNormalizer.Normalize(normalizedPath, out long normalizedPathLen, rootPath, PreserveUnc, false); - if (rc.IsFailure()) return rc; - - // Ensure a trailing separator - if (!PathNormalizer.IsSeparator(normalizedPath[(int)normalizedPathLen - 1])) - { - Debug.Assert(normalizedPathLen + 2 <= normalizedPath.Length); - - normalizedPath[(int)normalizedPathLen] = StringTraits.DirectorySeparator; - normalizedPath[(int)normalizedPathLen + 1] = StringTraits.NullTerminator; - normalizedPathLen++; - } - - byte[] buffer = new byte[normalizedPathLen + 1]; - normalizedPath.Slice(0, (int)normalizedPathLen).CopyTo(buffer); - RootPath = new U8String(buffer); - - return Result.Success; + return _rootPath.Initialize(in rootPath); } - private Result ResolveFullPath(Span outPath, U8Span relativePath) + private Result ResolveFullPath(ref Path outPath, in Path relativePath) { - if (RootPath.Length + StringUtils.GetLength(relativePath, PathTools.MaxPathLength + 1) > outPath.Length) - return ResultFs.TooLongPath.Log(); - - // Copy root path to the output - RootPath.Value.CopyTo(outPath); - - // Copy the normalized relative path to the output - return PathNormalizer.Normalize(outPath.Slice(RootPath.Length - 2), out _, relativePath, PreserveUnc, false); - } - - protected override Result DoCreateDirectory(in Path path) - { - Span fullPath = stackalloc byte[PathTools.MaxPathLength + 1]; - Result rc = ResolveFullPath(fullPath, path); - if (rc.IsFailure()) return rc; - - return BaseFileSystem.CreateDirectory(new U8Span(fullPath)); - } - - protected override Result DoCreateFile(in Path path, long size, CreateFileOptions option) - { - Span fullPath = stackalloc byte[PathTools.MaxPathLength + 1]; - Result rc = ResolveFullPath(fullPath, path); - if (rc.IsFailure()) return rc; - - return BaseFileSystem.CreateFile(new U8Span(fullPath), size, option); - } - - protected override Result DoDeleteDirectory(in Path path) - { - Span fullPath = stackalloc byte[PathTools.MaxPathLength + 1]; - Result rc = ResolveFullPath(fullPath, path); - if (rc.IsFailure()) return rc; - - return BaseFileSystem.DeleteDirectory(new U8Span(fullPath)); - } - - protected override Result DoDeleteDirectoryRecursively(in Path path) - { - Span fullPath = stackalloc byte[PathTools.MaxPathLength + 1]; - Result rc = ResolveFullPath(fullPath, path); - if (rc.IsFailure()) return rc; - - return BaseFileSystem.DeleteDirectoryRecursively(new U8Span(fullPath)); - } - - protected override Result DoCleanDirectoryRecursively(in Path path) - { - Span fullPath = stackalloc byte[PathTools.MaxPathLength + 1]; - Result rc = ResolveFullPath(fullPath, path); - if (rc.IsFailure()) return rc; - - return BaseFileSystem.CleanDirectoryRecursively(new U8Span(fullPath)); - } - - protected override Result DoDeleteFile(in Path path) - { - Span fullPath = stackalloc byte[PathTools.MaxPathLength + 1]; - Result rc = ResolveFullPath(fullPath, path); - if (rc.IsFailure()) return rc; - - return BaseFileSystem.DeleteFile(new U8Span(fullPath)); - } - - protected override Result DoOpenDirectory(out IDirectory directory, in Path path, OpenDirectoryMode mode) - { - UnsafeHelpers.SkipParamInit(out directory); - - Span fullPath = stackalloc byte[PathTools.MaxPathLength + 1]; - Result rc = ResolveFullPath(fullPath, path); - if (rc.IsFailure()) return rc; - - return BaseFileSystem.OpenDirectory(out directory, new U8Span(fullPath), mode); - } - - protected override Result DoOpenFile(out IFile file, in Path path, OpenMode mode) - { - UnsafeHelpers.SkipParamInit(out file); - - Span fullPath = stackalloc byte[PathTools.MaxPathLength + 1]; - Result rc = ResolveFullPath(fullPath, path); - if (rc.IsFailure()) return rc; - - return BaseFileSystem.OpenFile(out file, new U8Span(fullPath), mode); - } - - protected override Result DoRenameDirectory(in Path currentPath, in Path newPath) - { - Span fullOldPath = stackalloc byte[PathTools.MaxPathLength + 1]; - Span fullNewPath = stackalloc byte[PathTools.MaxPathLength + 1]; - - Result rc = ResolveFullPath(fullOldPath, currentPath); - if (rc.IsFailure()) return rc; - - rc = ResolveFullPath(fullNewPath, newPath); - if (rc.IsFailure()) return rc; - - return BaseFileSystem.RenameDirectory(new U8Span(fullOldPath), new U8Span(fullNewPath)); - } - - protected override Result DoRenameFile(in Path currentPath, in Path newPath) - { - Span fullOldPath = stackalloc byte[PathTools.MaxPathLength + 1]; - Span fullNewPath = stackalloc byte[PathTools.MaxPathLength + 1]; - - Result rc = ResolveFullPath(fullOldPath, currentPath); - if (rc.IsFailure()) return rc; - - rc = ResolveFullPath(fullNewPath, newPath); - if (rc.IsFailure()) return rc; - - return BaseFileSystem.RenameFile(new U8Span(fullOldPath), new U8Span(fullNewPath)); + Path rootPath = _rootPath.GetPath(); + return outPath.Combine(in rootPath, in relativePath); } protected override Result DoGetEntryType(out DirectoryEntryType entryType, in Path path) { UnsafeHelpers.SkipParamInit(out entryType); - Unsafe.SkipInit(out FsPath fullPath); - - Result rc = ResolveFullPath(fullPath.Str, path); + var fullPath = new Path(); + Result rc = ResolveFullPath(ref fullPath, in path); if (rc.IsFailure()) return rc; - return BaseFileSystem.GetEntryType(out entryType, fullPath); - } + rc = _baseFileSystem.GetEntryType(out entryType, in fullPath); + if (rc.IsFailure()) return rc; - protected override Result DoCommit() - { - return BaseFileSystem.Commit(); - } - - protected override Result DoCommitProvisionally(long counter) - { - return BaseFileSystem.CommitProvisionally(counter); - } - - protected override Result DoRollback() - { - return BaseFileSystem.Rollback(); + fullPath.Dispose(); + return Result.Success; } protected override Result DoGetFreeSpaceSize(out long freeSpace, in Path path) { UnsafeHelpers.SkipParamInit(out freeSpace); - Span fullPath = stackalloc byte[PathTools.MaxPathLength + 1]; - Result rc = ResolveFullPath(fullPath, path); + var fullPath = new Path(); + Result rc = ResolveFullPath(ref fullPath, in path); if (rc.IsFailure()) return rc; - return BaseFileSystem.GetFreeSpaceSize(out freeSpace, new U8Span(fullPath)); + rc = _baseFileSystem.GetFreeSpaceSize(out freeSpace, in fullPath); + if (rc.IsFailure()) return rc; + + fullPath.Dispose(); + return Result.Success; } protected override Result DoGetTotalSpaceSize(out long totalSpace, in Path path) { UnsafeHelpers.SkipParamInit(out totalSpace); - Span fullPath = stackalloc byte[PathTools.MaxPathLength + 1]; - Result rc = ResolveFullPath(fullPath, path); + var fullPath = new Path(); + Result rc = ResolveFullPath(ref fullPath, in path); if (rc.IsFailure()) return rc; - return BaseFileSystem.GetTotalSpaceSize(out totalSpace, new U8Span(fullPath)); + rc = _baseFileSystem.GetTotalSpaceSize(out totalSpace, in fullPath); + if (rc.IsFailure()) return rc; + + fullPath.Dispose(); + return Result.Success; } protected override Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, in Path path) { UnsafeHelpers.SkipParamInit(out timeStamp); - Span fullPath = stackalloc byte[PathTools.MaxPathLength + 1]; - Result rc = ResolveFullPath(fullPath, path); + var fullPath = new Path(); + Result rc = ResolveFullPath(ref fullPath, in path); if (rc.IsFailure()) return rc; - return BaseFileSystem.GetFileTimeStampRaw(out timeStamp, new U8Span(fullPath)); + rc = _baseFileSystem.GetFileTimeStampRaw(out timeStamp, in fullPath); + if (rc.IsFailure()) return rc; + + fullPath.Dispose(); + return Result.Success; + } + + protected override Result DoOpenFile(out IFile file, in Path path, OpenMode mode) + { + UnsafeHelpers.SkipParamInit(out file); + + var fullPath = new Path(); + Result rc = ResolveFullPath(ref fullPath, in path); + if (rc.IsFailure()) return rc; + + rc = _baseFileSystem.OpenFile(out file, in fullPath, mode); + if (rc.IsFailure()) return rc; + + fullPath.Dispose(); + return Result.Success; + } + + protected override Result DoOpenDirectory(out IDirectory directory, in Path path, OpenDirectoryMode mode) + { + UnsafeHelpers.SkipParamInit(out directory); + + var fullPath = new Path(); + Result rc = ResolveFullPath(ref fullPath, in path); + if (rc.IsFailure()) return rc; + + rc = _baseFileSystem.OpenDirectory(out directory, in fullPath, mode); + if (rc.IsFailure()) return rc; + + fullPath.Dispose(); + return Result.Success; + } + + protected override Result DoCreateFile(in Path path, long size, CreateFileOptions option) + { + var fullPath = new Path(); + Result rc = ResolveFullPath(ref fullPath, in path); + if (rc.IsFailure()) return rc; + + rc = _baseFileSystem.CreateFile(in fullPath, size, option); + if (rc.IsFailure()) return rc; + + fullPath.Dispose(); + return Result.Success; + } + + protected override Result DoDeleteFile(in Path path) + { + var fullPath = new Path(); + Result rc = ResolveFullPath(ref fullPath, in path); + if (rc.IsFailure()) return rc; + + rc = _baseFileSystem.DeleteFile(in fullPath); + if (rc.IsFailure()) return rc; + + fullPath.Dispose(); + return Result.Success; + } + + protected override Result DoCreateDirectory(in Path path) + { + var fullPath = new Path(); + Result rc = ResolveFullPath(ref fullPath, in path); + if (rc.IsFailure()) return rc; + + rc = _baseFileSystem.CreateDirectory(in fullPath); + if (rc.IsFailure()) return rc; + + fullPath.Dispose(); + return Result.Success; + } + + protected override Result DoDeleteDirectory(in Path path) + { + var fullPath = new Path(); + Result rc = ResolveFullPath(ref fullPath, in path); + if (rc.IsFailure()) return rc; + + rc = _baseFileSystem.DeleteDirectory(in fullPath); + if (rc.IsFailure()) return rc; + + fullPath.Dispose(); + return Result.Success; + } + + protected override Result DoDeleteDirectoryRecursively(in Path path) + { + var fullPath = new Path(); + Result rc = ResolveFullPath(ref fullPath, in path); + if (rc.IsFailure()) return rc; + + rc = _baseFileSystem.DeleteDirectoryRecursively(in fullPath); + if (rc.IsFailure()) return rc; + + fullPath.Dispose(); + return Result.Success; + } + + protected override Result DoCleanDirectoryRecursively(in Path path) + { + var fullPath = new Path(); + Result rc = ResolveFullPath(ref fullPath, in path); + if (rc.IsFailure()) return rc; + + rc = _baseFileSystem.CleanDirectoryRecursively(in fullPath); + if (rc.IsFailure()) return rc; + + fullPath.Dispose(); + return Result.Success; + } + + protected override Result DoRenameFile(in Path currentPath, in Path newPath) + { + var currentFullPath = new Path(); + Result rc = ResolveFullPath(ref currentFullPath, in currentPath); + if (rc.IsFailure()) return rc; + + var newFullPath = new Path(); + rc = ResolveFullPath(ref newFullPath, in newPath); + if (rc.IsFailure()) return rc; + + rc = _baseFileSystem.RenameFile(in currentFullPath, in newFullPath); + if (rc.IsFailure()) return rc; + + currentFullPath.Dispose(); + newFullPath.Dispose(); + return Result.Success; + } + + protected override Result DoRenameDirectory(in Path currentPath, in Path newPath) + { + var currentFullPath = new Path(); + Result rc = ResolveFullPath(ref currentFullPath, in currentPath); + if (rc.IsFailure()) return rc; + + var newFullPath = new Path(); + rc = ResolveFullPath(ref newFullPath, in newPath); + if (rc.IsFailure()) return rc; + + rc = _baseFileSystem.RenameDirectory(in currentFullPath, in newFullPath); + if (rc.IsFailure()) return rc; + + currentFullPath.Dispose(); + newFullPath.Dispose(); + return Result.Success; } protected override Result DoQueryEntry(Span outBuffer, ReadOnlySpan inBuffer, QueryId queryId, in Path path) { - Span fullPath = stackalloc byte[PathTools.MaxPathLength + 1]; - Result rc = ResolveFullPath(fullPath, path); + var fullPath = new Path(); + Result rc = ResolveFullPath(ref fullPath, in path); if (rc.IsFailure()) return rc; - return BaseFileSystem.QueryEntry(outBuffer, inBuffer, queryId, new U8Span(fullPath)); + rc = _baseFileSystem.QueryEntry(outBuffer, inBuffer, queryId, in fullPath); + if (rc.IsFailure()) return rc; + + fullPath.Dispose(); + return Result.Success; + } + + protected override Result DoCommit() + { + return _baseFileSystem.Commit(); + } + + protected override Result DoCommitProvisionally(long counter) + { + return _baseFileSystem.CommitProvisionally(counter); + } + + protected override Result DoRollback() + { + return _baseFileSystem.Rollback(); } } } diff --git a/src/LibHac/SwitchFs.cs b/src/LibHac/SwitchFs.cs index 520f524c..0bf609ed 100644 --- a/src/LibHac/SwitchFs.cs +++ b/src/LibHac/SwitchFs.cs @@ -65,13 +65,22 @@ namespace LibHac { var concatFs = new ConcatenationFileSystem(fileSystem); SubdirectoryFileSystem saveDirFs = null; + SubdirectoryFileSystem contentDirFs = null; if (concatFs.DirectoryExists("/save")) { - SubdirectoryFileSystem.CreateNew(out saveDirFs, concatFs, "/save".ToU8String()).ThrowIfFailure(); + var savePath = new Fs.Path(); + PathFunctions.SetUpFixedPath(ref savePath, "/save".ToU8String()); + + saveDirFs = new SubdirectoryFileSystem(concatFs); + saveDirFs.Initialize(in savePath).ThrowIfFailure(); } - SubdirectoryFileSystem.CreateNew(out SubdirectoryFileSystem contentDirFs, concatFs, "/Contents".ToU8String()).ThrowIfFailure(); + var contentsPath = new Fs.Path(); + PathFunctions.SetUpFixedPath(ref contentsPath, "/Contents".ToU8String()); + + contentDirFs = new SubdirectoryFileSystem(concatFs); + contentDirFs.Initialize(in contentsPath).ThrowIfFailure(); return new SwitchFs(keySet, contentDirFs, saveDirFs); } diff --git a/tests/LibHac.Tests/Fs/SubdirectoryFileSystemTests.cs b/tests/LibHac.Tests/Fs/SubdirectoryFileSystemTests.cs index f59d6964..0626b342 100644 --- a/tests/LibHac.Tests/Fs/SubdirectoryFileSystemTests.cs +++ b/tests/LibHac.Tests/Fs/SubdirectoryFileSystemTests.cs @@ -20,8 +20,11 @@ namespace LibHac.Tests.Fs baseFs.CreateDirectory("/sub"); baseFs.CreateDirectory("/sub/path"); + var rootPath = new Path(); + PathFunctions.SetUpFixedPath(ref rootPath, "/sub/path".ToU8String()); + var subFs = new SubdirectoryFileSystem(baseFs); - subFs.Initialize("/sub/path".ToU8String()).ThrowIfFailure(); + subFs.Initialize(in rootPath).ThrowIfFailure(); return (baseFs, subFs); } @@ -55,8 +58,11 @@ namespace LibHac.Tests.Fs { var baseFs = new InMemoryFileSystem(); + var rootPath = new Path(); + PathFunctions.SetUpFixedPath(ref rootPath, "/".ToU8String()); + var subFs = new SubdirectoryFileSystem(baseFs); - subFs.Initialize("/".ToU8String()).ThrowIfFailure(); + subFs.Initialize(in rootPath).ThrowIfFailure(); return subFs; } }