Update SubdirectoryFileSystem and InMemoryFileSystem

This commit is contained in:
Alex Barney 2021-07-22 12:05:58 -07:00
parent 4efe313281
commit b86b57a4d3
5 changed files with 342 additions and 298 deletions

View File

@ -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<byte>.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<byte> GetString() => _buffer;
/// <summary>
/// Creates a <see cref="Path"/> from this <see cref="Path.Stored"/>. This <see cref="Stored"/>
/// must not be reinitialized or disposed for the lifetime of the created <see cref="Path"/>.
/// </summary>
/// <returns>The created <see cref="Path"/>.</returns>
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<byte>.Shared.Rent(alignedLength);
byte[] oldBuffer = _buffer;
_buffer = buffer;
if (oldBuffer is not null)
ArrayPool<byte>.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<byte> GetWriteBuffer()
internal Span<byte> 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<byte> path, int length)
{
if (length == 0 || path.At(0) == NullTerminator)

View File

@ -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);
}

View File

@ -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
{
/// <summary>
/// An <see cref="IFileSystem"/> that uses a directory of another <see cref="IFileSystem"/> as its root directory.
/// </summary>
/// <remarks>Based on FS 12.0.3 (nnSdk 12.3.1)</remarks>
public class SubdirectoryFileSystem : IFileSystem
{
private IFileSystem BaseFileSystem { get; }
private ReferenceCountedDisposable<IFileSystem> BaseFileSystemShared { get; }
private U8String RootPath { get; set; }
private bool PreserveUnc { get; }
private IFileSystem _baseFileSystem;
private ReferenceCountedDisposable<IFileSystem> _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<IFileSystem> baseFileSystem)
{
BaseFileSystem = baseFileSystem;
PreserveUnc = preserveUnc;
}
public SubdirectoryFileSystem(ref ReferenceCountedDisposable<IFileSystem> 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<IFileSystem> 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<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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<byte> fullOldPath = stackalloc byte[PathTools.MaxPathLength + 1];
Span<byte> 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<byte> fullOldPath = stackalloc byte[PathTools.MaxPathLength + 1];
Span<byte> 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<byte> 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<byte> 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<byte> 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<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId,
in Path path)
{
Span<byte> 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();
}
}
}

View File

@ -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);
}

View File

@ -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;
}
}