Introduce UniqueRef<T> and use it in IFileSystem

This commit is contained in:
Alex Barney 2021-08-10 14:44:58 -07:00
parent 5f85c0b8e2
commit 01ca9e0412
76 changed files with 1469 additions and 1561 deletions

View File

@ -0,0 +1,80 @@
using System;
using System.Runtime.CompilerServices;
using static InlineIL.IL.Emit;
namespace LibHac.Common
{
public static class UniqueRefExtensions
{
// ReSharper disable once EntityNameCapturedOnly.Global
public static ref UniqueRef<T> Ref<T>(this in UniqueRef<T> value) where T : class, IDisposable
{
Ldarg(nameof(value));
Ret();
throw InlineIL.IL.Unreachable();
}
}
public struct UniqueRef<T> : IDisposable where T : class, IDisposable
{
private T _value;
public void Dispose()
{
Release()?.Dispose();
}
public UniqueRef(T value)
{
_value = value;
}
public UniqueRef(ref UniqueRef<T> other)
{
_value = other.Release();
}
public readonly T Get => _value;
public readonly bool HasValue => Get is not null;
public void Reset() => Reset(null);
public void Reset(T value)
{
T oldValue = _value;
_value = value;
oldValue?.Dispose();
}
public void Reset<TFrom>(TFrom value) where TFrom : class, T
{
T oldValue = _value;
_value = value;
oldValue?.Dispose();
}
public void Set(ref UniqueRef<T> other)
{
if (Unsafe.AreSame(ref this, ref other))
return;
Reset(other.Release());
}
public void Set<TFrom>(ref UniqueRef<TFrom> other) where TFrom : class, T
{
Reset(other.Release());
}
public T Release()
{
T oldValue = _value;
_value = null;
return oldValue;
}
}
}

View File

@ -137,7 +137,7 @@ namespace LibHac.Fs
/// must not be reinitialized or disposed for the lifetime of the created <see cref="Path"/>. /// must not be reinitialized or disposed for the lifetime of the created <see cref="Path"/>.
/// </summary> /// </summary>
/// <returns>The created <see cref="Path"/>.</returns> /// <returns>The created <see cref="Path"/>.</returns>
public readonly Path GetPath() public readonly Path DangerousGetPath()
{ {
return new Path return new Path
{ {
@ -1113,6 +1113,20 @@ namespace LibHac.Fs
return SetUpFixedPath(ref path, pathBuffer); return SetUpFixedPath(ref path, pathBuffer);
} }
// /%s/%s
internal static Result SetUpFixedPathDoubleEntry(ref Path path, Span<byte> pathBuffer,
ReadOnlySpan<byte> entryName1, ReadOnlySpan<byte> entryName2)
{
var sb = new U8StringBuilder(pathBuffer);
sb.Append((byte)'/').Append(entryName1)
.Append((byte)'/').Append(entryName2);
if (sb.Overflowed)
return ResultFs.InvalidArgument.Log();
return SetUpFixedPath(ref path, pathBuffer);
}
// /%016llx // /%016llx
internal static Result SetUpFixedPathSaveId(ref Path path, Span<byte> pathBuffer, ulong saveDataId) internal static Result SetUpFixedPathSaveId(ref Path path, Span<byte> pathBuffer, ulong saveDataId)
{ {

View File

@ -5,22 +5,24 @@ namespace LibHac.Fs
{ {
public class FileStorageBasedFileSystem : FileStorage2 public class FileStorageBasedFileSystem : FileStorage2
{ {
private ReferenceCountedDisposable<IFileSystem> BaseFileSystem { get; set; } private ReferenceCountedDisposable<IFileSystem> _baseFileSystem;
private IFile BaseFile { get; set; } private UniqueRef<IFile> _baseFile;
public FileStorageBasedFileSystem() public FileStorageBasedFileSystem()
{ {
FileSize = SizeNotInitialized; FileSize = SizeNotInitialized;
} }
public Result Initialize(ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path, OpenMode mode) public Result Initialize(ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, in Path path,
OpenMode mode)
{ {
Result rc = baseFileSystem.Target.OpenFile(out IFile file, path, mode); using var baseFile = new UniqueRef<IFile>();
Result rc = baseFileSystem.Target.OpenFile(ref baseFile.Ref(), in path, mode);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
SetFile(file); SetFile(baseFile.Get);
BaseFile = file; _baseFileSystem = Shared.Move(ref baseFileSystem);
BaseFileSystem = Shared.Move(ref baseFileSystem); _baseFile.Set(ref _baseFile.Ref());
return Result.Success; return Result.Success;
} }
@ -29,8 +31,8 @@ namespace LibHac.Fs
{ {
if (disposing) if (disposing)
{ {
BaseFile?.Dispose(); _baseFile.Dispose();
BaseFileSystem?.Dispose(); _baseFileSystem?.Dispose();
} }
base.Dispose(disposing); base.Dispose(disposing);

View File

@ -7,33 +7,33 @@ namespace LibHac.Fs.Impl
{ {
internal class DirectoryAccessor : IDisposable internal class DirectoryAccessor : IDisposable
{ {
private IDirectory _directory; private UniqueRef<IDirectory> _directory;
private FileSystemAccessor _parentFileSystem; private FileSystemAccessor _parentFileSystem;
public DirectoryAccessor(ref IDirectory directory, FileSystemAccessor parentFileSystem) public DirectoryAccessor(ref UniqueRef<IDirectory> directory, FileSystemAccessor parentFileSystem)
{ {
_directory = Shared.Move(ref directory); _directory = new UniqueRef<IDirectory>(ref directory);
_parentFileSystem = parentFileSystem; _parentFileSystem = parentFileSystem;
} }
public void Dispose() public void Dispose()
{ {
IDirectory directory = Shared.Move(ref _directory); _directory.Reset();
directory?.Dispose();
_parentFileSystem.NotifyCloseDirectory(this); _parentFileSystem.NotifyCloseDirectory(this);
_directory.Dispose();
} }
public FileSystemAccessor GetParent() => _parentFileSystem; public FileSystemAccessor GetParent() => _parentFileSystem;
public Result Read(out long entriesRead, Span<DirectoryEntry> entryBuffer) public Result Read(out long entriesRead, Span<DirectoryEntry> entryBuffer)
{ {
return _directory.Read(out entriesRead, entryBuffer); return _directory.Get.Read(out entriesRead, entryBuffer);
} }
public Result GetEntryCount(out long entryCount) public Result GetEntryCount(out long entryCount)
{ {
return _directory.GetEntryCount(out entryCount); return _directory.Get.GetEntryCount(out entryCount);
} }
} }
} }

View File

@ -19,7 +19,7 @@ namespace LibHac.Fs.Impl
{ {
private const string NeedFlushMessage = "Error: nn::fs::CloseFile() failed because the file was not flushed.\n"; private const string NeedFlushMessage = "Error: nn::fs::CloseFile() failed because the file was not flushed.\n";
private IFile _file; private UniqueRef<IFile> _file;
private FileSystemAccessor _parentFileSystem; private FileSystemAccessor _parentFileSystem;
private WriteState _writeState; private WriteState _writeState;
private Result _lastResult; private Result _lastResult;
@ -30,12 +30,12 @@ namespace LibHac.Fs.Impl
internal HorizonClient Hos { get; } internal HorizonClient Hos { get; }
public FileAccessor(HorizonClient hosClient, ref IFile file, FileSystemAccessor parentFileSystem, public FileAccessor(HorizonClient hosClient, ref UniqueRef<IFile> file, FileSystemAccessor parentFileSystem,
OpenMode mode) OpenMode mode)
{ {
Hos = hosClient; Hos = hosClient;
_file = Shared.Move(ref file); _file = new UniqueRef<IFile>(ref file);
_parentFileSystem = parentFileSystem; _parentFileSystem = parentFileSystem;
_writeState = WriteState.None; _writeState = WriteState.None;
_lastResult = Result.Success; _lastResult = Result.Success;
@ -52,8 +52,7 @@ namespace LibHac.Fs.Impl
_parentFileSystem?.NotifyCloseFile(this); _parentFileSystem?.NotifyCloseFile(this);
IFile file = Shared.Move(ref _file); _file.Dispose();
file?.Dispose();
} }
public OpenMode GetOpenMode() => _openMode; public OpenMode GetOpenMode() => _openMode;
@ -77,7 +76,7 @@ namespace LibHac.Fs.Impl
public Result ReadWithoutCacheAccessLog(out long bytesRead, long offset, Span<byte> destination, public Result ReadWithoutCacheAccessLog(out long bytesRead, long offset, Span<byte> destination,
in ReadOption option) in ReadOption option)
{ {
return _file.Read(out bytesRead, offset, destination, in option); return _file.Get.Read(out bytesRead, offset, destination, in option);
} }
private Result ReadWithCacheAccessLog(out long bytesRead, long offset, Span<byte> destination, private Result ReadWithCacheAccessLog(out long bytesRead, long offset, Span<byte> destination,
@ -166,7 +165,7 @@ namespace LibHac.Fs.Impl
} }
else else
{ {
Result rc = UpdateLastResult(_file.Write(offset, source, in option)); Result rc = UpdateLastResult(_file.Get.Write(offset, source, in option));
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
} }
@ -182,7 +181,7 @@ namespace LibHac.Fs.Impl
using ScopedSetter<WriteState> setter = using ScopedSetter<WriteState> setter =
ScopedSetter<WriteState>.MakeScopedSetter(ref _writeState, WriteState.Failed); ScopedSetter<WriteState>.MakeScopedSetter(ref _writeState, WriteState.Failed);
Result rc = UpdateLastResult(_file.Flush()); Result rc = UpdateLastResult(_file.Get.Flush());
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
setter.Set(WriteState.None); setter.Set(WriteState.None);
@ -198,7 +197,7 @@ namespace LibHac.Fs.Impl
using ScopedSetter<WriteState> setter = using ScopedSetter<WriteState> setter =
ScopedSetter<WriteState>.MakeScopedSetter(ref _writeState, WriteState.Failed); ScopedSetter<WriteState>.MakeScopedSetter(ref _writeState, WriteState.Failed);
Result rc = UpdateLastResult(_file.SetSize(size)); Result rc = UpdateLastResult(_file.Get.SetSize(size));
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
if (_filePathHash.Data != 0) if (_filePathHash.Data != 0)
@ -217,13 +216,13 @@ namespace LibHac.Fs.Impl
if (_lastResult.IsFailure()) if (_lastResult.IsFailure())
return _lastResult; return _lastResult;
return _file.GetSize(out size); return _file.Get.GetSize(out size);
} }
public Result OperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size, public Result OperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size,
ReadOnlySpan<byte> inBuffer) ReadOnlySpan<byte> inBuffer)
{ {
return _file.OperateRange(outBuffer, operationId, offset, size, inBuffer); return _file.Get.OperateRange(outBuffer, operationId, offset, size, inBuffer);
} }
} }
} }

View File

@ -331,76 +331,58 @@ namespace LibHac.Fs.Impl
return Result.Success; return Result.Success;
} }
public Result OpenFile(out FileAccessor file, U8Span path, OpenMode mode) public Result OpenFile(ref UniqueRef<FileAccessor> outFile, U8Span path, OpenMode mode)
{ {
UnsafeHelpers.SkipParamInit(out file);
using var pathNormalized = new Path(); using var pathNormalized = new Path();
Result rc = SetUpPath(ref pathNormalized.Ref(), path); Result rc = SetUpPath(ref pathNormalized.Ref(), path);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
IFile iFile = null; using var file = new UniqueRef<IFile>();
try rc = _fileSystem.OpenFile(ref file.Ref(), in pathNormalized, mode);
if (rc.IsFailure()) return rc;
var accessor = new FileAccessor(Hos, ref file.Ref(), this, mode);
using (ScopedLock.Lock(ref _openListLock))
{ {
rc = _fileSystem.OpenFile(out iFile, in pathNormalized, mode); _openFiles.AddLast(accessor);
if (rc.IsFailure()) return rc;
var fileAccessor = new FileAccessor(Hos, ref iFile, this, mode);
using (ScopedLock.Lock(ref _openListLock))
{
_openFiles.AddLast(fileAccessor);
}
if (_isPathCacheAttached)
{
if (mode.HasFlag(OpenMode.AllowAppend))
{
throw new NotImplementedException();
}
else
{
throw new NotImplementedException();
}
}
file = Shared.Move(ref fileAccessor);
return Result.Success;
} }
finally
if (_isPathCacheAttached)
{ {
iFile?.Dispose(); if (mode.HasFlag(OpenMode.AllowAppend))
{
throw new NotImplementedException();
}
else
{
throw new NotImplementedException();
}
} }
outFile.Reset(accessor);
return Result.Success;
} }
public Result OpenDirectory(out DirectoryAccessor directory, U8Span path, OpenDirectoryMode mode) public Result OpenDirectory(ref UniqueRef<DirectoryAccessor> outDirectory, U8Span path, OpenDirectoryMode mode)
{ {
UnsafeHelpers.SkipParamInit(out directory);
using var pathNormalized = new Path(); using var pathNormalized = new Path();
Result rc = SetUpPath(ref pathNormalized.Ref(), path); Result rc = SetUpPath(ref pathNormalized.Ref(), path);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
IDirectory iDirectory = null; using var directory = new UniqueRef<IDirectory>();
try rc = _fileSystem.OpenDirectory(ref directory.Ref(), in pathNormalized, mode);
if (rc.IsFailure()) return rc;
var accessor = new DirectoryAccessor(ref directory.Ref(), this);
using (ScopedLock.Lock(ref _openListLock))
{ {
rc = _fileSystem.OpenDirectory(out iDirectory, in pathNormalized, mode); _openDirectories.AddLast(accessor);
if (rc.IsFailure()) return rc;
var directoryAccessor = new DirectoryAccessor(ref iDirectory, this);
using (ScopedLock.Lock(ref _openListLock))
{
_openDirectories.AddLast(directoryAccessor);
}
directory = Shared.Move(ref directoryAccessor);
return Result.Success;
}
finally
{
iDirectory?.Dispose();
} }
outDirectory.Reset(accessor);
return Result.Success;
} }
public Result Commit() public Result Commit()

View File

@ -203,10 +203,8 @@ namespace LibHac.Fs.Fsa
/// <see cref="ResultFs.PathNotFound"/>: The specified path does not exist or is a directory.<br/> /// <see cref="ResultFs.PathNotFound"/>: The specified path does not exist or is a directory.<br/>
/// <see cref="ResultFs.TargetLocked"/>: When opening as <see cref="OpenMode.Write"/>, /// <see cref="ResultFs.TargetLocked"/>: When opening as <see cref="OpenMode.Write"/>,
/// the file is already opened as <see cref="OpenMode.Write"/>.</returns> /// the file is already opened as <see cref="OpenMode.Write"/>.</returns>
public Result OpenFile(out IFile file, U8Span path, OpenMode mode) public Result OpenFile(ref UniqueRef<IFile> file, U8Span path, OpenMode mode)
{ {
UnsafeHelpers.SkipParamInit(out file);
if (path.IsNull()) if (path.IsNull())
return ResultFs.NullptrArgument.Log(); return ResultFs.NullptrArgument.Log();
@ -214,7 +212,7 @@ namespace LibHac.Fs.Fsa
Result rs = pathNormalized.InitializeWithNormalization(path); Result rs = pathNormalized.InitializeWithNormalization(path);
if (rs.IsFailure()) return rs; if (rs.IsFailure()) return rs;
return DoOpenFile(out file, in pathNormalized, mode); return DoOpenFile(ref file, in pathNormalized, mode);
} }
/// <summary> /// <summary>
@ -228,7 +226,7 @@ namespace LibHac.Fs.Fsa
/// <see cref="ResultFs.PathNotFound"/>: The specified path does not exist or is a directory.<br/> /// <see cref="ResultFs.PathNotFound"/>: The specified path does not exist or is a directory.<br/>
/// <see cref="ResultFs.TargetLocked"/>: When opening as <see cref="OpenMode.Write"/>, /// <see cref="ResultFs.TargetLocked"/>: When opening as <see cref="OpenMode.Write"/>,
/// the file is already opened as <see cref="OpenMode.Write"/>.</returns> /// the file is already opened as <see cref="OpenMode.Write"/>.</returns>
public Result OpenFile(out IFile file, in Path path, OpenMode mode) public Result OpenFile(ref UniqueRef<IFile> file, in Path path, OpenMode mode)
{ {
if ((mode & OpenMode.ReadWrite) == 0 || (mode & ~OpenMode.All) != 0) if ((mode & OpenMode.ReadWrite) == 0 || (mode & ~OpenMode.All) != 0)
{ {
@ -236,28 +234,24 @@ namespace LibHac.Fs.Fsa
return ResultFs.InvalidOpenMode.Log(); return ResultFs.InvalidOpenMode.Log();
} }
return DoOpenFile(out file, in path, mode); return DoOpenFile(ref file, in path, mode);
} }
/// <summary> /// <summary>
/// Creates an <see cref="IDirectory"/> instance for enumerating the specified directory. /// Creates an <see cref="IDirectory"/> instance for enumerating the specified directory.
/// </summary> /// </summary>
/// <param name="directory">If the operation returns successfully, /// <param name="outDirectory"></param>
/// An <see cref="IDirectory"/> instance for the specified directory.</param>
/// <param name="path">The directory's full path.</param> /// <param name="path">The directory's full path.</param>
/// <param name="mode">Specifies which sub-entries should be enumerated.</param> /// <param name="mode">Specifies which sub-entries should be enumerated.</param>
/// <returns><see cref="Result.Success"/>: The operation was successful.<br/> /// <returns><see cref="Result.Success"/>: The operation was successful.<br/>
/// <see cref="ResultFs.PathNotFound"/>: The specified path does not exist or is a file.</returns> /// <see cref="ResultFs.PathNotFound"/>: The specified path does not exist or is a file.</returns>
public Result OpenDirectory(out IDirectory directory, in Path path, OpenDirectoryMode mode) public Result OpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path, OpenDirectoryMode mode)
{ {
if ((mode & OpenDirectoryMode.All) == 0 || if ((mode & OpenDirectoryMode.All) == 0 ||
(mode & ~(OpenDirectoryMode.All | OpenDirectoryMode.NoFileSize)) != 0) (mode & ~(OpenDirectoryMode.All | OpenDirectoryMode.NoFileSize)) != 0)
{
UnsafeHelpers.SkipParamInit(out directory);
return ResultFs.InvalidOpenMode.Log(); return ResultFs.InvalidOpenMode.Log();
}
return DoOpenDirectory(out directory, in path, mode); return DoOpenDirectory(ref outDirectory, in path, mode);
} }
/// <summary> /// <summary>
@ -325,8 +319,9 @@ namespace LibHac.Fs.Fsa
return ResultFs.NotImplemented.Log(); return ResultFs.NotImplemented.Log();
} }
protected abstract Result DoOpenFile(out IFile file, in Path path, OpenMode mode); protected abstract Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode);
protected abstract Result DoOpenDirectory(out IDirectory directory, in Path path, OpenDirectoryMode mode); protected abstract Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,
OpenDirectoryMode mode);
protected abstract Result DoCommit(); protected abstract Result DoCommit();
protected virtual Result DoCommitProvisionally(long counter) => ResultFs.NotImplemented.Log(); protected virtual Result DoCommitProvisionally(long counter) => ResultFs.NotImplemented.Log();

View File

@ -508,27 +508,27 @@ namespace LibHac.Fs.Fsa
fs.Impl.AbortIfNeeded(rc); fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
FileAccessor accessor; using var file = new UniqueRef<FileAccessor>();
if (fs.Impl.IsEnabledAccessLog() && fileSystem.IsEnabledAccessLog()) if (fs.Impl.IsEnabledAccessLog() && fileSystem.IsEnabledAccessLog())
{ {
Tick start = fs.Hos.Os.GetSystemTick(); Tick start = fs.Hos.Os.GetSystemTick();
rc = fileSystem.OpenFile(out accessor, subPath, mode); rc = fileSystem.OpenFile(ref file.Ref(), subPath, mode);
Tick end = fs.Hos.Os.GetSystemTick(); Tick end = fs.Hos.Os.GetSystemTick();
fs.Impl.OutputAccessLog(rc, start, end, accessor, new U8Span(logBuffer)); fs.Impl.OutputAccessLog(rc, start, end, file.Get, new U8Span(logBuffer));
} }
else else
{ {
rc = fileSystem.OpenFile(out accessor, subPath, mode); rc = fileSystem.OpenFile(ref file.Ref(), subPath, mode);
} }
fs.Impl.AbortIfNeeded(rc); fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
handle = new FileHandle(accessor); handle = new FileHandle(file.Release());
return Result.Success; return Result.Success;
} }
public static Result OpenFile(this FileSystemClient fs, out FileHandle handle, IFile file, OpenMode mode) public static Result OpenFile(this FileSystemClient fs, out FileHandle handle, ref UniqueRef<IFile> file, OpenMode mode)
{ {
var accessor = new FileAccessor(fs.Hos, ref file, null, mode); var accessor = new FileAccessor(fs.Hos, ref file, null, mode);
handle = new FileHandle(accessor); handle = new FileHandle(accessor);
@ -565,23 +565,24 @@ namespace LibHac.Fs.Fsa
fs.Impl.AbortIfNeeded(rc); fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
DirectoryAccessor accessor; using var accessor = new UniqueRef<DirectoryAccessor>();
if (fs.Impl.IsEnabledAccessLog() && fileSystem.IsEnabledAccessLog()) if (fs.Impl.IsEnabledAccessLog() && fileSystem.IsEnabledAccessLog())
{ {
Tick start = fs.Hos.Os.GetSystemTick(); Tick start = fs.Hos.Os.GetSystemTick();
rc = fileSystem.OpenDirectory(out accessor, subPath, mode); rc = fileSystem.OpenDirectory(ref accessor.Ref(), subPath, mode);
Tick end = fs.Hos.Os.GetSystemTick(); Tick end = fs.Hos.Os.GetSystemTick();
fs.Impl.OutputAccessLog(rc, start, end, accessor, new U8Span(logBuffer)); fs.Impl.OutputAccessLog(rc, start, end, accessor, new U8Span(logBuffer));
} }
else else
{ {
rc = fileSystem.OpenDirectory(out accessor, subPath, mode); rc = fileSystem.OpenDirectory(ref accessor.Ref(), subPath, mode);
} }
fs.Impl.AbortIfNeeded(rc); fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
handle = new DirectoryHandle(accessor); handle = new DirectoryHandle(accessor.Release());
return Result.Success; return Result.Success;
} }

View File

@ -68,25 +68,22 @@ namespace LibHac.Fs
return FsTable.DeleteFile(new U8Span(path.GetString())); return FsTable.DeleteFile(new U8Span(path.GetString()));
} }
protected override Result DoOpenDirectory(out IDirectory directory, in Path path, OpenDirectoryMode mode) protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,
OpenDirectoryMode mode)
{ {
UnsafeHelpers.SkipParamInit(out directory);
Result rc = FsTable.GetDirectory(new U8Span(path.GetString()), out DirectoryNode dirNode); Result rc = FsTable.GetDirectory(new U8Span(path.GetString()), out DirectoryNode dirNode);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
directory = new MemoryDirectory(dirNode, mode); outDirectory.Reset(new MemoryDirectory(dirNode, mode));
return Result.Success; return Result.Success;
} }
protected override Result DoOpenFile(out IFile file, in Path path, OpenMode mode) protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode)
{ {
UnsafeHelpers.SkipParamInit(out file);
Result rc = FsTable.GetFile(new U8Span(path.GetString()), out FileNode fileNode); Result rc = FsTable.GetFile(new U8Span(path.GetString()), out FileNode fileNode);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
file = new MemoryFile(mode, fileNode.File); outFile.Reset(new MemoryFile(mode, fileNode.File));
return Result.Success; return Result.Success;
} }

View File

@ -252,10 +252,8 @@ namespace LibHac.Fs.Impl
return BaseFs.Target.GetTotalSpaceSize(out totalSpace, in sfPath); return BaseFs.Target.GetTotalSpaceSize(out totalSpace, in sfPath);
} }
protected override Result DoOpenFile(out IFile file, in Path path, OpenMode mode) protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode)
{ {
UnsafeHelpers.SkipParamInit(out file);
Result rc = GetPathForServiceObject(out PathSf sfPath, path); Result rc = GetPathForServiceObject(out PathSf sfPath, path);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
@ -265,7 +263,7 @@ namespace LibHac.Fs.Impl
rc = BaseFs.Target.OpenFile(out sfFile, in sfPath, (uint)mode); rc = BaseFs.Target.OpenFile(out sfFile, in sfPath, (uint)mode);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
file = new FileServiceObjectAdapter(sfFile); outFile.Reset(new FileServiceObjectAdapter(sfFile));
return Result.Success; return Result.Success;
} }
finally finally
@ -274,10 +272,9 @@ namespace LibHac.Fs.Impl
} }
} }
protected override Result DoOpenDirectory(out IDirectory directory, in Path path, OpenDirectoryMode mode) protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,
OpenDirectoryMode mode)
{ {
UnsafeHelpers.SkipParamInit(out directory);
Result rc = GetPathForServiceObject(out PathSf sfPath, path); Result rc = GetPathForServiceObject(out PathSf sfPath, path);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
@ -287,7 +284,7 @@ namespace LibHac.Fs.Impl
rc = BaseFs.Target.OpenDirectory(out sfDir, in sfPath, (uint)mode); rc = BaseFs.Target.OpenDirectory(out sfDir, in sfPath, (uint)mode);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
directory = new DirectoryServiceObjectAdapter(sfDir); outDirectory.Reset(new DirectoryServiceObjectAdapter(sfDir));
return Result.Success; return Result.Success;
} }
finally finally

View File

@ -33,10 +33,12 @@ namespace LibHac.FsSrv
if (!programInfo.AccessControl.CanCall(OperationType.OpenAccessFailureDetectionEventNotifier)) if (!programInfo.AccessControl.CanCall(OperationType.OpenAccessFailureDetectionEventNotifier))
return ResultFs.PermissionDenied.Log(); return ResultFs.PermissionDenied.Log();
rc = _serviceImpl.CreateNotifier(out IEventNotifier tempNotifier, processId, notifyOnDeepRetry); using var tempNotifier = new UniqueRef<IEventNotifier>();
rc = _serviceImpl.CreateNotifier(ref tempNotifier.Ref(), processId, notifyOnDeepRetry);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
notifier = new ReferenceCountedDisposable<IEventNotifier>(tempNotifier); notifier = new ReferenceCountedDisposable<IEventNotifier>(tempNotifier.Release());
return Result.Success; return Result.Success;
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using LibHac.Common;
using LibHac.FsSrv.Impl; using LibHac.FsSrv.Impl;
using LibHac.FsSrv.Sf; using LibHac.FsSrv.Sf;
using LibHac.Svc; using LibHac.Svc;
@ -30,9 +31,9 @@ namespace LibHac.FsSrv
return registry.GetProgramInfo(out programInfo, processId); return registry.GetProgramInfo(out programInfo, processId);
} }
public Result CreateNotifier(out IEventNotifier notifier, ulong processId, bool notifyOnDeepRetry) public Result CreateNotifier(ref UniqueRef<IEventNotifier> notifier, ulong processId, bool notifyOnDeepRetry)
{ {
return _eventManager.CreateNotifier(out notifier, processId, notifyOnDeepRetry); return _eventManager.CreateNotifier(ref notifier, processId, notifyOnDeepRetry);
} }
public void ResetAccessFailureDetection(ulong processId) public void ResetAccessFailureDetection(ulong processId)

View File

@ -36,9 +36,8 @@ namespace LibHac.FsSrv
ReferenceCountedDisposable<IFileSystem> tempFs = null; ReferenceCountedDisposable<IFileSystem> tempFs = null;
try try
{ {
const int pathBufferLength = 0x40;
// Hack around error CS8350. // Hack around error CS8350.
const int pathBufferLength = 0x40;
Span<byte> buffer = stackalloc byte[pathBufferLength]; Span<byte> buffer = stackalloc byte[pathBufferLength];
ref byte bufferRef = ref MemoryMarshal.GetReference(buffer); ref byte bufferRef = ref MemoryMarshal.GetReference(buffer);
Span<byte> pathBuffer = MemoryMarshal.CreateSpan(ref bufferRef, pathBufferLength); Span<byte> pathBuffer = MemoryMarshal.CreateSpan(ref bufferRef, pathBufferLength);
@ -49,11 +48,8 @@ namespace LibHac.FsSrv
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
using var path = new Path(); using var path = new Path();
var sb = new U8StringBuilder(pathBuffer); rc = PathFunctions.SetUpFixedPathSingleEntry(ref path.Ref(), pathBuffer,
sb.Append((byte)'/') CustomStorage.GetCustomStorageDirectoryName(CustomStorageId.System));
.Append(CustomStorage.GetCustomStorageDirectoryName(CustomStorageId.System));
rc = PathFunctions.SetUpFixedPath(ref path.Ref(), pathBuffer);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
tempFs = Shared.Move(ref fileSystem); tempFs = Shared.Move(ref fileSystem);
@ -66,13 +62,9 @@ namespace LibHac.FsSrv
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
using var path = new Path(); using var path = new Path();
var sb = new U8StringBuilder(pathBuffer); rc = PathFunctions.SetUpFixedPathDoubleEntry(ref path.Ref(), pathBuffer,
sb.Append((byte)'/') CommonPaths.SdCardNintendoRootDirectoryName,
.Append(CommonPaths.SdCardNintendoRootDirectoryName) CustomStorage.GetCustomStorageDirectoryName(CustomStorageId.System));
.Append((byte)'/')
.Append(CustomStorage.GetCustomStorageDirectoryName(CustomStorageId.SdCard));
rc = PathFunctions.SetUpFixedPath(ref path.Ref(), pathBuffer);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
tempFs = Shared.Move(ref fileSystem); tempFs = Shared.Move(ref fileSystem);

View File

@ -12,10 +12,12 @@ namespace LibHac.FsSrv.FsCreator
{ {
UnsafeHelpers.SkipParamInit(out subDirFileSystem); UnsafeHelpers.SkipParamInit(out subDirFileSystem);
Result rc = baseFileSystem.Target.OpenDirectory(out IDirectory dir, in path, OpenDirectoryMode.Directory); using var directory = new UniqueRef<IDirectory>();
Result rc = baseFileSystem.Target.OpenDirectory(ref directory.Ref(), in path, OpenDirectoryMode.Directory);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
dir.Dispose(); directory.Reset();
ReferenceCountedDisposable<SubdirectoryFileSystem> subFs = null; ReferenceCountedDisposable<SubdirectoryFileSystem> subFs = null;
try try

View File

@ -1,10 +1,11 @@
using LibHac.Fs; using LibHac.Common;
using LibHac.Fs;
namespace LibHac.FsSrv namespace LibHac.FsSrv
{ {
public interface ISaveDataIndexerManager public interface ISaveDataIndexerManager
{ {
Result OpenSaveDataIndexerAccessor(out SaveDataIndexerAccessor accessor, out bool neededInit, SaveDataSpaceId spaceId); Result OpenSaveDataIndexerAccessor(ref UniqueRef<SaveDataIndexerAccessor> outAccessor, out bool neededInit, SaveDataSpaceId spaceId);
void ResetIndexer(SaveDataSpaceId spaceId); void ResetIndexer(SaveDataSpaceId spaceId);
void InvalidateIndexer(SaveDataSpaceId spaceId); void InvalidateIndexer(SaveDataSpaceId spaceId);
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using LibHac.Common;
using LibHac.FsSrv.Sf; using LibHac.FsSrv.Sf;
using LibHac.Svc; using LibHac.Svc;
@ -6,7 +7,7 @@ namespace LibHac.FsSrv.Impl
{ {
public class AccessFailureDetectionEventManager public class AccessFailureDetectionEventManager
{ {
public Result CreateNotifier(out IEventNotifier notifier, ulong processId, bool notifyOnDeepRetry) public Result CreateNotifier(ref UniqueRef<IEventNotifier> notifier, ulong processId, bool notifyOnDeepRetry)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

View File

@ -1,4 +1,5 @@
using LibHac.Fs; using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
using LibHac.FsSystem; using LibHac.FsSystem;
@ -17,10 +18,10 @@ namespace LibHac.FsSrv.Impl
} }
// ReSharper disable once RedundantOverriddenMember // ReSharper disable once RedundantOverriddenMember
protected override Result DoOpenFile(out IFile file, in Path path, OpenMode mode) protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode)
{ {
// Todo: Implement // Todo: Implement
return base.DoOpenFile(out file, path, mode); return base.DoOpenFile(ref outFile, path, mode);
} }
} }
} }

View File

@ -46,10 +46,10 @@ namespace LibHac.FsSrv.Impl
} }
// ReSharper disable once RedundantOverriddenMember // ReSharper disable once RedundantOverriddenMember
protected override Result DoOpenFile(out IFile file, in Path path, OpenMode mode) protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode)
{ {
// Todo: Implement // Todo: Implement
return base.DoOpenFile(out file, path, mode); return base.DoOpenFile(ref outFile, path, mode);
} }
} }
} }

View File

@ -24,20 +24,20 @@ namespace LibHac.FsSrv.Impl
public class FileInterfaceAdapter : IFileSf public class FileInterfaceAdapter : IFileSf
{ {
private ReferenceCountedDisposable<FileSystemInterfaceAdapter> _parentFs; private ReferenceCountedDisposable<FileSystemInterfaceAdapter> _parentFs;
private IFile _baseFile; private UniqueRef<IFile> _baseFile;
private bool _allowAllOperations; private bool _allowAllOperations;
public FileInterfaceAdapter(IFile baseFile, public FileInterfaceAdapter(ref UniqueRef<IFile> baseFile,
ref ReferenceCountedDisposable<FileSystemInterfaceAdapter> parentFileSystem, bool allowAllOperations) ref ReferenceCountedDisposable<FileSystemInterfaceAdapter> parentFileSystem, bool allowAllOperations)
{ {
_baseFile = baseFile; _baseFile = new UniqueRef<IFile>(ref baseFile);
_parentFs = Shared.Move(ref parentFileSystem); _parentFs = Shared.Move(ref parentFileSystem);
_allowAllOperations = allowAllOperations; _allowAllOperations = allowAllOperations;
} }
public void Dispose() public void Dispose()
{ {
_baseFile?.Dispose(); _baseFile.Dispose();
_parentFs?.Dispose(); _parentFs?.Dispose();
} }
@ -57,7 +57,7 @@ namespace LibHac.FsSrv.Impl
for (int tryNum = 0; tryNum < maxTryCount; tryNum++) for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
{ {
rc = _baseFile.Read(out tmpBytesRead, offset, destination.Buffer.Slice(0, (int)size), option); rc = _baseFile.Get.Read(out tmpBytesRead, offset, destination.Buffer.Slice(0, (int)size), option);
// Retry on ResultDataCorrupted // Retry on ResultDataCorrupted
if (!ResultFs.DataCorrupted.Includes(rc)) if (!ResultFs.DataCorrupted.Includes(rc))
@ -81,12 +81,12 @@ namespace LibHac.FsSrv.Impl
using var scopedPriorityChanger = using var scopedPriorityChanger =
new ScopedThreadPriorityChangerByAccessPriority(ScopedThreadPriorityChangerByAccessPriority.AccessMode.Write); new ScopedThreadPriorityChangerByAccessPriority(ScopedThreadPriorityChangerByAccessPriority.AccessMode.Write);
return _baseFile.Write(offset, source.Buffer.Slice(0, (int)size), option); return _baseFile.Get.Write(offset, source.Buffer.Slice(0, (int)size), option);
} }
public Result Flush() public Result Flush()
{ {
return _baseFile.Flush(); return _baseFile.Get.Flush();
} }
public Result SetSize(long size) public Result SetSize(long size)
@ -94,7 +94,7 @@ namespace LibHac.FsSrv.Impl
if (size < 0) if (size < 0)
return ResultFs.InvalidSize.Log(); return ResultFs.InvalidSize.Log();
return _baseFile.SetSize(size); return _baseFile.Get.SetSize(size);
} }
public Result GetSize(out long size) public Result GetSize(out long size)
@ -107,7 +107,7 @@ namespace LibHac.FsSrv.Impl
for (int tryNum = 0; tryNum < maxTryCount; tryNum++) for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
{ {
rc = _baseFile.GetSize(out tmpSize); rc = _baseFile.Get.GetSize(out tmpSize);
// Retry on ResultDataCorrupted // Retry on ResultDataCorrupted
if (!ResultFs.DataCorrupted.Includes(rc)) if (!ResultFs.DataCorrupted.Includes(rc))
@ -129,7 +129,7 @@ namespace LibHac.FsSrv.Impl
{ {
Unsafe.SkipInit(out QueryRangeInfo info); Unsafe.SkipInit(out QueryRangeInfo info);
Result rc = _baseFile.OperateRange(SpanHelpers.AsByteSpan(ref info), OperationId.QueryRange, offset, Result rc = _baseFile.Get.OperateRange(SpanHelpers.AsByteSpan(ref info), OperationId.QueryRange, offset,
size, ReadOnlySpan<byte>.Empty); size, ReadOnlySpan<byte>.Empty);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
@ -137,7 +137,7 @@ namespace LibHac.FsSrv.Impl
} }
else if (operationId == (int)OperationId.InvalidateCache) else if (operationId == (int)OperationId.InvalidateCache)
{ {
Result rc = _baseFile.OperateRange(Span<byte>.Empty, OperationId.InvalidateCache, offset, size, Result rc = _baseFile.Get.OperateRange(Span<byte>.Empty, OperationId.InvalidateCache, offset, size,
ReadOnlySpan<byte>.Empty); ReadOnlySpan<byte>.Empty);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
} }
@ -165,7 +165,7 @@ namespace LibHac.FsSrv.Impl
Result rc = PermissionCheck((OperationId)operationId, this); Result rc = PermissionCheck((OperationId)operationId, this);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = _baseFile.OperateRange(outBuffer.Buffer, (OperationId)operationId, offset, size, inBuffer.Buffer); rc = _baseFile.Get.OperateRange(outBuffer.Buffer, (OperationId)operationId, offset, size, inBuffer.Buffer);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
return Result.Success; return Result.Success;
@ -179,18 +179,18 @@ namespace LibHac.FsSrv.Impl
public class DirectoryInterfaceAdapter : IDirectorySf public class DirectoryInterfaceAdapter : IDirectorySf
{ {
private ReferenceCountedDisposable<FileSystemInterfaceAdapter> _parentFs; private ReferenceCountedDisposable<FileSystemInterfaceAdapter> _parentFs;
private IDirectory _baseDirectory; private UniqueRef<IDirectory> _baseDirectory;
public DirectoryInterfaceAdapter(IDirectory baseDirectory, public DirectoryInterfaceAdapter(ref UniqueRef<IDirectory> baseDirectory,
ref ReferenceCountedDisposable<FileSystemInterfaceAdapter> parentFileSystem) ref ReferenceCountedDisposable<FileSystemInterfaceAdapter> parentFileSystem)
{ {
_baseDirectory = baseDirectory; _baseDirectory = new UniqueRef<IDirectory>(ref baseDirectory);
_parentFs = Shared.Move(ref parentFileSystem); _parentFs = Shared.Move(ref parentFileSystem);
} }
public void Dispose() public void Dispose()
{ {
_baseDirectory?.Dispose(); _baseDirectory.Dispose();
_parentFs?.Dispose(); _parentFs?.Dispose();
} }
@ -206,7 +206,7 @@ namespace LibHac.FsSrv.Impl
for (int tryNum = 0; tryNum < maxTryCount; tryNum++) for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
{ {
rc = _baseDirectory.Read(out numRead, entries); rc = _baseDirectory.Get.Read(out numRead, entries);
// Retry on ResultDataCorrupted // Retry on ResultDataCorrupted
if (!ResultFs.DataCorrupted.Includes(rc)) if (!ResultFs.DataCorrupted.Includes(rc))
@ -223,7 +223,7 @@ namespace LibHac.FsSrv.Impl
{ {
UnsafeHelpers.SkipParamInit(out entryCount); UnsafeHelpers.SkipParamInit(out entryCount);
Result rc = _baseDirectory.GetEntryCount(out long count); Result rc = _baseDirectory.Get.GetEntryCount(out long count);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
entryCount = count; entryCount = count;
@ -496,75 +496,63 @@ namespace LibHac.FsSrv.Impl
return Result.Success; return Result.Success;
} }
public Result OpenFile(out ReferenceCountedDisposable<IFileSf> file, in PathSf path, uint mode) public Result OpenFile(out ReferenceCountedDisposable<IFileSf> outFile, in PathSf path, uint mode)
{ {
const int maxTryCount = 2; const int maxTryCount = 2;
UnsafeHelpers.SkipParamInit(out file); UnsafeHelpers.SkipParamInit(out outFile);
using var pathNormalized = new Path(); using var pathNormalized = new Path();
Result rc = SetUpPath(ref pathNormalized.Ref(), in path); Result rc = SetUpPath(ref pathNormalized.Ref(), in path);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
IFile fileInterface = null; using var file = new UniqueRef<IFile>();
try
for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
{ {
for (int tryNum = 0; tryNum < maxTryCount; tryNum++) rc = _baseFileSystem.Target.OpenFile(ref file.Ref(), in pathNormalized, (OpenMode)mode);
{
rc = _baseFileSystem.Target.OpenFile(out fileInterface, in pathNormalized, (OpenMode)mode);
// Retry on ResultDataCorrupted // Retry on ResultDataCorrupted
if (!ResultFs.DataCorrupted.Includes(rc)) if (!ResultFs.DataCorrupted.Includes(rc))
break; break;
}
if (rc.IsFailure()) return rc;
ReferenceCountedDisposable<FileSystemInterfaceAdapter> selfReference = _selfReference.AddReference();
var adapter = new FileInterfaceAdapter(Shared.Move(ref fileInterface), ref selfReference, _allowAllOperations);
file = new ReferenceCountedDisposable<IFileSf>(adapter);
return Result.Success;
}
finally
{
fileInterface?.Dispose();
} }
if (rc.IsFailure()) return rc;
ReferenceCountedDisposable<FileSystemInterfaceAdapter> selfReference = _selfReference.AddReference();
var adapter = new FileInterfaceAdapter(ref file.Ref(), ref selfReference, _allowAllOperations);
outFile = new ReferenceCountedDisposable<IFileSf>(adapter);
return Result.Success;
} }
public Result OpenDirectory(out ReferenceCountedDisposable<IDirectorySf> directory, in PathSf path, uint mode) public Result OpenDirectory(out ReferenceCountedDisposable<IDirectorySf> outDirectory, in PathSf path, uint mode)
{ {
const int maxTryCount = 2; const int maxTryCount = 2;
UnsafeHelpers.SkipParamInit(out directory); UnsafeHelpers.SkipParamInit(out outDirectory);
using var pathNormalized = new Path(); using var pathNormalized = new Path();
Result rc = SetUpPath(ref pathNormalized.Ref(), in path); Result rc = SetUpPath(ref pathNormalized.Ref(), in path);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
IDirectory dirInterface = null; using var directory = new UniqueRef<IDirectory>();
try
for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
{ {
for (int tryNum = 0; tryNum < maxTryCount; tryNum++) rc = _baseFileSystem.Target.OpenDirectory(ref directory.Ref(), in pathNormalized,
{ (OpenDirectoryMode)mode);
rc = _baseFileSystem.Target.OpenDirectory(out dirInterface, in pathNormalized,
(OpenDirectoryMode)mode);
// Retry on ResultDataCorrupted // Retry on ResultDataCorrupted
if (!ResultFs.DataCorrupted.Includes(rc)) if (!ResultFs.DataCorrupted.Includes(rc))
break; break;
}
if (rc.IsFailure()) return rc;
ReferenceCountedDisposable<FileSystemInterfaceAdapter> selfReference = _selfReference.AddReference();
var adapter = new DirectoryInterfaceAdapter(dirInterface, ref selfReference);
directory = new ReferenceCountedDisposable<IDirectorySf>(adapter);
return Result.Success;
}
finally
{
dirInterface?.Dispose();
} }
if (rc.IsFailure()) return rc;
ReferenceCountedDisposable<FileSystemInterfaceAdapter> selfReference = _selfReference.AddReference();
var adapter = new DirectoryInterfaceAdapter(ref directory.Ref(), ref selfReference);
outDirectory = new ReferenceCountedDisposable<IDirectorySf>(adapter);
return Result.Success;
} }
public Result Commit() public Result Commit()

View File

@ -1,10 +1,11 @@
using System; using System;
using LibHac.Common;
using LibHac.FsSystem; using LibHac.FsSystem;
namespace LibHac.FsSrv.Impl namespace LibHac.FsSrv.Impl
{ {
public interface IEntryOpenCountSemaphoreManager : IDisposable public interface IEntryOpenCountSemaphoreManager : IDisposable
{ {
Result TryAcquireEntryOpenCountSemaphore(out IUniqueLock semaphore); Result TryAcquireEntryOpenCountSemaphore(ref UniqueRef<IUniqueLock> outSemaphore);
} }
} }

View File

@ -8,50 +8,48 @@ namespace LibHac.FsSrv.Impl
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
public abstract class IResultConvertFile : IFile public abstract class IResultConvertFile : IFile
{ {
protected IFile BaseFile; protected UniqueRef<IFile> BaseFile;
protected IResultConvertFile(IFile baseFile) protected IResultConvertFile(ref UniqueRef<IFile> baseFile)
{ {
BaseFile = baseFile; BaseFile = new UniqueRef<IFile>(ref baseFile);
} }
public override void Dispose() public override void Dispose()
{ {
BaseFile?.Dispose(); BaseFile.Dispose();
BaseFile = null;
base.Dispose(); base.Dispose();
} }
protected override Result DoRead(out long bytesRead, long offset, Span<byte> destination, in ReadOption option) protected override Result DoRead(out long bytesRead, long offset, Span<byte> destination, in ReadOption option)
{ {
return ConvertResult(BaseFile.Read(out bytesRead, offset, destination, option)); return ConvertResult(BaseFile.Get.Read(out bytesRead, offset, destination, option));
} }
protected override Result DoWrite(long offset, ReadOnlySpan<byte> source, in WriteOption option) protected override Result DoWrite(long offset, ReadOnlySpan<byte> source, in WriteOption option)
{ {
return ConvertResult(BaseFile.Write(offset, source, option)); return ConvertResult(BaseFile.Get.Write(offset, source, option));
} }
protected override Result DoFlush() protected override Result DoFlush()
{ {
return ConvertResult(BaseFile.Flush()); return ConvertResult(BaseFile.Get.Flush());
} }
protected override Result DoSetSize(long size) protected override Result DoSetSize(long size)
{ {
return ConvertResult(BaseFile.SetSize(size)); return ConvertResult(BaseFile.Get.SetSize(size));
} }
protected override Result DoGetSize(out long size) protected override Result DoGetSize(out long size)
{ {
return ConvertResult(BaseFile.GetSize(out size)); return ConvertResult(BaseFile.Get.GetSize(out size));
} }
protected override Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size, protected override Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size,
ReadOnlySpan<byte> inBuffer) ReadOnlySpan<byte> inBuffer)
{ {
return ConvertResult(BaseFile.OperateRange(outBuffer, operationId, offset, size, inBuffer)); return ConvertResult(BaseFile.Get.OperateRange(outBuffer, operationId, offset, size, inBuffer));
} }
protected abstract Result ConvertResult(Result result); protected abstract Result ConvertResult(Result result);
@ -60,29 +58,27 @@ namespace LibHac.FsSrv.Impl
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
public abstract class IResultConvertDirectory : IDirectory public abstract class IResultConvertDirectory : IDirectory
{ {
protected IDirectory BaseDirectory; protected UniqueRef<IDirectory> BaseDirectory;
protected IResultConvertDirectory(IDirectory baseDirectory) protected IResultConvertDirectory(ref UniqueRef<IDirectory> baseDirectory)
{ {
BaseDirectory = baseDirectory; BaseDirectory = new UniqueRef<IDirectory>(ref baseDirectory);
} }
public override void Dispose() public override void Dispose()
{ {
BaseDirectory?.Dispose(); BaseDirectory.Dispose();
BaseDirectory = null;
base.Dispose(); base.Dispose();
} }
protected override Result DoRead(out long entriesRead, Span<DirectoryEntry> entryBuffer) protected override Result DoRead(out long entriesRead, Span<DirectoryEntry> entryBuffer)
{ {
return ConvertResult(BaseDirectory.Read(out entriesRead, entryBuffer)); return ConvertResult(BaseDirectory.Get.Read(out entriesRead, entryBuffer));
} }
protected override Result DoGetEntryCount(out long entryCount) protected override Result DoGetEntryCount(out long entryCount)
{ {
return ConvertResult(BaseDirectory.GetEntryCount(out entryCount)); return ConvertResult(BaseDirectory.Get.GetEntryCount(out entryCount));
} }
protected abstract Result ConvertResult(Result result); protected abstract Result ConvertResult(Result result);
@ -150,9 +146,9 @@ namespace LibHac.FsSrv.Impl
return ConvertResult(BaseFileSystem.Target.GetEntryType(out entryType, path)); return ConvertResult(BaseFileSystem.Target.GetEntryType(out entryType, path));
} }
protected abstract override Result DoOpenFile(out IFile file, in Path path, OpenMode mode); protected abstract override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode);
protected abstract override Result DoOpenDirectory(out IDirectory directory, in Path path, protected abstract override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,
OpenDirectoryMode mode); OpenDirectoryMode mode);
protected override Result DoCommit() protected override Result DoCommit()

View File

@ -1,4 +1,5 @@
using System; using System;
using LibHac.Common;
using LibHac.Fs; using LibHac.Fs;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
using LibHac.Util; using LibHac.Util;
@ -25,6 +26,6 @@ namespace LibHac.FsSrv.Impl
Result SwapSaveDataKeyAndState(SaveDataSpaceId spaceId, ulong saveDataId1, ulong saveDataId2); Result SwapSaveDataKeyAndState(SaveDataSpaceId spaceId, ulong saveDataId1, ulong saveDataId2);
Result SetSaveDataState(SaveDataSpaceId spaceId, ulong saveDataId, SaveDataState state); Result SetSaveDataState(SaveDataSpaceId spaceId, ulong saveDataId, SaveDataState state);
Result SetSaveDataRank(SaveDataSpaceId spaceId, ulong saveDataId, SaveDataRank rank); Result SetSaveDataRank(SaveDataSpaceId spaceId, ulong saveDataId, SaveDataRank rank);
Result OpenSaveDataIndexerAccessor(out SaveDataIndexerAccessor accessor, SaveDataSpaceId spaceId); Result OpenSaveDataIndexerAccessor(ref UniqueRef<SaveDataIndexerAccessor> outAccessor, SaveDataSpaceId spaceId);
} }
} }

View File

@ -294,92 +294,84 @@ namespace LibHac.FsSrv.Impl
Result rc = PathFunctions.SetUpFixedPath(ref contextFilePath.Ref(), CommitContextFileName); Result rc = PathFunctions.SetUpFixedPath(ref contextFilePath.Ref(), CommitContextFileName);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
IFile contextFile = null; // Read the multi-commit context
using var contextFile = new UniqueRef<IFile>();
rc = contextFs.OpenFile(ref contextFile.Ref(), in contextFilePath, OpenMode.ReadWrite);
if (rc.IsFailure()) return rc;
Unsafe.SkipInit(out Context context);
rc = contextFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref context), ReadOption.None);
if (rc.IsFailure()) return rc;
// Note: Nintendo doesn't check if the proper amount of bytes were read, but it
// doesn't really matter since the context is validated.
if (context.Version > CurrentCommitContextVersion)
return ResultFs.InvalidMultiCommitContextVersion.Log();
// All the file systems in the multi-commit must have been at least provisionally committed
// before we can try to recover the commit.
if (context.State != CommitState.ProvisionallyCommitted)
return ResultFs.InvalidMultiCommitContextState.Log();
// Keep track of the first error that occurs during the recovery
Result recoveryResult = Result.Success;
int saveCount = 0;
Span<SaveDataInfo> savesToRecover = stackalloc SaveDataInfo[MaxFileSystemCount];
ReferenceCountedDisposable<SaveDataInfoReaderImpl> infoReader = null;
try try
{ {
// Read the multi-commit context using var accessor = new UniqueRef<SaveDataIndexerAccessor>();
rc = contextFs.OpenFile(out contextFile, in contextFilePath, OpenMode.ReadWrite); rc = saveService.OpenSaveDataIndexerAccessor(ref accessor.Ref(), out _, SaveDataSpaceId.User);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
Unsafe.SkipInit(out Context context); rc = accessor.Get.Indexer.OpenSaveDataInfoReader(out infoReader);
rc = contextFile.Read(out _, 0, SpanHelpers.AsByteSpan(ref context), ReadOption.None);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
// Note: Nintendo doesn't check if the proper amount of bytes were read, but it // Iterate through all the saves to find any provisionally committed save data
// doesn't really matter since the context is validated. while (true)
if (context.Version > CurrentCommitContextVersion)
return ResultFs.InvalidMultiCommitContextVersion.Log();
// All the file systems in the multi-commit must have been at least provisionally committed
// before we can try to recover the commit.
if (context.State != CommitState.ProvisionallyCommitted)
return ResultFs.InvalidMultiCommitContextState.Log();
// Keep track of the first error that occurs during the recovery
Result recoveryResult = Result.Success;
int saveCount = 0;
Span<SaveDataInfo> savesToRecover = stackalloc SaveDataInfo[MaxFileSystemCount];
SaveDataIndexerAccessor accessor = null;
ReferenceCountedDisposable<SaveDataInfoReaderImpl> infoReader = null;
try
{ {
rc = saveService.OpenSaveDataIndexerAccessor(out accessor, out _, SaveDataSpaceId.User); Unsafe.SkipInit(out SaveDataInfo info);
rc = infoReader.Target.Read(out long readCount, OutBuffer.FromStruct(ref info));
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = accessor.Indexer.OpenSaveDataInfoReader(out infoReader); // Break once we're done iterating all save data
if (rc.IsFailure()) return rc; if (readCount == 0)
break;
// Iterate through all the saves to find any provisionally committed save data rc = multiCommitInterface.IsProvisionallyCommittedSaveData(out bool isProvisionallyCommitted,
while (true) in info);
// Note: Some saves could be missed if there are more than MaxFileSystemCount
// provisionally committed saves. Not sure why Nintendo doesn't catch this.
if (rc.IsSuccess() && isProvisionallyCommitted && saveCount < MaxFileSystemCount)
{ {
Unsafe.SkipInit(out SaveDataInfo info); savesToRecover[saveCount] = info;
saveCount++;
rc = infoReader.Target.Read(out long readCount, OutBuffer.FromStruct(ref info));
if (rc.IsFailure()) return rc;
// Break once we're done iterating all save data
if (readCount == 0)
break;
rc = multiCommitInterface.IsProvisionallyCommittedSaveData(out bool isProvisionallyCommitted,
in info);
// Note: Some saves could be missed if there are more than MaxFileSystemCount
// provisionally committed saves. Not sure why Nintendo doesn't catch this.
if (rc.IsSuccess() && isProvisionallyCommitted && saveCount < MaxFileSystemCount)
{
savesToRecover[saveCount] = info;
saveCount++;
}
} }
} }
finally
{
accessor?.Dispose();
infoReader?.Dispose();
}
// Recover the saves by finishing their commits.
// All file systems will try to be recovered, even if one fails.
// If any commits fail, the result from the first failed recovery will be returned.
for (int i = 0; i < saveCount; i++)
{
rc = multiCommitInterface.RecoverProvisionallyCommittedSaveData(in savesToRecover[i], false);
if (rc.IsFailure() && !recoveryResult.IsFailure())
{
recoveryResult = rc;
}
}
return recoveryResult;
} }
finally finally
{ {
contextFile?.Dispose(); infoReader?.Dispose();
} }
// Recover the saves by finishing their commits.
// All file systems will try to be recovered, even if one fails.
// If any commits fail, the result from the first failed recovery will be returned.
for (int i = 0; i < saveCount; i++)
{
rc = multiCommitInterface.RecoverProvisionallyCommittedSaveData(in savesToRecover[i], false);
if (rc.IsFailure() && !recoveryResult.IsFailure())
{
recoveryResult = rc;
}
}
return recoveryResult;
} }
/// <summary> /// <summary>
@ -410,14 +402,14 @@ namespace LibHac.FsSrv.Impl
int saveCount = 0; int saveCount = 0;
Span<SaveDataInfo> savesToRecover = stackalloc SaveDataInfo[MaxFileSystemCount]; Span<SaveDataInfo> savesToRecover = stackalloc SaveDataInfo[MaxFileSystemCount];
SaveDataIndexerAccessor accessor = null;
ReferenceCountedDisposable<SaveDataInfoReaderImpl> infoReader = null; ReferenceCountedDisposable<SaveDataInfoReaderImpl> infoReader = null;
try try
{ {
rc = saveService.OpenSaveDataIndexerAccessor(out accessor, out _, SaveDataSpaceId.User); using var accessor = new UniqueRef<SaveDataIndexerAccessor>();
rc = saveService.OpenSaveDataIndexerAccessor(ref accessor.Ref(), out _, SaveDataSpaceId.User);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = accessor.Indexer.OpenSaveDataInfoReader(out infoReader); rc = accessor.Get.Indexer.OpenSaveDataInfoReader(out infoReader);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
// Iterate through all the saves to find any provisionally committed save data // Iterate through all the saves to find any provisionally committed save data
@ -446,7 +438,6 @@ namespace LibHac.FsSrv.Impl
} }
finally finally
{ {
accessor?.Dispose();
infoReader?.Dispose(); infoReader?.Dispose();
} }
@ -516,8 +507,8 @@ namespace LibHac.FsSrv.Impl
rc = PathFunctions.SetUpFixedPath(ref contextFilePath.Ref(), CommitContextFileName); rc = PathFunctions.SetUpFixedPath(ref contextFilePath.Ref(), CommitContextFileName);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = fileSystem.Target.OpenFile(out IFile file, in contextFilePath, OpenMode.Read); using var file = new UniqueRef<IFile>();
file?.Dispose(); rc = fileSystem.Target.OpenFile(ref file.Ref(), in contextFilePath, OpenMode.Read);
if (rc.IsFailure()) if (rc.IsFailure())
{ {
@ -591,12 +582,10 @@ namespace LibHac.FsSrv.Impl
Result rc = PathFunctions.SetUpFixedPath(ref contextFilePath.Ref(), CommitContextFileName); Result rc = PathFunctions.SetUpFixedPath(ref contextFilePath.Ref(), CommitContextFileName);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
IFile contextFile = null; // Open context file and create if it doesn't exist
using (var contextFile = new UniqueRef<IFile>())
try
{ {
// Open context file and create if it doesn't exist rc = _fileSystem.OpenFile(ref contextFile.Ref(), in contextFilePath, OpenMode.Read);
rc = _fileSystem.OpenFile(out contextFile, in contextFilePath, OpenMode.Read);
if (rc.IsFailure()) if (rc.IsFailure())
{ {
@ -606,18 +595,14 @@ namespace LibHac.FsSrv.Impl
rc = _fileSystem.CreateFile(in contextFilePath, CommitContextFileSize); rc = _fileSystem.CreateFile(in contextFilePath, CommitContextFileSize);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = _fileSystem.OpenFile(out contextFile, in contextFilePath, OpenMode.Read); rc = _fileSystem.OpenFile(ref contextFile.Ref(), in contextFilePath, OpenMode.Read);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
} }
} }
finally
{
contextFile?.Dispose();
}
try using (var contextFile = new UniqueRef<IFile>())
{ {
rc = _fileSystem.OpenFile(out contextFile, in contextFilePath, OpenMode.ReadWrite); rc = _fileSystem.OpenFile(ref contextFile.Ref(), in contextFilePath, OpenMode.ReadWrite);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
_context.Version = CurrentCommitContextVersion; _context.Version = CurrentCommitContextVersion;
@ -626,16 +611,12 @@ namespace LibHac.FsSrv.Impl
_context.Counter = counter; _context.Counter = counter;
// Write the initial context to the file // Write the initial context to the file
rc = contextFile.Write(0, SpanHelpers.AsByteSpan(ref _context), WriteOption.None); rc = contextFile.Get.Write(0, SpanHelpers.AsByteSpan(ref _context), WriteOption.None);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = contextFile.Flush(); rc = contextFile.Get.Flush();
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
} }
finally
{
contextFile?.Dispose();
}
rc = _fileSystem.Commit(); rc = _fileSystem.Commit();
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
@ -650,29 +631,23 @@ namespace LibHac.FsSrv.Impl
/// <returns>The <see cref="Result"/> of the operation.</returns> /// <returns>The <see cref="Result"/> of the operation.</returns>
public Result CommitProvisionallyDone() public Result CommitProvisionallyDone()
{ {
using var contextFilePath = new Fs.Path(); using (var contextFilePath = new Fs.Path())
Result rc = PathFunctions.SetUpFixedPath(ref contextFilePath.Ref(), CommitContextFileName);
if (rc.IsFailure()) return rc;
IFile contextFile = null;
try
{ {
rc = _fileSystem.OpenFile(out contextFile, in contextFilePath, OpenMode.ReadWrite); Result rc = PathFunctions.SetUpFixedPath(ref contextFilePath.Ref(), CommitContextFileName);
if (rc.IsFailure()) return rc;
using var contextFile = new UniqueRef<IFile>();
rc = _fileSystem.OpenFile(ref contextFile.Ref(), in contextFilePath, OpenMode.ReadWrite);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
_context.State = CommitState.ProvisionallyCommitted; _context.State = CommitState.ProvisionallyCommitted;
rc = contextFile.Write(0, SpanHelpers.AsByteSpan(ref _context), WriteOption.None); rc = contextFile.Get.Write(0, SpanHelpers.AsByteSpan(ref _context), WriteOption.None);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = contextFile.Flush(); rc = contextFile.Get.Flush();
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
} }
finally
{
contextFile?.Dispose();
}
return _fileSystem.Commit(); return _fileSystem.Commit();
} }

View File

@ -8,27 +8,27 @@ namespace LibHac.FsSrv.Impl
internal class OpenCountFileSystem : ForwardingFileSystem internal class OpenCountFileSystem : ForwardingFileSystem
{ {
private ReferenceCountedDisposable<IEntryOpenCountSemaphoreManager> _entryCountSemaphore; private ReferenceCountedDisposable<IEntryOpenCountSemaphoreManager> _entryCountSemaphore;
private IUniqueLock _mountCountSemaphore; private UniqueRef<IUniqueLock> _mountCountSemaphore;
protected OpenCountFileSystem(ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, public OpenCountFileSystem(ref ReferenceCountedDisposable<IFileSystem> baseFileSystem,
ref ReferenceCountedDisposable<IEntryOpenCountSemaphoreManager> entryCountSemaphore) : base( ref ReferenceCountedDisposable<IEntryOpenCountSemaphoreManager> entryCountSemaphore) : base(
ref baseFileSystem) ref baseFileSystem)
{ {
Shared.Move(out _entryCountSemaphore, ref entryCountSemaphore); Shared.Move(out _entryCountSemaphore, ref entryCountSemaphore);
} }
protected OpenCountFileSystem(ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, public OpenCountFileSystem(ref ReferenceCountedDisposable<IFileSystem> baseFileSystem,
ref ReferenceCountedDisposable<IEntryOpenCountSemaphoreManager> entryCountSemaphore, ref ReferenceCountedDisposable<IEntryOpenCountSemaphoreManager> entryCountSemaphore,
ref IUniqueLock mountCountSemaphore) : base(ref baseFileSystem) ref UniqueRef<IUniqueLock> mountCountSemaphore) : base(ref baseFileSystem)
{ {
Shared.Move(out _entryCountSemaphore, ref entryCountSemaphore); Shared.Move(out _entryCountSemaphore, ref entryCountSemaphore);
Shared.Move(out _mountCountSemaphore, ref mountCountSemaphore); _mountCountSemaphore = new UniqueRef<IUniqueLock>(ref mountCountSemaphore);
} }
public static ReferenceCountedDisposable<IFileSystem> CreateShared( public static ReferenceCountedDisposable<IFileSystem> CreateShared(
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, ref ReferenceCountedDisposable<IFileSystem> baseFileSystem,
ref ReferenceCountedDisposable<IEntryOpenCountSemaphoreManager> entryCountSemaphore, ref ReferenceCountedDisposable<IEntryOpenCountSemaphoreManager> entryCountSemaphore,
ref IUniqueLock mountCountSemaphore) ref UniqueRef<IUniqueLock> mountCountSemaphore)
{ {
var filesystem = var filesystem =
new OpenCountFileSystem(ref baseFileSystem, ref entryCountSemaphore, ref mountCountSemaphore); new OpenCountFileSystem(ref baseFileSystem, ref entryCountSemaphore, ref mountCountSemaphore);
@ -47,23 +47,24 @@ namespace LibHac.FsSrv.Impl
} }
// ReSharper disable once RedundantOverriddenMember // ReSharper disable once RedundantOverriddenMember
protected override Result DoOpenFile(out IFile file, in Path path, OpenMode mode) protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode)
{ {
// Todo: Implement // Todo: Implement
return base.DoOpenFile(out file, path, mode); return base.DoOpenFile(ref outFile, path, mode);
} }
// ReSharper disable once RedundantOverriddenMember // ReSharper disable once RedundantOverriddenMember
protected override Result DoOpenDirectory(out IDirectory directory, in Path path, OpenDirectoryMode mode) protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,
OpenDirectoryMode mode)
{ {
// Todo: Implement // Todo: Implement
return base.DoOpenDirectory(out directory, path, mode); return base.DoOpenDirectory(ref outDirectory, path, mode);
} }
public override void Dispose() public override void Dispose()
{ {
_entryCountSemaphore?.Dispose(); _entryCountSemaphore?.Dispose();
_mountCountSemaphore?.Dispose(); _mountCountSemaphore.Dispose();
base.Dispose(); base.Dispose();
} }
} }

View File

@ -132,7 +132,7 @@ namespace LibHac.FsSrv.Impl
/// </summary> /// </summary>
public class SaveDataResultConvertFile : IResultConvertFile public class SaveDataResultConvertFile : IResultConvertFile
{ {
public SaveDataResultConvertFile(IFile baseFile) : base(baseFile) public SaveDataResultConvertFile(ref UniqueRef<IFile> baseFile) : base(ref baseFile)
{ {
} }
@ -148,7 +148,7 @@ namespace LibHac.FsSrv.Impl
/// </summary> /// </summary>
public class SaveDataResultConvertDirectory : IResultConvertDirectory public class SaveDataResultConvertDirectory : IResultConvertDirectory
{ {
public SaveDataResultConvertDirectory(IDirectory baseDirectory) : base(baseDirectory) public SaveDataResultConvertDirectory(ref UniqueRef<IDirectory> baseDirectory) : base(ref baseDirectory)
{ {
} }
@ -176,25 +176,24 @@ namespace LibHac.FsSrv.Impl
return new ReferenceCountedDisposable<IFileSystem>(resultConvertFileSystem); return new ReferenceCountedDisposable<IFileSystem>(resultConvertFileSystem);
} }
protected override Result DoOpenFile(out IFile file, in Path path, OpenMode mode) protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode)
{ {
UnsafeHelpers.SkipParamInit(out file); using var file = new UniqueRef<IFile>();
Result rc = ConvertResult(BaseFileSystem.Target.OpenFile(ref file.Ref(), path, mode));
Result rc = ConvertResult(BaseFileSystem.Target.OpenFile(out IFile tempFile, path, mode));
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
file = new SaveDataResultConvertFile(tempFile); outFile.Reset(new SaveDataResultConvertFile(ref file.Ref()));
return Result.Success; return Result.Success;
} }
protected override Result DoOpenDirectory(out IDirectory directory, in Path path, OpenDirectoryMode mode) protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,
OpenDirectoryMode mode)
{ {
UnsafeHelpers.SkipParamInit(out directory); using var directory = new UniqueRef<IDirectory>();
Result rc = ConvertResult(BaseFileSystem.Target.OpenDirectory(ref directory.Ref(), path, mode));
Result rc = ConvertResult(BaseFileSystem.Target.OpenDirectory(out IDirectory tempDirectory, path, mode));
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
directory = new SaveDataResultConvertDirectory(tempDirectory); outDirectory.Reset(new SaveDataResultConvertDirectory(ref directory.Ref()));
return Result.Success; return Result.Success;
} }

View File

@ -27,10 +27,11 @@ namespace LibHac.FsSrv.Impl
} }
// Check if the directory exists // Check if the directory exists
Result rc = baseFileSystem.Target.OpenDirectory(out IDirectory dir, rootPath, OpenDirectoryMode.Directory); using var dir = new UniqueRef<IDirectory>();
Result rc = baseFileSystem.Target.OpenDirectory(ref dir.Ref(), rootPath, OpenDirectoryMode.Directory);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
dir.Dispose(); dir.Reset();
var fs = new SubdirectoryFileSystem(ref baseFileSystem); var fs = new SubdirectoryFileSystem(ref baseFileSystem);
using (var subDirFs = new ReferenceCountedDisposable<SubdirectoryFileSystem>(fs)) using (var subDirFs = new ReferenceCountedDisposable<SubdirectoryFileSystem>(fs))

View File

@ -593,12 +593,12 @@ namespace LibHac.FsSrv
ServiceImpl.IncrementRomFsRecoveredByInvalidateCacheCount(); ServiceImpl.IncrementRomFsRecoveredByInvalidateCacheCount();
} }
private Result TryAcquireAddOnContentOpenCountSemaphore(out IUniqueLock semaphoreLock) private Result TryAcquireAddOnContentOpenCountSemaphore(ref UniqueRef<IUniqueLock> outSemaphoreLock)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
private Result TryAcquireRomMountCountSemaphore(out IUniqueLock semaphoreLock) private Result TryAcquireRomMountCountSemaphore(ref UniqueRef<IUniqueLock> outSemaphoreLock)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

View File

@ -634,16 +634,14 @@ namespace LibHac.FsSrv
nspPathLen += 4; nspPathLen += 4;
if (nspPathLen > FsPath.MaxLength + 1) using var pathNsp = new Path();
return ResultFs.TooLongPath.Log(); Result rc = pathNsp.InitializeWithNormalization(path, nspPathLen);
Result rc = FsPath.FromSpan(out FsPath nspPath, path.Slice(0, nspPathLen));
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
var storage = new FileStorageBasedFileSystem(); var storage = new FileStorageBasedFileSystem();
using var nspFileStorage = new ReferenceCountedDisposable<FileStorageBasedFileSystem>(storage); using var nspFileStorage = new ReferenceCountedDisposable<FileStorageBasedFileSystem>(storage);
rc = nspFileStorage.Target.Initialize(ref baseFileSystem, new U8Span(nspPath.Str), OpenMode.Read); rc = nspFileStorage.Target.Initialize(ref baseFileSystem, in pathNsp, OpenMode.Read);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = _config.PartitionFsCreator.Create(out fileSystem, nspFileStorage.AddReference<IStorage>()); rc = _config.PartitionFsCreator.Create(out fileSystem, nspFileStorage.AddReference<IStorage>());
@ -664,7 +662,11 @@ namespace LibHac.FsSrv
// Todo: Create ref-counted storage // Todo: Create ref-counted storage
var ncaFileStorage = new FileStorageBasedFileSystem(); var ncaFileStorage = new FileStorageBasedFileSystem();
Result rc = ncaFileStorage.Initialize(ref baseFileSystem, path, OpenMode.Read); using var pathNca = new Path();
Result rc = pathNca.InitializeWithNormalization(path);
if (rc.IsFailure()) return rc;
rc = ncaFileStorage.Initialize(ref baseFileSystem, in pathNca, OpenMode.Read);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = _config.StorageOnNcaCreator.OpenNca(out Nca ncaTemp, ncaFileStorage); rc = _config.StorageOnNcaCreator.OpenNca(out Nca ncaTemp, ncaFileStorage);

File diff suppressed because it is too large Load Diff

View File

@ -343,11 +343,9 @@ namespace LibHac.FsSrv
} }
} }
public Result OpenSaveDataMeta(out IFile metaFile, ulong saveDataId, SaveDataSpaceId spaceId, public Result OpenSaveDataMeta(ref UniqueRef<IFile> outMetaFile, ulong saveDataId, SaveDataSpaceId spaceId,
SaveDataMetaType metaType) SaveDataMetaType metaType)
{ {
UnsafeHelpers.SkipParamInit(out metaFile);
ReferenceCountedDisposable<IFileSystem> metaDirFs = null; ReferenceCountedDisposable<IFileSystem> metaDirFs = null;
try try
{ {
@ -365,7 +363,7 @@ namespace LibHac.FsSrv
(uint)metaType); (uint)metaType);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
return metaDirFs.Target.OpenFile(out metaFile, in saveDataMetaName, OpenMode.ReadWrite); return metaDirFs.Target.OpenFile(ref outMetaFile, in saveDataMetaName, OpenMode.ReadWrite);
} }
finally finally
{ {
@ -875,25 +873,19 @@ namespace LibHac.FsSrv
{ {
UnsafeHelpers.SkipParamInit(out count); UnsafeHelpers.SkipParamInit(out count);
SaveDataIndexerAccessor accessor = null; using var accessor = new UniqueRef<SaveDataIndexerAccessor>();
try
{
Result rc = OpenSaveDataIndexerAccessor(out accessor, out bool _, SaveDataSpaceId.User);
if (rc.IsFailure()) return rc;
count = accessor.Indexer.GetIndexCount(); Result rc = OpenSaveDataIndexerAccessor(ref accessor.Ref(), out bool _, SaveDataSpaceId.User);
return Result.Success; if (rc.IsFailure()) return rc;
}
finally count = accessor.Get.Indexer.GetIndexCount();
{ return Result.Success;
accessor?.Dispose();
}
} }
public Result OpenSaveDataIndexerAccessor(out SaveDataIndexerAccessor accessor, out bool neededInit, public Result OpenSaveDataIndexerAccessor(ref UniqueRef<SaveDataIndexerAccessor> outAccessor, out bool neededInit,
SaveDataSpaceId spaceId) SaveDataSpaceId spaceId)
{ {
return _config.SaveIndexerManager.OpenSaveDataIndexerAccessor(out accessor, out neededInit, spaceId); return _config.SaveIndexerManager.OpenSaveDataIndexerAccessor(ref outAccessor, out neededInit, spaceId);
} }
public void ResetTemporaryStorageIndexer() public void ResetTemporaryStorageIndexer()

View File

@ -49,13 +49,13 @@ namespace LibHac.FsSrv
/// The returned <see cref="SaveDataIndexerAccessor"/> will have exclusive access to the requested indexer. /// The returned <see cref="SaveDataIndexerAccessor"/> will have exclusive access to the requested indexer.
/// The accessor must be disposed after use. /// The accessor must be disposed after use.
/// </remarks> /// </remarks>
/// <param name="accessor">If the method returns successfully, contains the created accessor.</param> /// <param name="outAccessor">If the method returns successfully, contains the created accessor.</param>
/// <param name="neededInit">If the method returns successfully, contains <see langword="true"/> /// <param name="neededInit">If the method returns successfully, contains <see langword="true"/>
/// if the indexer needed to be initialized.</param> /// if the indexer needed to be initialized.</param>
/// <param name="spaceId">The <see cref="SaveDataSpaceId"/> of the indexer to open.</param> /// <param name="spaceId">The <see cref="SaveDataSpaceId"/> of the indexer to open.</param>
/// <returns>The <see cref="Result"/> of the operation.</returns> /// <returns>The <see cref="Result"/> of the operation.</returns>
public Result OpenSaveDataIndexerAccessor(out SaveDataIndexerAccessor accessor, out bool neededInit, public Result OpenSaveDataIndexerAccessor(ref UniqueRef<SaveDataIndexerAccessor> outAccessor,
SaveDataSpaceId spaceId) out bool neededInit, SaveDataSpaceId spaceId)
{ {
UnsafeHelpers.SkipParamInit(out neededInit); UnsafeHelpers.SkipParamInit(out neededInit);
@ -145,12 +145,12 @@ namespace LibHac.FsSrv
break; break;
default: default:
accessor = default; outAccessor = default;
return ResultFs.InvalidArgument.Log(); return ResultFs.InvalidArgument.Log();
} }
accessor = new SaveDataIndexerAccessor(indexer, ref indexerLock); outAccessor.Reset(new SaveDataIndexerAccessor(indexer, ref indexerLock));
return Result.Success; return Result.Success;
} }
finally finally

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Common; using LibHac.Common;
using LibHac.Diag; using LibHac.Diag;
using LibHac.Fs; using LibHac.Fs;
@ -68,10 +69,10 @@ namespace LibHac.FsSrv
_mutex.Initialize(); _mutex.Initialize();
} }
public Result Initialize(ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path, OpenMode mode, public Result Initialize(ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, in Path path, OpenMode mode,
OpenType type) OpenType type)
{ {
Result rc = Initialize(ref baseFileSystem, path, mode); Result rc = Initialize(ref baseFileSystem, in path, mode);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
return SetOpenType(type); return SetOpenType(type);
@ -328,12 +329,17 @@ namespace LibHac.FsSrv
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, SaveDataSpaceId spaceId, ulong saveDataId, ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, SaveDataSpaceId spaceId, ulong saveDataId,
OpenMode mode, Optional<SaveDataOpenTypeSetFileStorage.OpenType> type) OpenMode mode, Optional<SaveDataOpenTypeSetFileStorage.OpenType> type)
{ {
Result rc;
UnsafeHelpers.SkipParamInit(out saveDataStorage); UnsafeHelpers.SkipParamInit(out saveDataStorage);
Span<byte> saveImageName = stackalloc byte[0x30]; // Hack around error CS8350.
var sb = new U8StringBuilder(saveImageName); const int bufferLength = 0x12;
sb.Append((byte)'/').AppendFormat(saveDataId, 'x', 16); Span<byte> buffer = stackalloc byte[bufferLength];
ref byte bufferRef = ref MemoryMarshal.GetReference(buffer);
Span<byte> saveImageNameBuffer = MemoryMarshal.CreateSpan(ref bufferRef, bufferLength);
using var saveImageName = new Path();
Result rc = PathFunctions.SetUpFixedPathSaveId(ref saveImageName.Ref(), saveImageNameBuffer, saveDataId);
if (rc.IsFailure()) return rc;
// If an open type isn't specified, open the save without the shared file storage layer // If an open type isn't specified, open the save without the shared file storage layer
if (!type.HasValue) if (!type.HasValue)
@ -344,7 +350,7 @@ namespace LibHac.FsSrv
fileStorage = fileStorage =
new ReferenceCountedDisposable<FileStorageBasedFileSystem>(new FileStorageBasedFileSystem()); new ReferenceCountedDisposable<FileStorageBasedFileSystem>(new FileStorageBasedFileSystem());
rc = fileStorage.Target.Initialize(ref baseFileSystem, new U8Span(saveImageName), mode); rc = fileStorage.Target.Initialize(ref baseFileSystem, in saveImageName, mode);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
saveDataStorage = fileStorage.AddReference<IStorage>(); saveDataStorage = fileStorage.AddReference<IStorage>();
@ -375,7 +381,7 @@ namespace LibHac.FsSrv
new ReferenceCountedDisposable<SaveDataOpenTypeSetFileStorage>( new ReferenceCountedDisposable<SaveDataOpenTypeSetFileStorage>(
new SaveDataOpenTypeSetFileStorage(_fsServer, spaceId, saveDataId)); new SaveDataOpenTypeSetFileStorage(_fsServer, spaceId, saveDataId));
rc = baseFileStorage.Target.Initialize(ref baseFileSystem, new U8Span(saveImageName), mode, rc = baseFileStorage.Target.Initialize(ref baseFileSystem, in saveImageName, mode,
type.ValueRo); type.ValueRo);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;

View File

@ -17,8 +17,8 @@ namespace LibHac.FsSrv.Sf
Result RenameFile(in Path currentPath, in Path newPath); Result RenameFile(in Path currentPath, in Path newPath);
Result RenameDirectory(in Path currentPath, in Path newPath); Result RenameDirectory(in Path currentPath, in Path newPath);
Result GetEntryType(out uint entryType, in Path path); Result GetEntryType(out uint entryType, in Path path);
Result OpenFile(out ReferenceCountedDisposable<IFileSf> file, in Path path, uint mode); Result OpenFile(out ReferenceCountedDisposable<IFileSf> outFile, in Path path, uint mode);
Result OpenDirectory(out ReferenceCountedDisposable<IDirectorySf> directory, in Path path, uint mode); Result OpenDirectory(out ReferenceCountedDisposable<IDirectorySf> outDirectory, in Path path, uint mode);
Result Commit(); Result Commit();
Result GetFreeSpaceSize(out long freeSpace, in Path path); Result GetFreeSpaceSize(out long freeSpace, in Path path);
Result GetTotalSpaceSize(out long totalSpace, in Path path); Result GetTotalSpaceSize(out long totalSpace, in Path path);

View File

@ -8,23 +8,29 @@ namespace LibHac.FsSystem
{ {
public class AesXtsDirectory : IDirectory public class AesXtsDirectory : IDirectory
{ {
private U8String Path { get; } private U8String _path;
private OpenDirectoryMode Mode { get; } private OpenDirectoryMode _mode;
private IFileSystem BaseFileSystem { get; } private IFileSystem _baseFileSystem;
private IDirectory BaseDirectory { get; } private UniqueRef<IDirectory> _baseDirectory;
public AesXtsDirectory(IFileSystem baseFs, IDirectory baseDir, U8String path, OpenDirectoryMode mode) public AesXtsDirectory(IFileSystem baseFs, ref UniqueRef<IDirectory> baseDir, U8String path, OpenDirectoryMode mode)
{ {
BaseFileSystem = baseFs; _baseFileSystem = baseFs;
BaseDirectory = baseDir; _baseDirectory = new UniqueRef<IDirectory>(ref baseDir);
Mode = mode; _mode = mode;
Path = path; _path = path;
}
public override void Dispose()
{
_baseDirectory.Dispose();
base.Dispose();
} }
protected override Result DoRead(out long entriesRead, Span<DirectoryEntry> entryBuffer) protected override Result DoRead(out long entriesRead, Span<DirectoryEntry> entryBuffer)
{ {
Result rc = BaseDirectory.Read(out entriesRead, entryBuffer); Result rc = _baseDirectory.Get.Read(out entriesRead, entryBuffer);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
for (int i = 0; i < entriesRead; i++) for (int i = 0; i < entriesRead; i++)
@ -33,14 +39,14 @@ namespace LibHac.FsSystem
if (entry.Type == DirectoryEntryType.File) if (entry.Type == DirectoryEntryType.File)
{ {
if (Mode.HasFlag(OpenDirectoryMode.NoFileSize)) if (_mode.HasFlag(OpenDirectoryMode.NoFileSize))
{ {
entry.Size = 0; entry.Size = 0;
} }
else else
{ {
string entryName = StringUtils.NullTerminatedUtf8ToString(entry.Name); string entryName = StringUtils.NullTerminatedUtf8ToString(entry.Name);
entry.Size = GetAesXtsFileSize(PathTools.Combine(Path.ToString(), entryName).ToU8Span()); entry.Size = GetAesXtsFileSize(PathTools.Combine(_path.ToString(), entryName).ToU8Span());
} }
} }
} }
@ -50,7 +56,7 @@ namespace LibHac.FsSystem
protected override Result DoGetEntryCount(out long entryCount) protected override Result DoGetEntryCount(out long entryCount)
{ {
return BaseDirectory.GetEntryCount(out entryCount); return _baseDirectory.Get.GetEntryCount(out entryCount);
} }
/// <summary> /// <summary>
@ -66,24 +72,21 @@ namespace LibHac.FsSystem
// Todo: Remove try/catch when more code uses Result // Todo: Remove try/catch when more code uses Result
try try
{ {
Result rc = BaseFileSystem.OpenFile(out IFile file, path, OpenMode.Read); using var file = new UniqueRef<IFile>();
Result rc = _baseFileSystem.OpenFile(ref file.Ref(), path, OpenMode.Read);
if (rc.IsFailure()) return 0; if (rc.IsFailure()) return 0;
using (file) uint magic = 0;
{ long fileSize = 0;
uint magic = 0; long bytesRead;
long fileSize = 0;
long bytesRead;
file.Read(out bytesRead, magicOffset, SpanHelpers.AsByteSpan(ref magic), ReadOption.None); file.Get.Read(out bytesRead, magicOffset, SpanHelpers.AsByteSpan(ref magic), ReadOption.None);
if (bytesRead != sizeof(uint) || magic != AesXtsFileHeader.AesXtsFileMagic) return 0; if (bytesRead != sizeof(uint) || magic != AesXtsFileHeader.AesXtsFileMagic) return 0;
file.Read(out bytesRead, fileSizeOffset, SpanHelpers.AsByteSpan(ref fileSize), ReadOption.None); file.Get.Read(out bytesRead, fileSizeOffset, SpanHelpers.AsByteSpan(ref fileSize), ReadOption.None);
if (bytesRead != sizeof(long) || magic != AesXtsFileHeader.AesXtsFileMagic) return 0; if (bytesRead != sizeof(long) || magic != AesXtsFileHeader.AesXtsFileMagic) return 0;
return fileSize;
}
return fileSize;
} }
catch (Exception) catch (Exception)
{ {

View File

@ -8,7 +8,7 @@ namespace LibHac.FsSystem
{ {
public class AesXtsFile : IFile public class AesXtsFile : IFile
{ {
private IFile BaseFile { get; } private UniqueRef<IFile> BaseFile { get; }
private U8String Path { get; } private U8String Path { get; }
private byte[] KekSeed { get; } private byte[] KekSeed { get; }
private byte[] VerificationKey { get; } private byte[] VerificationKey { get; }
@ -20,18 +20,18 @@ namespace LibHac.FsSystem
internal const int HeaderLength = 0x4000; internal const int HeaderLength = 0x4000;
public AesXtsFile(OpenMode mode, IFile baseFile, U8String path, ReadOnlySpan<byte> kekSeed, ReadOnlySpan<byte> verificationKey, int blockSize) public AesXtsFile(OpenMode mode, ref UniqueRef<IFile> baseFile, U8String path, ReadOnlySpan<byte> kekSeed, ReadOnlySpan<byte> verificationKey, int blockSize)
{ {
Mode = mode; Mode = mode;
BaseFile = baseFile; BaseFile = new UniqueRef<IFile>(ref baseFile);
Path = path; Path = path;
KekSeed = kekSeed.ToArray(); KekSeed = kekSeed.ToArray();
VerificationKey = verificationKey.ToArray(); VerificationKey = verificationKey.ToArray();
BlockSize = blockSize; BlockSize = blockSize;
Header = new AesXtsFileHeader(BaseFile); Header = new AesXtsFileHeader(BaseFile.Get);
baseFile.GetSize(out long fileSize).ThrowIfFailure(); BaseFile.Get.GetSize(out long fileSize).ThrowIfFailure();
if (!Header.TryDecryptHeader(Path.ToString(), KekSeed, VerificationKey)) if (!Header.TryDecryptHeader(Path.ToString(), KekSeed, VerificationKey))
{ {
@ -43,7 +43,7 @@ namespace LibHac.FsSystem
ThrowHelper.ThrowResult(ResultFs.AesXtsFileTooShort.Value, "NAX0 key derivation failed."); ThrowHelper.ThrowResult(ResultFs.AesXtsFileTooShort.Value, "NAX0 key derivation failed.");
} }
var fileStorage = new FileStorage2(baseFile); var fileStorage = new FileStorage2(BaseFile.Get);
var encStorage = new SubStorage(fileStorage, HeaderLength, fileSize - HeaderLength); var encStorage = new SubStorage(fileStorage, HeaderLength, fileSize - HeaderLength);
encStorage.SetResizable(true); encStorage.SetResizable(true);
@ -116,7 +116,7 @@ namespace LibHac.FsSystem
{ {
Header.SetSize(size, VerificationKey); Header.SetSize(size, VerificationKey);
Result rc = BaseFile.Write(0, Header.ToBytes(false)); Result rc = BaseFile.Get.Write(0, Header.ToBytes(false));
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
return BaseStorage.SetSize(Alignment.AlignUp(size, 0x10)); return BaseStorage.SetSize(Alignment.AlignUp(size, 0x10));
@ -126,7 +126,7 @@ namespace LibHac.FsSystem
{ {
BaseStorage.Flush(); BaseStorage.Flush();
BaseStorage.Dispose(); BaseStorage.Dispose();
BaseFile?.Dispose(); BaseFile.Dispose();
base.Dispose(); base.Dispose();
} }

View File

@ -66,14 +66,12 @@ namespace LibHac.FsSystem
var header = new AesXtsFileHeader(key, size, path.ToString(), KekSource, ValidationKey); var header = new AesXtsFileHeader(key, size, path.ToString(), KekSource, ValidationKey);
rc = BaseFileSystem.OpenFile(out IFile baseFile, in path, OpenMode.Write); using var baseFile = new UniqueRef<IFile>();
rc = BaseFileSystem.OpenFile(ref baseFile.Ref(), in path, OpenMode.Write);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
using (baseFile) rc = baseFile.Get.Write(0, header.ToBytes(false));
{ if (rc.IsFailure()) return rc;
rc = baseFile.Write(0, header.ToBytes(false));
if (rc.IsFailure()) return rc;
}
return Result.Success; return Result.Success;
} }
@ -98,28 +96,27 @@ namespace LibHac.FsSystem
return BaseFileSystem.DeleteFile(path); return BaseFileSystem.DeleteFile(path);
} }
protected override Result DoOpenDirectory(out IDirectory directory, in Path path, OpenDirectoryMode mode) protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,
OpenDirectoryMode mode)
{ {
UnsafeHelpers.SkipParamInit(out directory); using var baseDir = new UniqueRef<IDirectory>();
Result rc = BaseFileSystem.OpenDirectory(ref baseDir.Ref(), path, mode);
Result rc = BaseFileSystem.OpenDirectory(out IDirectory baseDir, path, mode);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
directory = new AesXtsDirectory(BaseFileSystem, baseDir, new U8String(path.GetString().ToArray()), mode); outDirectory.Reset(new AesXtsDirectory(BaseFileSystem, ref baseDir.Ref(), new U8String(path.GetString().ToArray()), mode));
return Result.Success; return Result.Success;
} }
protected override Result DoOpenFile(out IFile file, in Path path, OpenMode mode) protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode)
{ {
UnsafeHelpers.SkipParamInit(out file); using var baseFile = new UniqueRef<IFile>();
Result rc = BaseFileSystem.OpenFile(ref baseFile.Ref(), path, mode | OpenMode.Read);
Result rc = BaseFileSystem.OpenFile(out IFile baseFile, path, mode | OpenMode.Read);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
var xtsFile = new AesXtsFile(mode, baseFile, new U8String(path.GetString().ToArray()), KekSource, var xtsFile = new AesXtsFile(mode, ref baseFile.Ref(), new U8String(path.GetString().ToArray()), KekSource,
ValidationKey, BlockSize); ValidationKey, BlockSize);
file = xtsFile; outFile.Reset(xtsFile);
return Result.Success; return Result.Success;
} }
@ -265,15 +262,13 @@ namespace LibHac.FsSystem
header = null; header = null;
Result rc = BaseFileSystem.OpenFile(out IFile file, filePath.ToU8Span(), OpenMode.Read); using var file = new UniqueRef<IFile>();
Result rc = BaseFileSystem.OpenFile(ref file.Ref(), filePath.ToU8Span(), OpenMode.Read);
if (rc.IsFailure()) return false; if (rc.IsFailure()) return false;
using (file) header = new AesXtsFileHeader(file.Get);
{
header = new AesXtsFileHeader(file);
return header.TryDecryptHeader(keyPath, KekSource, ValidationKey); return header.TryDecryptHeader(keyPath, KekSource, ValidationKey);
}
} }
private void WriteXtsHeader(AesXtsFileHeader header, string filePath, string keyPath) private void WriteXtsHeader(AesXtsFileHeader header, string filePath, string keyPath)
@ -283,12 +278,10 @@ namespace LibHac.FsSystem
header.EncryptHeader(keyPath, KekSource, ValidationKey); header.EncryptHeader(keyPath, KekSource, ValidationKey);
BaseFileSystem.OpenFile(out IFile file, filePath.ToU8Span(), OpenMode.ReadWrite); using var file = new UniqueRef<IFile>();
BaseFileSystem.OpenFile(ref file.Ref(), filePath.ToU8Span(), OpenMode.ReadWrite);
using (file) file.Get.Write(0, header.ToBytes(false), WriteOption.Flush).ThrowIfFailure();
{
file.Write(0, header.ToBytes(false), WriteOption.Flush).ThrowIfFailure();
}
} }
} }
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using LibHac.Common;
using LibHac.Fs; using LibHac.Fs;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
@ -51,12 +52,13 @@ namespace LibHac.FsSystem
throw new NotImplementedException(); throw new NotImplementedException();
} }
protected override Result DoOpenFile(out IFile file, in Path path, OpenMode mode) protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
protected override Result DoOpenDirectory(out IDirectory directory, in Path path, OpenDirectoryMode mode) protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,
OpenDirectoryMode mode)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

View File

@ -218,10 +218,11 @@ namespace LibHac.FsSystem
CreateFileOptions.None); CreateFileOptions.None);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = _baseFileSystem.OpenFile(out IFile newInternalFile, in internalFilePath, _mode); using var newInternalFile = new UniqueRef<IFile>();
rc = _baseFileSystem.OpenFile(ref newInternalFile.Ref(), in internalFilePath, _mode);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
_files.Add(newInternalFile); _files.Add(newInternalFile.Release());
rc = internalFilePath.RemoveChild(); rc = internalFilePath.RemoveChild();
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
@ -375,16 +376,16 @@ namespace LibHac.FsSystem
private class ConcatenationDirectory : IDirectory private class ConcatenationDirectory : IDirectory
{ {
private OpenDirectoryMode _mode; private OpenDirectoryMode _mode;
private IDirectory _baseDirectory; private UniqueRef<IDirectory> _baseDirectory;
private Path.Stored _path; private Path.Stored _path;
private IFileSystem _baseFileSystem; private IFileSystem _baseFileSystem;
private ConcatenationFileSystem _concatenationFileSystem; private ConcatenationFileSystem _concatenationFileSystem;
public ConcatenationDirectory(OpenDirectoryMode mode, IDirectory baseDirectory, public ConcatenationDirectory(OpenDirectoryMode mode, ref UniqueRef<IDirectory> baseDirectory,
ConcatenationFileSystem concatFileSystem, IFileSystem baseFileSystem) ConcatenationFileSystem concatFileSystem, IFileSystem baseFileSystem)
{ {
_mode = mode; _mode = mode;
_baseDirectory = baseDirectory; _baseDirectory = new UniqueRef<IDirectory>(ref baseDirectory);
_baseFileSystem = baseFileSystem; _baseFileSystem = baseFileSystem;
_concatenationFileSystem = concatFileSystem; _concatenationFileSystem = concatFileSystem;
} }
@ -414,7 +415,7 @@ namespace LibHac.FsSystem
while (readCountTotal < entryBuffer.Length) while (readCountTotal < entryBuffer.Length)
{ {
Result rc = _baseDirectory.Read(out long readCount, SpanHelpers.AsSpan(ref entry)); Result rc = _baseDirectory.Get.Read(out long readCount, SpanHelpers.AsSpan(ref entry));
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
if (readCount == 0) if (readCount == 0)
@ -454,37 +455,30 @@ namespace LibHac.FsSystem
UnsafeHelpers.SkipParamInit(out entryCount); UnsafeHelpers.SkipParamInit(out entryCount);
Unsafe.SkipInit(out DirectoryEntry entry); Unsafe.SkipInit(out DirectoryEntry entry);
IDirectory directory = null; using var directory = new UniqueRef<IDirectory>();
try Path path = _path.DangerousGetPath();
Result rc = _baseFileSystem.OpenDirectory(ref directory.Ref(), in path,
OpenDirectoryMode.All | OpenDirectoryMode.NoFileSize);
if (rc.IsFailure()) return rc;
long entryCountTotal = 0;
while (true)
{ {
Path path = _path.GetPath(); directory.Get.Read(out long readCount, SpanHelpers.AsSpan(ref entry));
Result rc = _baseFileSystem.OpenDirectory(out directory, in path,
OpenDirectoryMode.All | OpenDirectoryMode.NoFileSize);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
long entryCountTotal = 0; if (readCount == 0)
break;
while (true) if (IsReadTarget(in entry))
{ entryCountTotal++;
directory.Read(out long readCount, SpanHelpers.AsSpan(ref entry));
if (rc.IsFailure()) return rc;
if (readCount == 0)
break;
if (IsReadTarget(in entry))
entryCountTotal++;
}
entryCount = entryCountTotal;
return Result.Success;
}
finally
{
directory?.Dispose();
} }
entryCount = entryCountTotal;
return Result.Success;
} }
private bool IsReadTarget(in DirectoryEntry entry) private bool IsReadTarget(in DirectoryEntry entry)
@ -642,19 +636,16 @@ namespace LibHac.FsSystem
return _baseFileSystem.Flush(); return _baseFileSystem.Flush();
} }
protected override Result DoOpenFile(out IFile file, in Path path, OpenMode mode) protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode)
{ {
UnsafeHelpers.SkipParamInit(out file);
if (!IsConcatenationFile(in path)) if (!IsConcatenationFile(in path))
{ {
return _baseFileSystem.OpenFile(out file, in path, mode); return _baseFileSystem.OpenFile(ref outFile, in path, mode);
} }
Result rc = GetInternalFileCount(out int fileCount, in path); Result rc = GetInternalFileCount(out int fileCount, in path);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
ConcatenationFile concatFile = null;
var internalFiles = new List<IFile>(fileCount); var internalFiles = new List<IFile>(fileCount);
using var filePath = new Path(); using var filePath = new Path();
@ -668,27 +659,27 @@ namespace LibHac.FsSystem
rc = AppendInternalFilePath(ref filePath.Ref(), i); rc = AppendInternalFilePath(ref filePath.Ref(), i);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = _baseFileSystem.OpenFile(out IFile internalFile, in filePath, mode); using var internalFile = new UniqueRef<IFile>();
rc = _baseFileSystem.OpenFile(ref internalFile.Ref(), in filePath, mode);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
internalFiles.Add(internalFile); internalFiles.Add(internalFile.Release());
rc = filePath.RemoveChild(); rc = filePath.RemoveChild();
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
} }
concatFile = new ConcatenationFile(mode, ref internalFiles, _InternalFileSize, _baseFileSystem); using var concatFile = new UniqueRef<ConcatenationFile>(
new ConcatenationFile(mode, ref internalFiles, _InternalFileSize, _baseFileSystem));
rc = concatFile.Initialize(in path); rc = concatFile.Get.Initialize(in path);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
file = Shared.Move(ref concatFile); outFile.Set(ref concatFile.Ref());
return Result.Success; return Result.Success;
} }
finally finally
{ {
concatFile?.Dispose();
if (internalFiles is not null) if (internalFiles is not null)
{ {
foreach (IFile internalFile in internalFiles) foreach (IFile internalFile in internalFiles)
@ -699,23 +690,24 @@ namespace LibHac.FsSystem
} }
} }
protected override Result DoOpenDirectory(out IDirectory directory, in Path path, OpenDirectoryMode mode) protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,
OpenDirectoryMode mode)
{ {
UnsafeHelpers.SkipParamInit(out directory);
if (IsConcatenationFile(path)) if (IsConcatenationFile(path))
{ {
return ResultFs.PathNotFound.Log(); return ResultFs.PathNotFound.Log();
} }
Result rc = _baseFileSystem.OpenDirectory(out IDirectory baseDirectory, path, OpenDirectoryMode.All); using var baseDirectory = new UniqueRef<IDirectory>();
Result rc = _baseFileSystem.OpenDirectory(ref baseDirectory.Ref(), path, OpenDirectoryMode.All);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
var concatDirectory = new ConcatenationDirectory(mode, baseDirectory, this, _baseFileSystem); using var concatDirectory = new UniqueRef<ConcatenationDirectory>(
rc = concatDirectory.Initialize(in path); new ConcatenationDirectory(mode, ref baseDirectory.Ref(), this, _baseFileSystem));
rc = concatDirectory.Get.Initialize(in path);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
directory = concatDirectory; outDirectory.Set(ref concatDirectory.Ref());
return Result.Success; return Result.Success;
} }

View File

@ -38,10 +38,9 @@ namespace LibHac.FsSystem
private FileSystemClient _fsClient; private FileSystemClient _fsClient;
private IFileSystem _baseFs; private IFileSystem _baseFs;
private SdkMutexType _mutex; private SdkMutexType _mutex;
private UniqueRef<IFileSystem> _uniqueBaseFs;
// Todo: Unique file system for disposal
private int _openWritableFileCount; private int _openWritableFileCount;
private bool _isJournalingSupported; private bool _isJournalingSupported;
private bool _isMultiCommitSupported; private bool _isMultiCommitSupported;
@ -57,24 +56,24 @@ namespace LibHac.FsSystem
private ulong _saveDataId; private ulong _saveDataId;
// Additions to ensure only one directory save data fs is opened at a time // Additions to ensure only one directory save data fs is opened at a time
private IFile _lockFile; private UniqueRef<IFile> _lockFile;
private class DirectorySaveDataFile : IFile private class DirectorySaveDataFile : IFile
{ {
private IFile _baseFile; private UniqueRef<IFile> _baseFile;
private DirectorySaveDataFileSystem _parentFs; private DirectorySaveDataFileSystem _parentFs;
private OpenMode _mode; private OpenMode _mode;
public DirectorySaveDataFile(IFile baseFile, DirectorySaveDataFileSystem parentFs, OpenMode mode) public DirectorySaveDataFile(ref UniqueRef<IFile> baseFile, DirectorySaveDataFileSystem parentFs, OpenMode mode)
{ {
_baseFile = baseFile; _baseFile = new UniqueRef<IFile>(ref baseFile);
_parentFs = parentFs; _parentFs = parentFs;
_mode = mode; _mode = mode;
} }
public override void Dispose() public override void Dispose()
{ {
_baseFile?.Dispose(); _baseFile.Dispose();
if (_mode.HasFlag(OpenMode.Write)) if (_mode.HasFlag(OpenMode.Write))
{ {
@ -88,63 +87,36 @@ namespace LibHac.FsSystem
protected override Result DoRead(out long bytesRead, long offset, Span<byte> destination, protected override Result DoRead(out long bytesRead, long offset, Span<byte> destination,
in ReadOption option) in ReadOption option)
{ {
return _baseFile.Read(out bytesRead, offset, destination, in option); return _baseFile.Get.Read(out bytesRead, offset, destination, in option);
} }
protected override Result DoWrite(long offset, ReadOnlySpan<byte> source, in WriteOption option) protected override Result DoWrite(long offset, ReadOnlySpan<byte> source, in WriteOption option)
{ {
return _baseFile.Write(offset, source, in option); return _baseFile.Get.Write(offset, source, in option);
} }
protected override Result DoFlush() protected override Result DoFlush()
{ {
return _baseFile.Flush(); return _baseFile.Get.Flush();
} }
protected override Result DoGetSize(out long size) protected override Result DoGetSize(out long size)
{ {
return _baseFile.GetSize(out size); return _baseFile.Get.GetSize(out size);
} }
protected override Result DoSetSize(long size) protected override Result DoSetSize(long size)
{ {
return _baseFile.SetSize(size); return _baseFile.Get.SetSize(size);
} }
protected override Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset, protected override Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset,
long size, ReadOnlySpan<byte> inBuffer) long size, ReadOnlySpan<byte> inBuffer)
{ {
return _baseFile.OperateRange(outBuffer, operationId, offset, size, inBuffer); return _baseFile.Get.OperateRange(outBuffer, operationId, offset, size, inBuffer);
} }
} }
public static Result CreateNew(out DirectorySaveDataFileSystem created, IFileSystem baseFileSystem,
ISaveDataCommitTimeStampGetter timeStampGetter, RandomDataGenerator randomGenerator,
bool isJournalingSupported, bool isMultiCommitSupported, bool isJournalingEnabled,
FileSystemClient fsClient)
{
var obj = new DirectorySaveDataFileSystem(baseFileSystem, fsClient);
Result rc = obj.Initialize(timeStampGetter, randomGenerator, isJournalingSupported, isMultiCommitSupported,
isJournalingEnabled);
if (rc.IsSuccess())
{
created = obj;
return Result.Success;
}
obj.Dispose();
UnsafeHelpers.SkipParamInit(out created);
return rc;
}
public static Result CreateNew(out DirectorySaveDataFileSystem created, IFileSystem baseFileSystem,
bool isJournalingSupported, bool isMultiCommitSupported, bool isJournalingEnabled)
{
return CreateNew(out created, baseFileSystem, null, null, isJournalingSupported, isMultiCommitSupported,
isJournalingEnabled, null);
}
public static ReferenceCountedDisposable<DirectorySaveDataFileSystem> CreateShared(IFileSystem baseFileSystem, public static ReferenceCountedDisposable<DirectorySaveDataFileSystem> CreateShared(IFileSystem baseFileSystem,
FileSystemClient fsClient) FileSystemClient fsClient)
{ {
@ -162,6 +134,17 @@ namespace LibHac.FsSystem
_mutex.Initialize(); _mutex.Initialize();
} }
/// <summary>
/// Create an uninitialized <see cref="DirectorySaveDataFileSystem"/>.
/// </summary>
/// <param name="baseFileSystem">The base <see cref="IFileSystem"/> to use.</param>
public DirectorySaveDataFileSystem(ref UniqueRef<IFileSystem> baseFileSystem)
{
_baseFs = baseFileSystem.Get;
_mutex.Initialize();
_uniqueBaseFs = new UniqueRef<IFileSystem>(ref baseFileSystem);
}
/// <summary> /// <summary>
/// Create an uninitialized <see cref="DirectorySaveDataFileSystem"/>. /// Create an uninitialized <see cref="DirectorySaveDataFileSystem"/>.
/// If a <see cref="FileSystemClient"/> is provided a global mutex will be used when synchronizing directories. /// If a <see cref="FileSystemClient"/> is provided a global mutex will be used when synchronizing directories.
@ -177,13 +160,28 @@ namespace LibHac.FsSystem
_fsClient = fsClient; _fsClient = fsClient;
} }
/// <summary>
/// Create an uninitialized <see cref="DirectorySaveDataFileSystem"/>.
/// If a <see cref="FileSystemClient"/> is provided a global mutex will be used when synchronizing directories.
/// Running outside of a Horizon context doesn't require this mutex,
/// and null can be passed to <paramref name="fsClient"/>.
/// </summary>
/// <param name="baseFileSystem">The base <see cref="IFileSystem"/> to use.</param>
/// <param name="fsClient">The <see cref="FileSystemClient"/> to use. May be null.</param>
public DirectorySaveDataFileSystem(ref UniqueRef<IFileSystem> baseFileSystem, FileSystemClient fsClient)
{
_baseFs = baseFileSystem.Get;
_mutex.Initialize();
_uniqueBaseFs = new UniqueRef<IFileSystem>(ref baseFileSystem);
_fsClient = fsClient;
}
public override void Dispose() public override void Dispose()
{ {
_lockFile?.Dispose(); _lockFile.Dispose();
_lockFile = null;
_cacheObserver?.Unregister(_spaceId, _saveDataId); _cacheObserver?.Unregister(_spaceId, _saveDataId);
_baseFs?.Dispose(); _uniqueBaseFs.Dispose();
base.Dispose(); base.Dispose();
} }
@ -328,14 +326,14 @@ namespace LibHac.FsSystem
private Result GetFileSystemLock() private Result GetFileSystemLock()
{ {
// Having an open lock file means we already have the lock for the file system. // Having an open lock file means we already have the lock for the file system.
if (_lockFile is not null) if (_lockFile.HasValue)
return Result.Success; return Result.Success;
using var pathLockFile = new Path(); using var pathLockFile = new Path();
Result rc = PathFunctions.SetUpFixedPath(ref pathLockFile.Ref(), LockFileName); Result rc = PathFunctions.SetUpFixedPath(ref pathLockFile.Ref(), LockFileName);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = _baseFs.OpenFile(out _lockFile, in pathLockFile, OpenMode.ReadWrite); rc = _baseFs.OpenFile(ref _lockFile, in pathLockFile, OpenMode.ReadWrite);
if (rc.IsFailure()) if (rc.IsFailure())
{ {
@ -344,7 +342,7 @@ namespace LibHac.FsSystem
rc = _baseFs.CreateFile(in pathLockFile, 0); rc = _baseFs.CreateFile(in pathLockFile, 0);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = _baseFs.OpenFile(out _lockFile, in pathLockFile, OpenMode.ReadWrite); rc = _baseFs.OpenFile(ref _lockFile, in pathLockFile, OpenMode.ReadWrite);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
} }
else else
@ -512,40 +510,39 @@ namespace LibHac.FsSystem
return Result.Success; return Result.Success;
} }
protected override Result DoOpenFile(out IFile file, in Path path, OpenMode mode) protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode)
{ {
UnsafeHelpers.SkipParamInit(out file);
using var fullPath = new Path(); using var fullPath = new Path();
Result rc = ResolvePath(ref fullPath.Ref(), in path); Result rc = ResolvePath(ref fullPath.Ref(), in path);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex); using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
rc = _baseFs.OpenFile(out IFile baseFile, in fullPath, mode); using var baseFile = new UniqueRef<IFile>();
rc = _baseFs.OpenFile(ref baseFile.Ref(), in fullPath, mode);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
file = new DirectorySaveDataFile(baseFile, this, mode); using var file = new UniqueRef<IFile>(new DirectorySaveDataFile(ref baseFile.Ref(), this, mode));
if (mode.HasFlag(OpenMode.Write)) if (mode.HasFlag(OpenMode.Write))
{ {
_openWritableFileCount++; _openWritableFileCount++;
} }
outFile.Set(ref file.Ref());
return Result.Success; return Result.Success;
} }
protected override Result DoOpenDirectory(out IDirectory directory, in Path path, OpenDirectoryMode mode) protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,
OpenDirectoryMode mode)
{ {
UnsafeHelpers.SkipParamInit(out directory);
using var fullPath = new Path(); using var fullPath = new Path();
Result rc = ResolvePath(ref fullPath.Ref(), in path); Result rc = ResolvePath(ref fullPath.Ref(), in path);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex); using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
rc = _baseFs.OpenDirectory(out directory, in fullPath, mode); rc = _baseFs.OpenDirectory(ref outDirectory, in fullPath, mode);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
return Result.Success; return Result.Success;
@ -704,8 +701,10 @@ namespace LibHac.FsSystem
return Result.Success; return Result.Success;
} }
internal void DecrementWriteOpenFileCount() private void DecrementWriteOpenFileCount()
{ {
// Todo?: Calling OpenFile when outFile already contains a DirectorySaveDataFile
// will try to lock this mutex a second time
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex); using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
_openWritableFileCount--; _openWritableFileCount--;
@ -811,47 +810,40 @@ namespace LibHac.FsSystem
private Result EnsureExtraDataSize(in Path path) private Result EnsureExtraDataSize(in Path path)
{ {
IFile file = null; using var file = new UniqueRef<IFile>();
try Result rc = _baseFs.OpenFile(ref file.Ref(), in path, OpenMode.ReadWrite);
{ if (rc.IsFailure()) return rc;
Result rc = _baseFs.OpenFile(out file, in path, OpenMode.ReadWrite);
if (rc.IsFailure()) return rc;
rc = file.GetSize(out long fileSize); rc = file.Get.GetSize(out long fileSize);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
if (fileSize == Unsafe.SizeOf<SaveDataExtraData>()) if (fileSize == Unsafe.SizeOf<SaveDataExtraData>())
return Result.Success; return Result.Success;
return file.SetSize(Unsafe.SizeOf<SaveDataExtraData>()); return file.Get.SetSize(Unsafe.SizeOf<SaveDataExtraData>());
}
finally
{
file?.Dispose();
}
} }
private Result SynchronizeExtraData(in Path destPath, in Path sourcePath) private Result SynchronizeExtraData(in Path destPath, in Path sourcePath)
{ {
Span<byte> workBuffer = stackalloc byte[Unsafe.SizeOf<SaveDataExtraData>()]; Span<byte> workBuffer = stackalloc byte[Unsafe.SizeOf<SaveDataExtraData>()];
Result rc = _baseFs.OpenFile(out IFile sourceFile, in sourcePath, OpenMode.Read); using (var sourceFile = new UniqueRef<IFile>())
if (rc.IsFailure()) return rc;
using (sourceFile)
{ {
rc = sourceFile.Read(out long bytesRead, 0, workBuffer); Result rc = _baseFs.OpenFile(ref sourceFile.Ref(), in sourcePath, OpenMode.Read);
if (rc.IsFailure()) return rc;
rc = sourceFile.Get.Read(out long bytesRead, 0, workBuffer);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
Assert.SdkEqual(bytesRead, Unsafe.SizeOf<SaveDataExtraData>()); Assert.SdkEqual(bytesRead, Unsafe.SizeOf<SaveDataExtraData>());
} }
rc = _baseFs.OpenFile(out IFile destFile, in destPath, OpenMode.Write); using (var destFile = new UniqueRef<IFile>())
if (rc.IsFailure()) return rc;
using (destFile)
{ {
rc = destFile.Write(0, workBuffer, WriteOption.Flush); Result rc = _baseFs.OpenFile(ref destFile.Ref(), in destPath, OpenMode.Write);
if (rc.IsFailure()) return rc;
rc = destFile.Get.Write(0, workBuffer, WriteOption.Flush);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
} }
@ -926,14 +918,12 @@ namespace LibHac.FsSystem
Result rc = GetExtraDataPath(ref pathExtraData.Ref()); Result rc = GetExtraDataPath(ref pathExtraData.Ref());
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = _baseFs.OpenFile(out IFile file, in pathExtraData, OpenMode.Write); using var file = new UniqueRef<IFile>();
rc = _baseFs.OpenFile(ref file.Ref(), in pathExtraData, OpenMode.Write);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
using (file) rc = file.Get.Write(0, SpanHelpers.AsReadOnlyByteSpan(in extraData), WriteOption.Flush);
{ if (rc.IsFailure()) return rc;
rc = file.Write(0, SpanHelpers.AsReadOnlyByteSpan(in extraData), WriteOption.Flush);
if (rc.IsFailure()) return rc;
}
return Result.Success; return Result.Success;
} }
@ -1002,16 +992,14 @@ namespace LibHac.FsSystem
Result rc = GetExtraDataPath(ref pathExtraData.Ref()); Result rc = GetExtraDataPath(ref pathExtraData.Ref());
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = _baseFs.OpenFile(out IFile file, in pathExtraData, OpenMode.Read); using var file = new UniqueRef<IFile>();
rc = _baseFs.OpenFile(ref file.Ref(), in pathExtraData, OpenMode.Read);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
using (file) rc = file.Get.Read(out long bytesRead, 0, SpanHelpers.AsByteSpan(ref extraData));
{ if (rc.IsFailure()) return rc;
rc = file.Read(out long bytesRead, 0, SpanHelpers.AsByteSpan(ref extraData));
if (rc.IsFailure()) return rc;
Assert.SdkEqual(bytesRead, Unsafe.SizeOf<SaveDataExtraData>()); Assert.SdkEqual(bytesRead, Unsafe.SizeOf<SaveDataExtraData>());
}
return Result.Success; return Result.Success;
} }

View File

@ -95,42 +95,38 @@ namespace LibHac.FsSystem
logger?.LogMessage(sourcePath.ToString()); logger?.LogMessage(sourcePath.ToString());
// Open source file. // Open source file.
Result rc = sourceFileSystem.OpenFile(out IFile sourceFile, sourcePath, OpenMode.Read); using var sourceFile = new UniqueRef<IFile>();
Result rc = sourceFileSystem.OpenFile(ref sourceFile.Ref(), sourcePath, OpenMode.Read);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
using (sourceFile) rc = sourceFile.Get.GetSize(out long fileSize);
if (rc.IsFailure()) return rc;
rc = CreateOrOverwriteFile(destFileSystem, in destPath, fileSize, option);
if (rc.IsFailure()) return rc;
using var destFile = new UniqueRef<IFile>();
rc = destFileSystem.OpenFile(ref destFile.Ref(), in destPath, OpenMode.Write);
if (rc.IsFailure()) return rc;
// Read/Write file in work buffer sized chunks.
long remaining = fileSize;
long offset = 0;
logger?.SetTotal(fileSize);
while (remaining > 0)
{ {
rc = sourceFile.GetSize(out long fileSize); rc = sourceFile.Get.Read(out long bytesRead, offset, workBuffer, ReadOption.None);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = CreateOrOverwriteFile(destFileSystem, in destPath, fileSize, option); rc = destFile.Get.Write(offset, workBuffer.Slice(0, (int)bytesRead), WriteOption.None);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = destFileSystem.OpenFile(out IFile destFile, in destPath, OpenMode.Write); remaining -= bytesRead;
if (rc.IsFailure()) return rc; offset += bytesRead;
using (destFile) logger?.ReportAdd(bytesRead);
{
// Read/Write file in work buffer sized chunks.
long remaining = fileSize;
long offset = 0;
logger?.SetTotal(fileSize);
while (remaining > 0)
{
rc = sourceFile.Read(out long bytesRead, offset, workBuffer, ReadOption.None);
if (rc.IsFailure()) return rc;
rc = destFile.Write(offset, workBuffer.Slice(0, (int)bytesRead), WriteOption.None);
if (rc.IsFailure()) return rc;
remaining -= bytesRead;
offset += bytesRead;
logger?.ReportAdd(bytesRead);
}
}
} }
return Result.Success; return Result.Success;
@ -166,13 +162,14 @@ namespace LibHac.FsSystem
var pathNormalized = new Path(); var pathNormalized = new Path();
InitializeFromString(ref pathNormalized, path).ThrowIfFailure(); InitializeFromString(ref pathNormalized, path).ThrowIfFailure();
fileSystem.OpenDirectory(out IDirectory directory, in pathNormalized, OpenDirectoryMode.All).ThrowIfFailure(); using var directory = new UniqueRef<IDirectory>();
fileSystem.OpenDirectory(ref directory.Ref(), in pathNormalized, OpenDirectoryMode.All).ThrowIfFailure();
while (true) while (true)
{ {
Unsafe.SkipInit(out DirectoryEntry dirEntry); Unsafe.SkipInit(out DirectoryEntry dirEntry);
directory.Read(out long entriesRead, SpanHelpers.AsSpan(ref dirEntry)).ThrowIfFailure(); directory.Get.Read(out long entriesRead, SpanHelpers.AsSpan(ref dirEntry)).ThrowIfFailure();
if (entriesRead == 0) break; if (entriesRead == 0) break;
DirectoryEntryEx entry = GetDirectoryEntryEx(ref dirEntry, path); DirectoryEntryEx entry = GetDirectoryEntryEx(ref dirEntry, path);

View File

@ -26,40 +26,41 @@ namespace LibHac.FsSystem
} }
protected override Result DoCreateFile(in Path path, long size, CreateFileOptions option) => protected override Result DoCreateFile(in Path path, long size, CreateFileOptions option) =>
BaseFileSystem.Target.CreateFile(path, size, option); BaseFileSystem.Target.CreateFile(in path, size, option);
protected override Result DoDeleteFile(in Path path) => BaseFileSystem.Target.DeleteFile(path); protected override Result DoDeleteFile(in Path path) => BaseFileSystem.Target.DeleteFile(in path);
protected override Result DoCreateDirectory(in Path path) => BaseFileSystem.Target.CreateDirectory(path); protected override Result DoCreateDirectory(in Path path) => BaseFileSystem.Target.CreateDirectory(in path);
protected override Result DoDeleteDirectory(in Path path) => BaseFileSystem.Target.DeleteDirectory(path); protected override Result DoDeleteDirectory(in Path path) => BaseFileSystem.Target.DeleteDirectory(in path);
protected override Result DoDeleteDirectoryRecursively(in Path path) => protected override Result DoDeleteDirectoryRecursively(in Path path) =>
BaseFileSystem.Target.DeleteDirectoryRecursively(path); BaseFileSystem.Target.DeleteDirectoryRecursively(in path);
protected override Result DoCleanDirectoryRecursively(in Path path) => protected override Result DoCleanDirectoryRecursively(in Path path) =>
BaseFileSystem.Target.CleanDirectoryRecursively(path); BaseFileSystem.Target.CleanDirectoryRecursively(in path);
protected override Result DoRenameFile(in Path currentPath, in Path newPath) => protected override Result DoRenameFile(in Path currentPath, in Path newPath) =>
BaseFileSystem.Target.RenameFile(currentPath, newPath); BaseFileSystem.Target.RenameFile(in currentPath, in newPath);
protected override Result DoRenameDirectory(in Path currentPath, in Path newPath) => protected override Result DoRenameDirectory(in Path currentPath, in Path newPath) =>
BaseFileSystem.Target.RenameDirectory(currentPath, newPath); BaseFileSystem.Target.RenameDirectory(in currentPath, in newPath);
protected override Result DoGetEntryType(out DirectoryEntryType entryType, in Path path) => protected override Result DoGetEntryType(out DirectoryEntryType entryType, in Path path) =>
BaseFileSystem.Target.GetEntryType(out entryType, path); BaseFileSystem.Target.GetEntryType(out entryType, in path);
protected override Result DoGetFreeSpaceSize(out long freeSpace, in Path path) => protected override Result DoGetFreeSpaceSize(out long freeSpace, in Path path) =>
BaseFileSystem.Target.GetFreeSpaceSize(out freeSpace, path); BaseFileSystem.Target.GetFreeSpaceSize(out freeSpace, in path);
protected override Result DoGetTotalSpaceSize(out long totalSpace, in Path path) => protected override Result DoGetTotalSpaceSize(out long totalSpace, in Path path) =>
BaseFileSystem.Target.GetTotalSpaceSize(out totalSpace, path); BaseFileSystem.Target.GetTotalSpaceSize(out totalSpace, in path);
protected override Result DoOpenFile(out IFile file, in Path path, OpenMode mode) => protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode) =>
BaseFileSystem.Target.OpenFile(out file, path, mode); BaseFileSystem.Target.OpenFile(ref outFile, in path, mode);
protected override Result DoOpenDirectory(out IDirectory directory, in Path path, OpenDirectoryMode mode) => protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,
BaseFileSystem.Target.OpenDirectory(out directory, path, mode); OpenDirectoryMode mode) =>
BaseFileSystem.Target.OpenDirectory(ref outDirectory, in path, mode);
protected override Result DoCommit() => BaseFileSystem.Target.Commit(); protected override Result DoCommit() => BaseFileSystem.Target.Commit();
@ -71,9 +72,9 @@ namespace LibHac.FsSystem
protected override Result DoFlush() => BaseFileSystem.Target.Flush(); protected override Result DoFlush() => BaseFileSystem.Target.Flush();
protected override Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, in Path path) => protected override Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, in Path path) =>
BaseFileSystem.Target.GetFileTimeStampRaw(out timeStamp, path); BaseFileSystem.Target.GetFileTimeStampRaw(out timeStamp, in path);
protected override Result DoQueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId, protected override Result DoQueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId,
in Path path) => BaseFileSystem.Target.QueryEntry(outBuffer, inBuffer, queryId, path); in Path path) => BaseFileSystem.Target.QueryEntry(outBuffer, inBuffer, queryId, in path);
} }
} }

View File

@ -13,7 +13,7 @@ namespace LibHac.FsSystem
public UniqueLockWithPin(ref UniqueLock<SemaphoreAdapter> semaphore, ref ReferenceCountedDisposable<T> pinnedObject) public UniqueLockWithPin(ref UniqueLock<SemaphoreAdapter> semaphore, ref ReferenceCountedDisposable<T> pinnedObject)
{ {
Shared.Move(out _semaphore, ref semaphore); _semaphore = new UniqueLock<SemaphoreAdapter>(ref semaphore);
Shared.Move(out _pinnedObject, ref pinnedObject); Shared.Move(out _pinnedObject, ref pinnedObject);
} }

View File

@ -37,10 +37,9 @@ namespace LibHac.FsSystem
Sources.AddRange(sourceFileSystems); Sources.AddRange(sourceFileSystems);
} }
protected override Result DoOpenDirectory(out IDirectory directory, in Path path, OpenDirectoryMode mode) protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,
OpenDirectoryMode mode)
{ {
UnsafeHelpers.SkipParamInit(out directory);
// Open directories from all layers so they can be merged // Open directories from all layers so they can be merged
// Only allocate the list for multiple sources if needed // Only allocate the list for multiple sources if needed
List<IFileSystem> multipleSources = null; List<IFileSystem> multipleSources = null;
@ -82,12 +81,12 @@ namespace LibHac.FsSystem
if (!(multipleSources is null)) if (!(multipleSources is null))
{ {
var dir = new MergedDirectory(multipleSources, mode); using var dir = new UniqueRef<MergedDirectory>(new MergedDirectory(multipleSources, mode));
Result rc = dir.Initialize(in path); Result rc = dir.Get.Initialize(in path);
if (rc.IsSuccess()) if (rc.IsSuccess())
{ {
directory = dir; outDirectory.Set(ref dir.Ref());
} }
return rc; return rc;
@ -95,11 +94,12 @@ namespace LibHac.FsSystem
if (!(singleSource is null)) if (!(singleSource is null))
{ {
Result rc = singleSource.OpenDirectory(out IDirectory dir, path, mode); using var dir = new UniqueRef<IDirectory>();
Result rc = singleSource.OpenDirectory(ref dir.Ref(), in path, mode);
if (rc.IsSuccess()) if (rc.IsSuccess())
{ {
directory = dir; outDirectory.Set(ref dir.Ref());
} }
return rc; return rc;
@ -108,10 +108,8 @@ namespace LibHac.FsSystem
return ResultFs.PathNotFound.Log(); return ResultFs.PathNotFound.Log();
} }
protected override Result DoOpenFile(out IFile file, in Path path, OpenMode mode) protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode)
{ {
UnsafeHelpers.SkipParamInit(out file);
foreach (IFileSystem fs in Sources) foreach (IFileSystem fs in Sources)
{ {
Result rc = fs.GetEntryType(out DirectoryEntryType type, path); Result rc = fs.GetEntryType(out DirectoryEntryType type, path);
@ -120,7 +118,7 @@ namespace LibHac.FsSystem
{ {
if (type == DirectoryEntryType.File) if (type == DirectoryEntryType.File)
{ {
return fs.OpenFile(out file, path, mode); return fs.OpenFile(ref outFile, path, mode);
} }
if (type == DirectoryEntryType.Directory) if (type == DirectoryEntryType.Directory)
@ -225,12 +223,14 @@ namespace LibHac.FsSystem
Result rc = _path.Initialize(in path); Result rc = _path.Initialize(in path);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
using var dir = new UniqueRef<IDirectory>();
foreach (IFileSystem fs in SourceFileSystems) foreach (IFileSystem fs in SourceFileSystems)
{ {
rc = fs.OpenDirectory(out IDirectory dir, in path, Mode); rc = fs.OpenDirectory(ref dir.Ref(), in path, Mode);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
SourceDirs.Add(dir); SourceDirs.Add(dir.Release());
} }
return Result.Success; return Result.Success;
@ -270,18 +270,19 @@ namespace LibHac.FsSystem
// todo: Efficient way to remove duplicates // todo: Efficient way to remove duplicates
var names = new HashSet<string>(); var names = new HashSet<string>();
Path path = _path.GetPath(); Path path = _path.DangerousGetPath();
using var dir = new UniqueRef<IDirectory>();
// Open new directories for each source because we need to remove duplicate entries // Open new directories for each source because we need to remove duplicate entries
foreach (IFileSystem fs in SourceFileSystems) foreach (IFileSystem fs in SourceFileSystems)
{ {
Result rc = fs.OpenDirectory(out IDirectory dir, in path, Mode); Result rc = fs.OpenDirectory(ref dir.Ref(), in path, Mode);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
long entriesRead; long entriesRead;
do do
{ {
rc = dir.Read(out entriesRead, SpanHelpers.AsSpan(ref entry)); rc = dir.Get.Read(out entriesRead, SpanHelpers.AsSpan(ref entry));
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
if (entriesRead == 1 && names.Add(StringUtils.Utf8ZToString(entry.Name))) if (entriesRead == 1 && names.Add(StringUtils.Utf8ZToString(entry.Name)))

View File

@ -175,7 +175,7 @@ namespace LibHac.FsSystem
rc = pathNormalized.Normalize(pathFlags); rc = pathNormalized.Normalize(pathFlags);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
Path rootPath = _rootPath.GetPath(); Path rootPath = _rootPath.DangerousGetPath();
using var fullPath = new Path(); using var fullPath = new Path();
rc = fullPath.Combine(in rootPath, in pathNormalized); rc = fullPath.Combine(in rootPath, in pathNormalized);
@ -379,9 +379,9 @@ namespace LibHac.FsSystem
() => DeleteFileInternal(file), _fsClient); () => DeleteFileInternal(file), _fsClient);
} }
protected override Result DoOpenDirectory(out IDirectory directory, in Path path, OpenDirectoryMode mode) protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,
OpenDirectoryMode mode)
{ {
UnsafeHelpers.SkipParamInit(out directory);
Result rc = ResolveFullPath(out string fullPath, in path, true); Result rc = ResolveFullPath(out string fullPath, in path, true);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
@ -398,14 +398,12 @@ namespace LibHac.FsSystem
OpenDirectoryInternal(out dirTemp, mode, dirInfo), _fsClient); OpenDirectoryInternal(out dirTemp, mode, dirInfo), _fsClient);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
directory = dirTemp; outDirectory.Reset(dirTemp);
return Result.Success; return Result.Success;
} }
protected override Result DoOpenFile(out IFile file, in Path path, OpenMode mode) protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode)
{ {
UnsafeHelpers.SkipParamInit(out file);
Result rc = ResolveFullPath(out string fullPath, in path, true); Result rc = ResolveFullPath(out string fullPath, in path, true);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
@ -423,7 +421,7 @@ namespace LibHac.FsSystem
OpenFileInternal(out fileStream, fullPath, mode), _fsClient); OpenFileInternal(out fileStream, fullPath, mode), _fsClient);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
file = new LocalFile(fileStream, mode); outFile.Reset(new LocalFile(fileStream, mode));
return Result.Success; return Result.Success;
} }

View File

@ -34,13 +34,14 @@ namespace LibHac.FsSystem
BaseStorage = storage; BaseStorage = storage;
} }
protected override Result DoOpenDirectory(out IDirectory directory, in Path path, OpenDirectoryMode mode) protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,
OpenDirectoryMode mode)
{ {
directory = new PartitionDirectory(this, path.ToString(), mode); outDirectory.Reset(new PartitionDirectory(this, path.ToString(), mode));
return Result.Success; return Result.Success;
} }
protected override Result DoOpenFile(out IFile file, in Path path, OpenMode mode) protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode)
{ {
string pathNormalized = PathTools.Normalize(path.ToString()).TrimStart('/'); string pathNormalized = PathTools.Normalize(path.ToString()).TrimStart('/');
@ -49,7 +50,7 @@ namespace LibHac.FsSystem
ThrowHelper.ThrowResult(ResultFs.PathNotFound.Value); ThrowHelper.ThrowResult(ResultFs.PathNotFound.Value);
} }
file = OpenFile(entry, mode); outFile.Reset(OpenFile(entry, mode));
return Result.Success; return Result.Success;
} }

View File

@ -26,12 +26,14 @@ namespace LibHac.FsSystem
/// </summary> /// </summary>
public PartitionFileSystemBuilder(IFileSystem input) public PartitionFileSystemBuilder(IFileSystem input)
{ {
using var file = new UniqueRef<IFile>();
foreach (DirectoryEntryEx entry in input.EnumerateEntries().Where(x => x.Type == DirectoryEntryType.File) foreach (DirectoryEntryEx entry in input.EnumerateEntries().Where(x => x.Type == DirectoryEntryType.File)
.OrderBy(x => x.FullPath, StringComparer.Ordinal)) .OrderBy(x => x.FullPath, StringComparer.Ordinal))
{ {
input.OpenFile(out IFile file, entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); input.OpenFile(ref file.Ref(), entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
AddFile(entry.FullPath.TrimStart('/'), file); AddFile(entry.FullPath.TrimStart('/'), file.Release());
} }
} }

View File

@ -49,10 +49,9 @@ namespace LibHac.FsSystem
base.Dispose(); base.Dispose();
} }
protected override Result DoOpenDirectory(out IDirectory directory, in Path path, OpenDirectoryMode mode) protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,
OpenDirectoryMode mode)
{ {
UnsafeHelpers.SkipParamInit(out directory);
if (!IsInitialized) if (!IsInitialized)
return ResultFs.PreconditionViolation.Log(); return ResultFs.PreconditionViolation.Log();
@ -61,15 +60,13 @@ namespace LibHac.FsSystem
if (path == rootPath) if (path == rootPath)
return ResultFs.PathNotFound.Log(); return ResultFs.PathNotFound.Log();
directory = new PartitionDirectory(this, mode); outDirectory.Reset(new PartitionDirectory(this, mode));
return Result.Success; return Result.Success;
} }
protected override Result DoOpenFile(out IFile file, in Path path, OpenMode mode) protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode)
{ {
UnsafeHelpers.SkipParamInit(out file);
if (!IsInitialized) if (!IsInitialized)
return ResultFs.PreconditionViolation.Log(); return ResultFs.PreconditionViolation.Log();
@ -81,7 +78,7 @@ namespace LibHac.FsSystem
ref T entry = ref MetaData.GetEntry(entryIndex); ref T entry = ref MetaData.GetEntry(entryIndex);
file = new PartitionFile(this, ref entry, mode); outFile.Reset(new PartitionFile(this, ref entry, mode));
return Result.Success; return Result.Success;
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using LibHac.Common;
using LibHac.Fs; using LibHac.Fs;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
@ -6,22 +7,22 @@ namespace LibHac.FsSystem
{ {
public class ReadOnlyFile : IFile public class ReadOnlyFile : IFile
{ {
private IFile BaseFile { get; } private UniqueRef<IFile> _baseFile;
public ReadOnlyFile(IFile baseFile) public ReadOnlyFile(ref UniqueRef<IFile> baseFile)
{ {
BaseFile = baseFile; _baseFile = new UniqueRef<IFile>(ref baseFile);
} }
protected override Result DoRead(out long bytesRead, long offset, Span<byte> destination, protected override Result DoRead(out long bytesRead, long offset, Span<byte> destination,
in ReadOption option) in ReadOption option)
{ {
return BaseFile.Read(out bytesRead, offset, destination, option); return _baseFile.Get.Read(out bytesRead, offset, destination, option);
} }
protected override Result DoGetSize(out long size) protected override Result DoGetSize(out long size)
{ {
return BaseFile.GetSize(out size); return _baseFile.Get.GetSize(out size);
} }
protected override Result DoFlush() protected override Result DoFlush()
@ -45,7 +46,7 @@ namespace LibHac.FsSystem
{ {
case OperationId.InvalidateCache: case OperationId.InvalidateCache:
case OperationId.QueryRange: case OperationId.QueryRange:
return BaseFile.OperateRange(outBuffer, operationId, offset, size, inBuffer); return _baseFile.Get.OperateRange(outBuffer, operationId, offset, size, inBuffer);
default: default:
return ResultFs.UnsupportedOperateRangeForReadOnlyFile.Log(); return ResultFs.UnsupportedOperateRangeForReadOnlyFile.Log();
} }

View File

@ -9,12 +9,6 @@ namespace LibHac.FsSystem
private IFileSystem BaseFs { get; } private IFileSystem BaseFs { get; }
private ReferenceCountedDisposable<IFileSystem> BaseFsShared { get; } private ReferenceCountedDisposable<IFileSystem> BaseFsShared { get; }
// Todo: Remove non-shared constructor
public ReadOnlyFileSystem(IFileSystem baseFileSystem)
{
BaseFs = baseFileSystem;
}
public ReadOnlyFileSystem(ReferenceCountedDisposable<IFileSystem> baseFileSystem) public ReadOnlyFileSystem(ReferenceCountedDisposable<IFileSystem> baseFileSystem)
{ {
BaseFsShared = baseFileSystem; BaseFsShared = baseFileSystem;
@ -28,35 +22,35 @@ namespace LibHac.FsSystem
return new ReferenceCountedDisposable<IFileSystem>(fs); return new ReferenceCountedDisposable<IFileSystem>(fs);
} }
protected override Result DoOpenDirectory(out IDirectory directory, in Path path, OpenDirectoryMode mode) protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,
OpenDirectoryMode mode)
{ {
return BaseFs.OpenDirectory(out directory, path, mode); return BaseFs.OpenDirectory(ref outDirectory, in path, mode);
} }
protected override Result DoOpenFile(out IFile file, in Path path, OpenMode mode) protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode)
{ {
UnsafeHelpers.SkipParamInit(out file); using var baseFile = new UniqueRef<IFile>();
Result rc = BaseFs.OpenFile(ref baseFile.Ref(), in path, mode);
Result rc = BaseFs.OpenFile(out IFile baseFile, path, mode);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
file = new ReadOnlyFile(baseFile); outFile.Reset(new ReadOnlyFile(ref baseFile.Ref()));
return Result.Success; return Result.Success;
} }
protected override Result DoGetEntryType(out DirectoryEntryType entryType, in Path path) protected override Result DoGetEntryType(out DirectoryEntryType entryType, in Path path)
{ {
return BaseFs.GetEntryType(out entryType, path); return BaseFs.GetEntryType(out entryType, in path);
} }
protected override Result DoGetFreeSpaceSize(out long freeSpace, in Path path) protected override Result DoGetFreeSpaceSize(out long freeSpace, in Path path)
{ {
return BaseFs.GetFreeSpaceSize(out freeSpace, path); return BaseFs.GetFreeSpaceSize(out freeSpace, in path);
} }
protected override Result DoGetTotalSpaceSize(out long totalSpace, in Path path) protected override Result DoGetTotalSpaceSize(out long totalSpace, in Path path)
{ {
return BaseFs.GetTotalSpaceSize(out totalSpace, path); return BaseFs.GetTotalSpaceSize(out totalSpace, in path);
// FS does: // FS does:
// return ResultFs.UnsupportedOperationReadOnlyFileSystemGetSpace.Log(); // return ResultFs.UnsupportedOperationReadOnlyFileSystemGetSpace.Log();
@ -64,7 +58,7 @@ namespace LibHac.FsSystem
protected override Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, in Path path) protected override Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, in Path path)
{ {
return BaseFs.GetFileTimeStampRaw(out timeStamp, path); return BaseFs.GetFileTimeStampRaw(out timeStamp, in path);
// FS does: // FS does:
// return ResultFs.NotImplemented.Log(); // return ResultFs.NotImplemented.Log();

View File

@ -39,9 +39,10 @@ namespace LibHac.FsSystem.RomFs
foreach (DirectoryEntryEx entry in input.EnumerateEntries().Where(x => x.Type == DirectoryEntryType.File) foreach (DirectoryEntryEx entry in input.EnumerateEntries().Where(x => x.Type == DirectoryEntryType.File)
.OrderBy(x => x.FullPath, StringComparer.Ordinal)) .OrderBy(x => x.FullPath, StringComparer.Ordinal))
{ {
input.OpenFile(out IFile file, entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); using var file = new UniqueRef<IFile>();
input.OpenFile(ref file.Ref(), entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
AddFile(entry.FullPath, file); AddFile(entry.FullPath, file.Release());
} }
} }

View File

@ -49,23 +49,20 @@ namespace LibHac.FsSystem.RomFs
return Result.Success; return Result.Success;
} }
protected override Result DoOpenDirectory(out IDirectory directory, in Path path, OpenDirectoryMode mode) protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,
OpenDirectoryMode mode)
{ {
UnsafeHelpers.SkipParamInit(out directory);
if (!FileTable.TryOpenDirectory(path.ToString(), out FindPosition position)) if (!FileTable.TryOpenDirectory(path.ToString(), out FindPosition position))
{ {
return ResultFs.PathNotFound.Log(); return ResultFs.PathNotFound.Log();
} }
directory = new RomFsDirectory(this, position, mode); outDirectory.Reset(new RomFsDirectory(this, position, mode));
return Result.Success; return Result.Success;
} }
protected override Result DoOpenFile(out IFile file, in Path path, OpenMode mode) protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode)
{ {
UnsafeHelpers.SkipParamInit(out file);
if (!FileTable.TryOpenFile(path.ToString(), out RomFileInfo info)) if (!FileTable.TryOpenFile(path.ToString(), out RomFileInfo info))
{ {
return ResultFs.PathNotFound.Log(); return ResultFs.PathNotFound.Log();
@ -77,7 +74,7 @@ namespace LibHac.FsSystem.RomFs
return ResultFs.InvalidArgument.Log(); return ResultFs.InvalidArgument.Log();
} }
file = new RomFsFile(BaseStorage, Header.DataOffset + info.Offset, info.Length); outFile.Reset(new RomFsFile(BaseStorage, Header.DataOffset + info.Offset, info.Length));
return Result.Success; return Result.Success;
} }

View File

@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using LibHac.Common;
using LibHac.Common.Keys; using LibHac.Common.Keys;
using LibHac.Crypto; using LibHac.Crypto;
using LibHac.Fs; using LibHac.Fs;
@ -148,91 +149,92 @@ namespace LibHac.FsSystem.Save
protected override Result DoCreateDirectory(in Path path) protected override Result DoCreateDirectory(in Path path)
{ {
Result result = SaveDataFileSystemCore.CreateDirectory(path); Result result = SaveDataFileSystemCore.CreateDirectory(in path);
return SaveResults.ConvertToExternalResult(result).LogConverted(result); return SaveResults.ConvertToExternalResult(result).LogConverted(result);
} }
protected override Result DoCreateFile(in Path path, long size, CreateFileOptions option) protected override Result DoCreateFile(in Path path, long size, CreateFileOptions option)
{ {
Result result = SaveDataFileSystemCore.CreateFile(path, size, option); Result result = SaveDataFileSystemCore.CreateFile(in path, size, option);
return SaveResults.ConvertToExternalResult(result).LogConverted(result); return SaveResults.ConvertToExternalResult(result).LogConverted(result);
} }
protected override Result DoDeleteDirectory(in Path path) protected override Result DoDeleteDirectory(in Path path)
{ {
Result result = SaveDataFileSystemCore.DeleteDirectory(path); Result result = SaveDataFileSystemCore.DeleteDirectory(in path);
return SaveResults.ConvertToExternalResult(result).LogConverted(result); return SaveResults.ConvertToExternalResult(result).LogConverted(result);
} }
protected override Result DoDeleteDirectoryRecursively(in Path path) protected override Result DoDeleteDirectoryRecursively(in Path path)
{ {
Result result = SaveDataFileSystemCore.DeleteDirectoryRecursively(path); Result result = SaveDataFileSystemCore.DeleteDirectoryRecursively(in path);
return SaveResults.ConvertToExternalResult(result).LogConverted(result); return SaveResults.ConvertToExternalResult(result).LogConverted(result);
} }
protected override Result DoCleanDirectoryRecursively(in Path path) protected override Result DoCleanDirectoryRecursively(in Path path)
{ {
Result result = SaveDataFileSystemCore.CleanDirectoryRecursively(path); Result result = SaveDataFileSystemCore.CleanDirectoryRecursively(in path);
return SaveResults.ConvertToExternalResult(result).LogConverted(result); return SaveResults.ConvertToExternalResult(result).LogConverted(result);
} }
protected override Result DoDeleteFile(in Path path) protected override Result DoDeleteFile(in Path path)
{ {
Result result = SaveDataFileSystemCore.DeleteFile(path); Result result = SaveDataFileSystemCore.DeleteFile(in path);
return SaveResults.ConvertToExternalResult(result).LogConverted(result); return SaveResults.ConvertToExternalResult(result).LogConverted(result);
} }
protected override Result DoOpenDirectory(out IDirectory directory, in Path path, OpenDirectoryMode mode) protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,
OpenDirectoryMode mode)
{ {
Result result = SaveDataFileSystemCore.OpenDirectory(out directory, path, mode); Result result = SaveDataFileSystemCore.OpenDirectory(ref outDirectory, in path, mode);
return SaveResults.ConvertToExternalResult(result).LogConverted(result); return SaveResults.ConvertToExternalResult(result).LogConverted(result);
} }
protected override Result DoOpenFile(out IFile file, in Path path, OpenMode mode) protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode)
{ {
Result result = SaveDataFileSystemCore.OpenFile(out file, path, mode); Result result = SaveDataFileSystemCore.OpenFile(ref outFile, in path, mode);
return SaveResults.ConvertToExternalResult(result).LogConverted(result); return SaveResults.ConvertToExternalResult(result).LogConverted(result);
} }
protected override Result DoRenameDirectory(in Path currentPath, in Path newPath) protected override Result DoRenameDirectory(in Path currentPath, in Path newPath)
{ {
Result result = SaveDataFileSystemCore.RenameDirectory(currentPath, newPath); Result result = SaveDataFileSystemCore.RenameDirectory(in currentPath, in newPath);
return SaveResults.ConvertToExternalResult(result).LogConverted(result); return SaveResults.ConvertToExternalResult(result).LogConverted(result);
} }
protected override Result DoRenameFile(in Path currentPath, in Path newPath) protected override Result DoRenameFile(in Path currentPath, in Path newPath)
{ {
Result result = SaveDataFileSystemCore.RenameFile(currentPath, newPath); Result result = SaveDataFileSystemCore.RenameFile(in currentPath, in newPath);
return SaveResults.ConvertToExternalResult(result).LogConverted(result); return SaveResults.ConvertToExternalResult(result).LogConverted(result);
} }
protected override Result DoGetEntryType(out DirectoryEntryType entryType, in Path path) protected override Result DoGetEntryType(out DirectoryEntryType entryType, in Path path)
{ {
Result result = SaveDataFileSystemCore.GetEntryType(out entryType, path); Result result = SaveDataFileSystemCore.GetEntryType(out entryType, in path);
return SaveResults.ConvertToExternalResult(result).LogConverted(result); return SaveResults.ConvertToExternalResult(result).LogConverted(result);
} }
protected override Result DoGetFreeSpaceSize(out long freeSpace, in Path path) protected override Result DoGetFreeSpaceSize(out long freeSpace, in Path path)
{ {
Result result = SaveDataFileSystemCore.GetFreeSpaceSize(out freeSpace, path); Result result = SaveDataFileSystemCore.GetFreeSpaceSize(out freeSpace, in path);
return SaveResults.ConvertToExternalResult(result).LogConverted(result); return SaveResults.ConvertToExternalResult(result).LogConverted(result);
} }
protected override Result DoGetTotalSpaceSize(out long totalSpace, in Path path) protected override Result DoGetTotalSpaceSize(out long totalSpace, in Path path)
{ {
Result result = SaveDataFileSystemCore.GetTotalSpaceSize(out totalSpace, path); Result result = SaveDataFileSystemCore.GetTotalSpaceSize(out totalSpace, in path);
return SaveResults.ConvertToExternalResult(result).LogConverted(result); return SaveResults.ConvertToExternalResult(result).LogConverted(result);
} }

View File

@ -134,10 +134,9 @@ namespace LibHac.FsSystem.Save
return Result.Success; return Result.Success;
} }
protected override Result DoOpenDirectory(out IDirectory directory, in Path path, OpenDirectoryMode mode) protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,
OpenDirectoryMode mode)
{ {
UnsafeHelpers.SkipParamInit(out directory);
Result rc = CheckIfNormalized(in path); Result rc = CheckIfNormalized(in path);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
@ -146,15 +145,13 @@ namespace LibHac.FsSystem.Save
return ResultFs.PathNotFound.Log(); return ResultFs.PathNotFound.Log();
} }
directory = new SaveDataDirectory(this, position, mode); outDirectory.Reset(new SaveDataDirectory(this, position, mode));
return Result.Success; return Result.Success;
} }
protected override Result DoOpenFile(out IFile file, in Path path, OpenMode mode) protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode)
{ {
UnsafeHelpers.SkipParamInit(out file);
Result rc = CheckIfNormalized(in path); Result rc = CheckIfNormalized(in path);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
@ -165,7 +162,7 @@ namespace LibHac.FsSystem.Save
AllocationTableStorage storage = OpenFatStorage(fileInfo.StartBlock); AllocationTableStorage storage = OpenFatStorage(fileInfo.StartBlock);
file = new SaveDataFile(storage, new U8Span(path.GetString()), FileTable, fileInfo.Length, mode); outFile.Reset(new SaveDataFile(storage, new U8Span(path.GetString()), FileTable, fileInfo.Length, mode));
return Result.Success; return Result.Success;
} }

View File

@ -95,14 +95,15 @@ namespace LibHac.FsSystem
base.Dispose(); base.Dispose();
} }
protected override Result DoOpenFile(out IFile file, in Path path, OpenMode mode) protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode)
{ {
return _baseFileSystem.Target.OpenFile(out file, path, mode); return _baseFileSystem.Target.OpenFile(ref outFile, path, mode);
} }
protected override Result DoOpenDirectory(out IDirectory directory, in Path path, OpenDirectoryMode mode) protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,
OpenDirectoryMode mode)
{ {
return _baseFileSystem.Target.OpenDirectory(out directory, path, mode); return _baseFileSystem.Target.OpenDirectory(ref outDirectory, path, mode);
} }
protected override Result DoGetEntryType(out DirectoryEntryType entryType, in Path path) protected override Result DoGetEntryType(out DirectoryEntryType entryType, in Path path)

View File

@ -111,16 +111,17 @@ namespace LibHac.FsSystem
return BaseFileSystem.Target.GetTotalSpaceSize(out totalSpace, path); return BaseFileSystem.Target.GetTotalSpaceSize(out totalSpace, path);
} }
protected override Result DoOpenFile(out IFile file, in Path path, OpenMode mode) protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode)
{ {
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageFlag); using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageFlag);
return BaseFileSystem.Target.OpenFile(out file, path, mode); return BaseFileSystem.Target.OpenFile(ref outFile, path, mode);
} }
protected override Result DoOpenDirectory(out IDirectory directory, in Path path, OpenDirectoryMode mode) protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,
OpenDirectoryMode mode)
{ {
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageFlag); using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageFlag);
return BaseFileSystem.Target.OpenDirectory(out directory, path, mode); return BaseFileSystem.Target.OpenDirectory(ref outDirectory, path, mode);
} }
protected override Result DoCommit() protected override Result DoCommit()

View File

@ -40,7 +40,7 @@ namespace LibHac.FsSystem
private Result ResolveFullPath(ref Path outPath, in Path relativePath) private Result ResolveFullPath(ref Path outPath, in Path relativePath)
{ {
Path rootPath = _rootPath.GetPath(); Path rootPath = _rootPath.DangerousGetPath();
return outPath.Combine(in rootPath, in relativePath); return outPath.Combine(in rootPath, in relativePath);
} }
@ -100,29 +100,26 @@ namespace LibHac.FsSystem
return Result.Success; return Result.Success;
} }
protected override Result DoOpenFile(out IFile file, in Path path, OpenMode mode) protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode)
{ {
UnsafeHelpers.SkipParamInit(out file);
using var fullPath = new Path(); using var fullPath = new Path();
Result rc = ResolveFullPath(ref fullPath.Ref(), in path); Result rc = ResolveFullPath(ref fullPath.Ref(), in path);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = _baseFileSystem.OpenFile(out file, in fullPath, mode); rc = _baseFileSystem.OpenFile(ref outFile, in fullPath, mode);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
return Result.Success; return Result.Success;
} }
protected override Result DoOpenDirectory(out IDirectory directory, in Path path, OpenDirectoryMode mode) protected override Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,
OpenDirectoryMode mode)
{ {
UnsafeHelpers.SkipParamInit(out directory);
using var fullPath = new Path(); using var fullPath = new Path();
Result rc = ResolveFullPath(ref fullPath.Ref(), in path); Result rc = ResolveFullPath(ref fullPath.Ref(), in path);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = _baseFileSystem.OpenDirectory(out directory, in fullPath, mode); rc = _baseFileSystem.OpenDirectory(ref outDirectory, in fullPath, mode);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
return Result.Success; return Result.Success;

View File

@ -39,105 +39,92 @@ namespace LibHac.FsSystem
ref DirectoryEntry dirEntry, FsIterationTask onEnterDir, FsIterationTask onExitDir, FsIterationTask onFile, ref DirectoryEntry dirEntry, FsIterationTask onEnterDir, FsIterationTask onExitDir, FsIterationTask onFile,
ref FsIterationTaskClosure closure) ref FsIterationTaskClosure closure)
{ {
IDirectory directory = null; using var directory = new UniqueRef<IDirectory>();
try
Result rc = fs.OpenDirectory(ref directory.Ref(), in workPath, OpenDirectoryMode.All);
if (rc.IsFailure()) return rc;
while (true)
{ {
Result rc = fs.OpenDirectory(out directory, in workPath, OpenDirectoryMode.All); rc = directory.Get.Read(out long entriesRead, SpanHelpers.AsSpan(ref dirEntry));
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
while (true) if (entriesRead == 0)
break;
workPath.AppendChild(dirEntry.Name);
if (rc.IsFailure()) return rc;
if (dirEntry.Type == DirectoryEntryType.Directory)
{ {
rc = directory.Read(out long entriesRead, SpanHelpers.AsSpan(ref dirEntry)); rc = onEnterDir(in workPath, in dirEntry, ref closure);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
if (entriesRead == 0) rc = IterateDirectoryRecursivelyInternal(fs, ref workPath, ref dirEntry, onEnterDir, onExitDir,
break; onFile, ref closure);
workPath.AppendChild(dirEntry.Name);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
if (dirEntry.Type == DirectoryEntryType.Directory) rc = onExitDir(in workPath, in dirEntry, ref closure);
{ if (rc.IsFailure()) return rc;
rc = onEnterDir(in workPath, in dirEntry, ref closure); }
if (rc.IsFailure()) return rc; else
{
rc = IterateDirectoryRecursivelyInternal(fs, ref workPath, ref dirEntry, onEnterDir, onExitDir, rc = onFile(in workPath, in dirEntry, ref closure);
onFile, ref closure);
if (rc.IsFailure()) return rc;
rc = onExitDir(in workPath, in dirEntry, ref closure);
if (rc.IsFailure()) return rc;
}
else
{
rc = onFile(in workPath, in dirEntry, ref closure);
if (rc.IsFailure()) return rc;
}
rc = workPath.RemoveChild();
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
} }
return Result.Success; rc = workPath.RemoveChild();
} if (rc.IsFailure()) return rc;
finally
{
directory?.Dispose();
} }
return Result.Success;
} }
private static Result CleanupDirectoryRecursivelyInternal(IFileSystem fs, ref Path workPath, private static Result CleanupDirectoryRecursivelyInternal(IFileSystem fs, ref Path workPath,
ref DirectoryEntry dirEntry, FsIterationTask onEnterDir, FsIterationTask onExitDir, FsIterationTask onFile, ref DirectoryEntry dirEntry, FsIterationTask onEnterDir, FsIterationTask onExitDir, FsIterationTask onFile,
ref FsIterationTaskClosure closure) ref FsIterationTaskClosure closure)
{ {
IDirectory directory = null; using var directory = new UniqueRef<IDirectory>();
try
while (true)
{ {
while (true) Result rc = fs.OpenDirectory(ref directory.Ref(), in workPath, OpenDirectoryMode.All);
if (rc.IsFailure()) return rc;
rc = directory.Get.Read(out long entriesRead, SpanHelpers.AsSpan(ref dirEntry));
if (rc.IsFailure()) return rc;
directory.Reset(null);
if (entriesRead == 0)
break;
rc = workPath.AppendChild(dirEntry.Name);
if (rc.IsFailure()) return rc;
if (dirEntry.Type == DirectoryEntryType.Directory)
{ {
Result rc = fs.OpenDirectory(out directory, in workPath, OpenDirectoryMode.All); rc = onEnterDir(in workPath, in dirEntry, ref closure);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = directory.Read(out long entriesRead, SpanHelpers.AsSpan(ref dirEntry)); rc = CleanupDirectoryRecursivelyInternal(fs, ref workPath, ref dirEntry, onEnterDir, onExitDir,
onFile, ref closure);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
directory.Dispose(); rc = onExitDir(in workPath, in dirEntry, ref closure);
directory = null;
if (entriesRead == 0)
break;
rc = workPath.AppendChild(dirEntry.Name);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
}
if (dirEntry.Type == DirectoryEntryType.Directory) else
{ {
rc = onEnterDir(in workPath, in dirEntry, ref closure); rc = onFile(in workPath, in dirEntry, ref closure);
if (rc.IsFailure()) return rc;
rc = CleanupDirectoryRecursivelyInternal(fs, ref workPath, ref dirEntry, onEnterDir, onExitDir,
onFile, ref closure);
if (rc.IsFailure()) return rc;
rc = onExitDir(in workPath, in dirEntry, ref closure);
if (rc.IsFailure()) return rc;
}
else
{
rc = onFile(in workPath, in dirEntry, ref closure);
if (rc.IsFailure()) return rc;
}
rc = workPath.RemoveChild();
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
} }
return Result.Success; rc = workPath.RemoveChild();
} if (rc.IsFailure()) return rc;
finally
{
directory?.Dispose();
} }
return Result.Success;
} }
public static Result IterateDirectoryRecursively(IFileSystem fs, in Path rootPath, ref DirectoryEntry dirEntry, public static Result IterateDirectoryRecursively(IFileSystem fs, in Path rootPath, ref DirectoryEntry dirEntry,
@ -171,38 +158,34 @@ namespace LibHac.FsSystem
in Path sourcePath, Span<byte> workBuffer) in Path sourcePath, Span<byte> workBuffer)
{ {
// Open source file. // Open source file.
Result rc = sourceFileSystem.OpenFile(out IFile sourceFile, sourcePath, OpenMode.Read); using var sourceFile = new UniqueRef<IFile>();
Result rc = sourceFileSystem.OpenFile(ref sourceFile.Ref(), sourcePath, OpenMode.Read);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
using (sourceFile) rc = sourceFile.Get.GetSize(out long fileSize);
if (rc.IsFailure()) return rc;
using var destFile = new UniqueRef<IFile>();
rc = destFileSystem.CreateFile(in destPath, fileSize);
if (rc.IsFailure()) return rc;
rc = destFileSystem.OpenFile(ref destFile.Ref(), in destPath, OpenMode.Write);
if (rc.IsFailure()) return rc;
// Read/Write file in work buffer sized chunks.
long remaining = fileSize;
long offset = 0;
while (remaining > 0)
{ {
rc = sourceFile.GetSize(out long fileSize); rc = sourceFile.Get.Read(out long bytesRead, offset, workBuffer, ReadOption.None);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = destFileSystem.CreateFile(in destPath, fileSize); rc = destFile.Get.Write(offset, workBuffer.Slice(0, (int)bytesRead), WriteOption.None);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = destFileSystem.OpenFile(out IFile destFile, in destPath, OpenMode.Write); remaining -= bytesRead;
if (rc.IsFailure()) return rc; offset += bytesRead;
using (destFile)
{
// Read/Write file in work buffer sized chunks.
long remaining = fileSize;
long offset = 0;
while (remaining > 0)
{
rc = sourceFile.Read(out long bytesRead, offset, workBuffer, ReadOption.None);
if (rc.IsFailure()) return rc;
rc = destFile.Write(offset, workBuffer.Slice(0, (int)bytesRead), WriteOption.None);
if (rc.IsFailure()) return rc;
remaining -= bytesRead;
offset += bytesRead;
}
}
} }
return Result.Success; return Result.Success;
@ -300,31 +283,25 @@ namespace LibHac.FsSystem
static Result OnFile(in Path path, in DirectoryEntry entry, ref FsIterationTaskClosure closure) static Result OnFile(in Path path, in DirectoryEntry entry, ref FsIterationTaskClosure closure)
{ {
IFile file = null; using var file = new UniqueRef<IFile>();
try
Result rc = closure.SourceFileSystem.OpenFile(ref file.Ref(), in path, OpenMode.Read);
if (rc.IsFailure()) return rc;
long offset = 0;
while (true)
{ {
Result rc = closure.SourceFileSystem.OpenFile(out file, in path, OpenMode.Read); rc = file.Get.Read(out long bytesRead, offset, closure.Buffer, ReadOption.None);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
long offset = 0; if (bytesRead < closure.Buffer.Length)
break;
while (true) offset += bytesRead;
{
rc = file.Read(out long bytesRead, offset, closure.Buffer, ReadOption.None);
if (rc.IsFailure()) return rc;
if (bytesRead < closure.Buffer.Length)
break;
offset += bytesRead;
}
return Result.Success;
}
finally
{
file?.Dispose();
} }
return Result.Success;
} }
using var rootPath = new Path(); using var rootPath = new Path();
@ -420,46 +397,30 @@ namespace LibHac.FsSystem
} }
} }
public static Result TryAcquireCountSemaphore(out UniqueLock<SemaphoreAdapter> uniqueLock, SemaphoreAdapter semaphore) public static Result TryAcquireCountSemaphore(ref UniqueLock<SemaphoreAdapter> outUniqueLock,
SemaphoreAdapter semaphore)
{ {
UniqueLock<SemaphoreAdapter> tempUniqueLock = default; using var uniqueLock = new UniqueLock<SemaphoreAdapter>(semaphore, new DeferLock());
try
{
tempUniqueLock = new UniqueLock<SemaphoreAdapter>(semaphore, new DeferLock());
if (!tempUniqueLock.TryLock()) if (!uniqueLock.TryLock())
{ return ResultFs.OpenCountLimit.Log();
uniqueLock = default;
return ResultFs.OpenCountLimit.Log();
}
uniqueLock = Shared.Move(ref tempUniqueLock); outUniqueLock.Set(ref uniqueLock.Ref());
return Result.Success; return Result.Success;
}
finally
{
tempUniqueLock.Dispose();
}
} }
public static Result MakeUniqueLockWithPin<T>(out IUniqueLock uniqueLock, SemaphoreAdapter semaphore, public static Result MakeUniqueLockWithPin<T>(ref UniqueRef<IUniqueLock> outUniqueLock,
ref ReferenceCountedDisposable<T> objectToPin) where T : class, IDisposable SemaphoreAdapter semaphore, ref ReferenceCountedDisposable<T> objectToPin) where T : class, IDisposable
{ {
UnsafeHelpers.SkipParamInit(out uniqueLock); using var semaphoreAdapter = new UniqueLock<SemaphoreAdapter>();
Result rc = TryAcquireCountSemaphore(ref semaphoreAdapter.Ref(), semaphore);
if (rc.IsFailure()) return rc;
UniqueLock<SemaphoreAdapter> tempUniqueLock = default; var lockWithPin = new UniqueLockWithPin<T>(ref semaphoreAdapter.Ref(), ref objectToPin);
try using var uniqueLock = new UniqueRef<IUniqueLock>(lockWithPin);
{
Result rc = TryAcquireCountSemaphore(out tempUniqueLock, semaphore);
if (rc.IsFailure()) return rc;
uniqueLock = new UniqueLockWithPin<T>(ref tempUniqueLock, ref objectToPin); outUniqueLock.Set(ref uniqueLock.Ref());
return Result.Success; return Result.Success;
}
finally
{
tempUniqueLock.Dispose();
}
} }
} }
} }

View File

@ -2,6 +2,7 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading; using System.Threading;
using LibHac.Common; using LibHac.Common;
using static InlineIL.IL.Emit;
namespace LibHac.Os namespace LibHac.Os
{ {
@ -24,6 +25,22 @@ namespace LibHac.Os
{ {
return new UniqueLock<TMutex>(lockable); return new UniqueLock<TMutex>(lockable);
} }
// ReSharper disable once EntityNameCapturedOnly.Global
public static ref UniqueLockRef<T> Ref<T>(this in UniqueLockRef<T> value) where T : struct, ILockable
{
Ldarg(nameof(value));
Ret();
throw InlineIL.IL.Unreachable();
}
// ReSharper disable once EntityNameCapturedOnly.Global
public static ref UniqueLock<T> Ref<T>(this in UniqueLock<T> value) where T : class, ILockable
{
Ldarg(nameof(value));
Ret();
throw InlineIL.IL.Unreachable();
}
} }
public ref struct UniqueLockRef<TMutex> where TMutex : struct, ILockable public ref struct UniqueLockRef<TMutex> where TMutex : struct, ILockable

View File

@ -107,9 +107,10 @@ namespace LibHac
SwitchFsNca nca = null; SwitchFsNca nca = null;
try try
{ {
ContentFs.OpenFile(out IFile ncaFile, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); using var ncaFile = new UniqueRef<IFile>();
ContentFs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
nca = new SwitchFsNca(new Nca(KeySet, ncaFile.AsStorage())); nca = new SwitchFsNca(new Nca(KeySet, ncaFile.Release().AsStorage()));
nca.NcaId = GetNcaFilename(fileEntry.Name, nca); nca.NcaId = GetNcaFilename(fileEntry.Name, nca);
string extension = nca.Nca.Header.ContentType == NcaContentType.Meta ? ".cnmt.nca" : ".nca"; string extension = nca.Nca.Header.ContentType == NcaContentType.Meta ? ".cnmt.nca" : ".nca";
@ -145,9 +146,10 @@ namespace LibHac
try try
{ {
SaveFs.OpenFile(out IFile file, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); using var file = new UniqueRef<IFile>();
SaveFs.OpenFile(ref file.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
save = new SaveDataFileSystem(KeySet, file.AsStorage(), IntegrityCheckLevel.None, true); save = new SaveDataFileSystem(KeySet, file.Release().AsStorage(), IntegrityCheckLevel.None, true);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -172,9 +174,10 @@ namespace LibHac
IFileSystem fs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); IFileSystem fs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath; string cnmtPath = fs.EnumerateEntries("/", "*.cnmt").Single().FullPath;
fs.OpenFile(out IFile file, cnmtPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); using var file = new UniqueRef<IFile>();
fs.OpenFile(ref file.Ref(), cnmtPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
var metadata = new Cnmt(file.AsStream()); var metadata = new Cnmt(file.Release().AsStream());
title.Id = metadata.TitleId; title.Id = metadata.TitleId;
title.Version = metadata.TitleVersion; title.Version = metadata.TitleVersion;
title.Metadata = metadata; title.Metadata = metadata;
@ -216,11 +219,12 @@ namespace LibHac
foreach (Title title in Titles.Values.Where(x => x.ControlNca != null)) foreach (Title title in Titles.Values.Where(x => x.ControlNca != null))
{ {
IFileSystem romfs = title.ControlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); IFileSystem romfs = title.ControlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
romfs.OpenFile(out IFile control, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
using (control) using (var control = new UniqueRef<IFile>())
{ {
control.Read(out _, 0, title.Control.ByteSpan).ThrowIfFailure(); romfs.OpenFile(ref control.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
control.Get.Read(out _, 0, title.Control.ByteSpan).ThrowIfFailure();
} }
foreach (ref ApplicationControlTitle desc in title.Control.Value.Titles) foreach (ref ApplicationControlTitle desc in title.Control.Value.Titles)

View File

@ -32,8 +32,9 @@ namespace LibHac
XciPartition root = GetRootPartition(); XciPartition root = GetRootPartition();
if (type == XciPartitionType.Root) return root; if (type == XciPartitionType.Root) return root;
root.OpenFile(out IFile partitionFile, type.GetFileName().ToU8Span(), OpenMode.Read).ThrowIfFailure(); using var partitionFile = new UniqueRef<IFile>();
return new XciPartition(partitionFile.AsStorage()); root.OpenFile(ref partitionFile.Ref(), type.GetFileName().ToU8Span(), OpenMode.Read).ThrowIfFailure();
return new XciPartition(partitionFile.Release().AsStorage());
} }
private XciPartition GetRootPartition() private XciPartition GetRootPartition()

View File

@ -36,9 +36,10 @@ namespace hactoolnet
throw new FileNotFoundException("Specified NCA does not contain a delta fragment"); throw new FileNotFoundException("Specified NCA does not contain a delta fragment");
} }
fs.OpenFile(out IFile deltaFragmentFile, FragmentFileName.ToU8String(), OpenMode.Read).ThrowIfFailure(); using var deltaFragmentFile = new UniqueRef<IFile>();
fs.OpenFile(ref deltaFragmentFile.Ref(), FragmentFileName.ToU8String(), OpenMode.Read).ThrowIfFailure();
deltaStorage = deltaFragmentFile.AsStorage(); deltaStorage = deltaFragmentFile.Release().AsStorage();
} }
catch (InvalidDataException) { } // Ignore non-NCA3 files catch (InvalidDataException) { } // Ignore non-NCA3 files
} }

View File

@ -5,6 +5,7 @@ using LibHac;
using LibHac.Common; using LibHac.Common;
using LibHac.Crypto; using LibHac.Crypto;
using LibHac.Fs; using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem; using LibHac.FsSystem;
using LibHac.Util; using LibHac.Util;
using static hactoolnet.Print; using static hactoolnet.Print;
@ -23,7 +24,7 @@ namespace hactoolnet
Span<AesXtsKey> keys = ctx.KeySet.SdCardEncryptionKeys; Span<AesXtsKey> keys = ctx.KeySet.SdCardEncryptionKeys;
using var baseFile = new LocalFile(ctx.Options.InFile, OpenMode.Read); using var baseFile = new UniqueRef<IFile>(new LocalFile(ctx.Options.InFile, OpenMode.Read));
AesXtsFile xtsFile = null; AesXtsFile xtsFile = null;
int contentType = 0; int contentType = 0;
@ -35,7 +36,7 @@ namespace hactoolnet
try try
{ {
xtsFile = new AesXtsFile(OpenMode.Read, baseFile, ctx.Options.SdPath.ToU8String(), kekSource, validationKey, 0x4000); xtsFile = new AesXtsFile(OpenMode.Read, ref baseFile.Ref(), ctx.Options.SdPath.ToU8String(), kekSource, validationKey, 0x4000);
contentType = i; contentType = i;
break; break;

View File

@ -227,8 +227,9 @@ namespace hactoolnet
IFileSystem pfs = nca.OpenFileSystem(NcaSectionType.Code, IntegrityCheckLevel.ErrorOnInvalid); IFileSystem pfs = nca.OpenFileSystem(NcaSectionType.Code, IntegrityCheckLevel.ErrorOnInvalid);
if (!pfs.FileExists("main.npdm")) return Validity.Unchecked; if (!pfs.FileExists("main.npdm")) return Validity.Unchecked;
pfs.OpenFile(out IFile npdmFile, "main.npdm".ToU8String(), OpenMode.Read).ThrowIfFailure(); using var npdmFile = new UniqueRef<IFile>();
var npdm = new NpdmBinary(npdmFile.AsStream()); pfs.OpenFile(ref npdmFile.Ref(), "main.npdm".ToU8String(), OpenMode.Read).ThrowIfFailure();
var npdm = new NpdmBinary(npdmFile.Release().AsStream());
return nca.Header.VerifySignature2(npdm.AciD.Rsa2048Modulus); return nca.Header.VerifySignature2(npdm.AciD.Rsa2048Modulus);
} }
@ -258,10 +259,12 @@ namespace hactoolnet
if (nca.CanOpenSection(NcaSectionType.Code)) if (nca.CanOpenSection(NcaSectionType.Code))
{ {
IFileSystem fs = nca.OpenFileSystem(NcaSectionType.Code, IntegrityCheckLevel.None); IFileSystem fs = nca.OpenFileSystem(NcaSectionType.Code, IntegrityCheckLevel.None);
Result r = fs.OpenFile(out IFile file, "/main.npdm".ToU8String(), OpenMode.Read);
using var file = new UniqueRef<IFile>();
Result r = fs.OpenFile(ref file.Ref(), "/main.npdm".ToU8String(), OpenMode.Read);
if (r.IsSuccess()) if (r.IsSuccess())
{ {
var npdm = new NpdmBinary(file.AsStream(), null); var npdm = new NpdmBinary(file.Release().AsStream(), null);
PrintItem(sb, colLen, "Title Name:", npdm.TitleName); PrintItem(sb, colLen, "Title Name:", npdm.TitleName);
} }
} }

View File

@ -67,26 +67,23 @@ namespace hactoolnet
string destFilename = ctx.Options.ReplaceFileDest; string destFilename = ctx.Options.ReplaceFileDest;
if (!destFilename.StartsWith("/")) destFilename = '/' + destFilename; if (!destFilename.StartsWith("/")) destFilename = '/' + destFilename;
using (IFile inFile = new LocalFile(ctx.Options.ReplaceFileSource, OpenMode.Read)) using var inFile = new UniqueRef<IFile>(new LocalFile(ctx.Options.ReplaceFileSource, OpenMode.Read));
using var outFile = new UniqueRef<IFile>();
save.OpenFile(ref outFile.Ref(), destFilename.ToU8String(), OpenMode.ReadWrite).ThrowIfFailure();
inFile.Get.GetSize(out long inFileSize).ThrowIfFailure();
outFile.Get.GetSize(out long outFileSize).ThrowIfFailure();
if (inFileSize != outFileSize)
{ {
save.OpenFile(out IFile outFile, destFilename.ToU8String(), OpenMode.ReadWrite).ThrowIfFailure(); outFile.Get.SetSize(inFileSize).ThrowIfFailure();
using (outFile)
{
inFile.GetSize(out long inFileSize).ThrowIfFailure();
outFile.GetSize(out long outFileSize).ThrowIfFailure();
if (inFileSize != outFileSize)
{
outFile.SetSize(inFileSize).ThrowIfFailure();
}
inFile.CopyTo(outFile, ctx.Logger);
ctx.Logger.LogMessage($"Replaced file {destFilename}");
}
} }
inFile.Get.CopyTo(outFile.Get, ctx.Logger);
ctx.Logger.LogMessage($"Replaced file {destFilename}");
signNeeded = true; signNeeded = true;
} }

View File

@ -34,20 +34,45 @@ namespace LibHac.Tests.Fs
public IFileSystem Create() public IFileSystem Create()
{ {
DirectorySaveDataFileSystem CreateDirSaveFs(out DirectorySaveDataFileSystem saveFs, BaseFileSystem, true, true, true)
.CreateNew(out DirectorySaveDataFileSystem saveFs, BaseFileSystem, true, true, true)
.ThrowIfFailure(); .ThrowIfFailure();
return saveFs; return saveFs;
} }
} }
public static Result CreateDirSaveFs(out DirectorySaveDataFileSystem created, IFileSystem baseFileSystem,
ISaveDataCommitTimeStampGetter timeStampGetter, RandomDataGenerator randomGenerator,
bool isJournalingSupported, bool isMultiCommitSupported, bool isJournalingEnabled,
FileSystemClient fsClient)
{
var obj = new DirectorySaveDataFileSystem(baseFileSystem, fsClient);
Result rc = obj.Initialize(timeStampGetter, randomGenerator, isJournalingSupported, isMultiCommitSupported,
isJournalingEnabled);
if (rc.IsSuccess())
{
created = obj;
return Result.Success;
}
obj.Dispose();
UnsafeHelpers.SkipParamInit(out created);
return rc;
}
public static Result CreateDirSaveFs(out DirectorySaveDataFileSystem created, IFileSystem baseFileSystem,
bool isJournalingSupported, bool isMultiCommitSupported, bool isJournalingEnabled)
{
return CreateDirSaveFs(out created, baseFileSystem, null, null, isJournalingSupported, isMultiCommitSupported,
isJournalingEnabled, null);
}
private (IFileSystem baseFs, DirectorySaveDataFileSystem saveFs) CreateFileSystemInternal() private (IFileSystem baseFs, DirectorySaveDataFileSystem saveFs) CreateFileSystemInternal()
{ {
var baseFs = new InMemoryFileSystem(); var baseFs = new InMemoryFileSystem();
DirectorySaveDataFileSystem.CreateNew(out DirectorySaveDataFileSystem saveFs, baseFs, true, true, true) CreateDirSaveFs(out DirectorySaveDataFileSystem saveFs, baseFs, true, true, true).ThrowIfFailure();
.ThrowIfFailure();
return (baseFs, saveFs); return (baseFs, saveFs);
} }
@ -132,8 +157,7 @@ namespace LibHac.Tests.Fs
baseFs.CreateFile("/0/file1", 0, CreateFileOptions.None).ThrowIfFailure(); baseFs.CreateFile("/0/file1", 0, CreateFileOptions.None).ThrowIfFailure();
baseFs.CreateFile("/1/file2", 0, CreateFileOptions.None).ThrowIfFailure(); baseFs.CreateFile("/1/file2", 0, CreateFileOptions.None).ThrowIfFailure();
DirectorySaveDataFileSystem.CreateNew(out DirectorySaveDataFileSystem saveFs, baseFs, true, true, true) CreateDirSaveFs(out DirectorySaveDataFileSystem saveFs, baseFs, true, true, true).ThrowIfFailure();
.ThrowIfFailure();
Assert.Success(saveFs.GetEntryType(out _, "/file1")); Assert.Success(saveFs.GetEntryType(out _, "/file1"));
Assert.Result(ResultFs.PathNotFound, saveFs.GetEntryType(out _, "/file2")); Assert.Result(ResultFs.PathNotFound, saveFs.GetEntryType(out _, "/file2"));
@ -151,8 +175,7 @@ namespace LibHac.Tests.Fs
baseFs.CreateFile("/_/file1", 0, CreateFileOptions.None).ThrowIfFailure(); baseFs.CreateFile("/_/file1", 0, CreateFileOptions.None).ThrowIfFailure();
baseFs.CreateFile("/1/file2", 0, CreateFileOptions.None).ThrowIfFailure(); baseFs.CreateFile("/1/file2", 0, CreateFileOptions.None).ThrowIfFailure();
DirectorySaveDataFileSystem.CreateNew(out DirectorySaveDataFileSystem saveFs, baseFs, true, true, true) CreateDirSaveFs(out DirectorySaveDataFileSystem saveFs, baseFs, true, true, true).ThrowIfFailure();
.ThrowIfFailure();
Assert.Result(ResultFs.PathNotFound, saveFs.GetEntryType(out _, "/file1")); Assert.Result(ResultFs.PathNotFound, saveFs.GetEntryType(out _, "/file1"));
Assert.Success(saveFs.GetEntryType(out _, "/file2")); Assert.Success(saveFs.GetEntryType(out _, "/file2"));
@ -168,8 +191,7 @@ namespace LibHac.Tests.Fs
// Set the existing files before initializing the save FS // Set the existing files before initializing the save FS
baseFs.CreateFile("/1/file2", 0, CreateFileOptions.None).ThrowIfFailure(); baseFs.CreateFile("/1/file2", 0, CreateFileOptions.None).ThrowIfFailure();
DirectorySaveDataFileSystem.CreateNew(out DirectorySaveDataFileSystem saveFs, baseFs, true, true, true) CreateDirSaveFs(out DirectorySaveDataFileSystem saveFs, baseFs, true, true, true).ThrowIfFailure();
.ThrowIfFailure();
Assert.Result(ResultFs.PathNotFound, saveFs.GetEntryType(out _, "/file1")); Assert.Result(ResultFs.PathNotFound, saveFs.GetEntryType(out _, "/file1"));
Assert.Success(saveFs.GetEntryType(out _, "/file2")); Assert.Success(saveFs.GetEntryType(out _, "/file2"));
@ -202,8 +224,7 @@ namespace LibHac.Tests.Fs
{ {
var baseFs = new InMemoryFileSystem(); var baseFs = new InMemoryFileSystem();
DirectorySaveDataFileSystem.CreateNew(out DirectorySaveDataFileSystem saveFs, baseFs, true, true, true) CreateDirSaveFs(out DirectorySaveDataFileSystem saveFs, baseFs, true, true, true).ThrowIfFailure();
.ThrowIfFailure();
var originalExtraData = new SaveDataExtraData(); var originalExtraData = new SaveDataExtraData();
originalExtraData.DataSize = 0x12345; originalExtraData.DataSize = 0x12345;
@ -212,7 +233,7 @@ namespace LibHac.Tests.Fs
Assert.Success(saveFs.CommitExtraData(false)); Assert.Success(saveFs.CommitExtraData(false));
saveFs.Dispose(); saveFs.Dispose();
DirectorySaveDataFileSystem.CreateNew(out saveFs, baseFs, true, true, true).ThrowIfFailure(); CreateDirSaveFs(out saveFs, baseFs, true, true, true).ThrowIfFailure();
Assert.Success(saveFs.ReadExtraData(out SaveDataExtraData extraData)); Assert.Success(saveFs.ReadExtraData(out SaveDataExtraData extraData));
Assert.Equal(originalExtraData, extraData); Assert.Equal(originalExtraData, extraData);
@ -223,8 +244,7 @@ namespace LibHac.Tests.Fs
{ {
var baseFs = new InMemoryFileSystem(); var baseFs = new InMemoryFileSystem();
DirectorySaveDataFileSystem.CreateNew(out DirectorySaveDataFileSystem saveFs, baseFs, true, true, true) CreateDirSaveFs(out DirectorySaveDataFileSystem saveFs, baseFs, true, true, true).ThrowIfFailure();
.ThrowIfFailure();
var originalExtraData = new SaveDataExtraData(); var originalExtraData = new SaveDataExtraData();
originalExtraData.DataSize = 0x12345; originalExtraData.DataSize = 0x12345;
@ -233,7 +253,7 @@ namespace LibHac.Tests.Fs
saveFs.CommitExtraData(false).ThrowIfFailure(); saveFs.CommitExtraData(false).ThrowIfFailure();
saveFs.Dispose(); saveFs.Dispose();
DirectorySaveDataFileSystem.CreateNew(out saveFs, baseFs, true, true, true).ThrowIfFailure(); CreateDirSaveFs(out saveFs, baseFs, true, true, true).ThrowIfFailure();
var newExtraData = new SaveDataExtraData(); var newExtraData = new SaveDataExtraData();
newExtraData.DataSize = 0x67890; newExtraData.DataSize = 0x67890;
@ -251,8 +271,7 @@ namespace LibHac.Tests.Fs
{ {
var baseFs = new InMemoryFileSystem(); var baseFs = new InMemoryFileSystem();
DirectorySaveDataFileSystem.CreateNew(out DirectorySaveDataFileSystem saveFs, baseFs, true, true, true) CreateDirSaveFs(out DirectorySaveDataFileSystem saveFs, baseFs, true, true, true).ThrowIfFailure();
.ThrowIfFailure();
// Write extra data and close with committing // Write extra data and close with committing
var originalExtraData = new SaveDataExtraData(); var originalExtraData = new SaveDataExtraData();
@ -262,7 +281,7 @@ namespace LibHac.Tests.Fs
saveFs.CommitExtraData(false).ThrowIfFailure(); saveFs.CommitExtraData(false).ThrowIfFailure();
saveFs.Dispose(); saveFs.Dispose();
DirectorySaveDataFileSystem.CreateNew(out saveFs, baseFs, true, true, true).ThrowIfFailure(); CreateDirSaveFs(out saveFs, baseFs, true, true, true).ThrowIfFailure();
// Write a new extra data and close without committing // Write a new extra data and close without committing
var newExtraData = new SaveDataExtraData(); var newExtraData = new SaveDataExtraData();
@ -272,7 +291,7 @@ namespace LibHac.Tests.Fs
saveFs.Dispose(); saveFs.Dispose();
// Read extra data should match the first one // Read extra data should match the first one
DirectorySaveDataFileSystem.CreateNew(out saveFs, baseFs, true, true, true).ThrowIfFailure(); CreateDirSaveFs(out saveFs, baseFs, true, true, true).ThrowIfFailure();
Assert.Success(saveFs.ReadExtraData(out SaveDataExtraData extraData)); Assert.Success(saveFs.ReadExtraData(out SaveDataExtraData extraData));
Assert.Equal(originalExtraData, extraData); Assert.Equal(originalExtraData, extraData);
@ -286,8 +305,7 @@ namespace LibHac.Tests.Fs
CreateExtraDataForTest(baseFs, "/ExtraData_", 0x12345).ThrowIfFailure(); CreateExtraDataForTest(baseFs, "/ExtraData_", 0x12345).ThrowIfFailure();
CreateExtraDataForTest(baseFs, "/ExtraData1", 0x67890).ThrowIfFailure(); CreateExtraDataForTest(baseFs, "/ExtraData1", 0x67890).ThrowIfFailure();
DirectorySaveDataFileSystem.CreateNew(out DirectorySaveDataFileSystem saveFs, baseFs, true, true, true) CreateDirSaveFs(out DirectorySaveDataFileSystem saveFs, baseFs, true, true, true).ThrowIfFailure();
.ThrowIfFailure();
saveFs.ReadExtraData(out SaveDataExtraData extraData).ThrowIfFailure(); saveFs.ReadExtraData(out SaveDataExtraData extraData).ThrowIfFailure();
@ -302,8 +320,8 @@ namespace LibHac.Tests.Fs
var timeStampGetter = new TimeStampGetter(); var timeStampGetter = new TimeStampGetter();
var baseFs = new InMemoryFileSystem(); var baseFs = new InMemoryFileSystem();
DirectorySaveDataFileSystem.CreateNew(out DirectorySaveDataFileSystem saveFs, baseFs, timeStampGetter, CreateDirSaveFs(out DirectorySaveDataFileSystem saveFs, baseFs, timeStampGetter,
randomGeneratorFunc, true, true, true, null).ThrowIfFailure(); randomGeneratorFunc, true, true, true, null).ThrowIfFailure();
saveFs.CommitExtraData(true).ThrowIfFailure(); saveFs.CommitExtraData(true).ThrowIfFailure();
saveFs.ReadExtraData(out SaveDataExtraData extraData).ThrowIfFailure(); saveFs.ReadExtraData(out SaveDataExtraData extraData).ThrowIfFailure();
@ -330,8 +348,8 @@ namespace LibHac.Tests.Fs
var timeStampGetter = new TimeStampGetter(); var timeStampGetter = new TimeStampGetter();
var baseFs = new InMemoryFileSystem(); var baseFs = new InMemoryFileSystem();
DirectorySaveDataFileSystem.CreateNew(out DirectorySaveDataFileSystem saveFs, baseFs, timeStampGetter, CreateDirSaveFs(out DirectorySaveDataFileSystem saveFs, baseFs, timeStampGetter,
randomGeneratorFunc, true, true, true, null).ThrowIfFailure(); randomGeneratorFunc, true, true, true, null).ThrowIfFailure();
saveFs.CommitExtraData(true).ThrowIfFailure(); saveFs.CommitExtraData(true).ThrowIfFailure();
saveFs.ReadExtraData(out SaveDataExtraData extraData).ThrowIfFailure(); saveFs.ReadExtraData(out SaveDataExtraData extraData).ThrowIfFailure();
@ -358,8 +376,8 @@ namespace LibHac.Tests.Fs
var timeStampGetter = new TimeStampGetter(); var timeStampGetter = new TimeStampGetter();
var baseFs = new InMemoryFileSystem(); var baseFs = new InMemoryFileSystem();
DirectorySaveDataFileSystem.CreateNew(out DirectorySaveDataFileSystem saveFs, baseFs, timeStampGetter, CreateDirSaveFs(out DirectorySaveDataFileSystem saveFs, baseFs, timeStampGetter,
randomGeneratorFunc, true, true, true, null).ThrowIfFailure(); randomGeneratorFunc, true, true, true, null).ThrowIfFailure();
saveFs.CommitExtraData(true).ThrowIfFailure(); saveFs.CommitExtraData(true).ThrowIfFailure();
saveFs.ReadExtraData(out SaveDataExtraData extraData).ThrowIfFailure(); saveFs.ReadExtraData(out SaveDataExtraData extraData).ThrowIfFailure();
@ -421,12 +439,13 @@ namespace LibHac.Tests.Fs
var extraData = new SaveDataExtraData(); var extraData = new SaveDataExtraData();
extraData.DataSize = saveDataSize; extraData.DataSize = saveDataSize;
rc = fileSystem.OpenFile(out IFile file, path, OpenMode.ReadWrite); using var file = new UniqueRef<IFile>();
rc = fileSystem.OpenFile(ref file.Ref(), path, OpenMode.ReadWrite);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
using (file) using (file)
{ {
rc = file.Write(0, SpanHelpers.AsByteSpan(ref extraData), WriteOption.Flush); rc = file.Get.Write(0, SpanHelpers.AsByteSpan(ref extraData), WriteOption.Flush);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
} }

View File

@ -140,26 +140,22 @@ namespace LibHac.Tests.Fs
return fs.GetTotalSpaceSize(out totalSpace, in pathNormalized); return fs.GetTotalSpaceSize(out totalSpace, in pathNormalized);
} }
public static Result OpenFile(this IFileSystem fs, out IFile file, string path, OpenMode mode) public static Result OpenFile(this IFileSystem fs, ref UniqueRef<IFile> file, string path, OpenMode mode)
{ {
UnsafeHelpers.SkipParamInit(out file);
using var pathNormalized = new Path(); using var pathNormalized = new Path();
Result rc = SetUpPath(ref pathNormalized.Ref(), path); Result rc = SetUpPath(ref pathNormalized.Ref(), path);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
return fs.OpenFile(out file, in pathNormalized, mode); return fs.OpenFile(ref file, in pathNormalized, mode);
} }
public static Result OpenDirectory(this IFileSystem fs, out IDirectory directory, string path, OpenDirectoryMode mode) public static Result OpenDirectory(this IFileSystem fs, ref UniqueRef<IDirectory> directory, string path, OpenDirectoryMode mode)
{ {
UnsafeHelpers.SkipParamInit(out directory);
using var pathNormalized = new Path(); using var pathNormalized = new Path();
Result rc = SetUpPath(ref pathNormalized.Ref(), path); Result rc = SetUpPath(ref pathNormalized.Ref(), path);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
return fs.OpenDirectory(out directory, in pathNormalized, mode); return fs.OpenDirectory(ref directory, in pathNormalized, mode);
} }
public static Result GetFileTimeStampRaw(this IFileSystem fs, out FileTimeStampRaw timeStamp, string path) public static Result GetFileTimeStampRaw(this IFileSystem fs, out FileTimeStampRaw timeStamp, string path)

View File

@ -1,4 +1,5 @@
using LibHac.Fs; using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
using Xunit; using Xunit;
@ -23,14 +24,17 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
fs.CreateFile("/dir1/file", data1.Length, CreateFileOptions.None).ThrowIfFailure(); fs.CreateFile("/dir1/file", data1.Length, CreateFileOptions.None).ThrowIfFailure();
fs.CreateFile("/dir2/file", data2.Length, CreateFileOptions.None).ThrowIfFailure(); fs.CreateFile("/dir2/file", data2.Length, CreateFileOptions.None).ThrowIfFailure();
fs.OpenFile(out IFile file1, "/dir1/file", OpenMode.Write).ThrowIfFailure(); using var file1 = new UniqueRef<IFile>();
fs.OpenFile(out IFile file2, "/dir2/file", OpenMode.Write).ThrowIfFailure(); using var file2 = new UniqueRef<IFile>();
file1.Write(0, data1, WriteOption.Flush).ThrowIfFailure(); fs.OpenFile(ref file1.Ref(), "/dir1/file", OpenMode.Write).ThrowIfFailure();
file2.Write(0, data2, WriteOption.Flush).ThrowIfFailure(); fs.OpenFile(ref file2.Ref(), "/dir2/file", OpenMode.Write).ThrowIfFailure();
file1.Dispose(); file1.Get.Write(0, data1, WriteOption.Flush).ThrowIfFailure();
file2.Dispose(); file2.Get.Write(0, data2, WriteOption.Flush).ThrowIfFailure();
file1.Reset();
file2.Reset();
fs.Commit().ThrowIfFailure(); fs.Commit().ThrowIfFailure();
fs.Dispose(); fs.Dispose();
@ -41,23 +45,18 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
byte[] readData1 = new byte[data1.Length]; byte[] readData1 = new byte[data1.Length];
byte[] readData2 = new byte[data2.Length]; byte[] readData2 = new byte[data2.Length];
Assert.Success(fs.OpenFile(out file1, "/dir1/file", OpenMode.Read)); Assert.Success(fs.OpenFile(ref file1.Ref(), "/dir1/file", OpenMode.Read));
using (file1) Assert.Success(file1.Get.Read(out long bytesReadFile1, 0, readData1, ReadOption.None));
{ file1.Reset();
Assert.Success(file1.Read(out long bytesRead, 0, readData1, ReadOption.None)); Assert.Equal(data1.Length, bytesReadFile1);
Assert.Equal(data1.Length, bytesRead);
}
Assert.Equal(data1, readData1); Assert.Equal(data1, readData1);
Assert.Success(fs.OpenFile(out file2, "/dir2/file", OpenMode.Read)); Assert.Success(fs.OpenFile(ref file2.Ref(), "/dir2/file", OpenMode.Read));
using (file2) Assert.Success(file2.Get.Read(out long bytesReadFile2, 0, readData2, ReadOption.None));
{ Assert.Equal(data2.Length, bytesReadFile2);
Assert.Success(file2.Read(out long bytesRead, 0, readData2, ReadOption.None));
Assert.Equal(data2.Length, bytesRead);
}
Assert.Equal(data2, readData2); Assert.Equal(data2, readData2);
} }
@ -109,9 +108,10 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
fs.CreateDirectory("/dir").ThrowIfFailure(); fs.CreateDirectory("/dir").ThrowIfFailure();
fs.CreateFile("/dir/file", data1.Length, CreateFileOptions.None).ThrowIfFailure(); fs.CreateFile("/dir/file", data1.Length, CreateFileOptions.None).ThrowIfFailure();
fs.OpenFile(out IFile file, "/dir/file", OpenMode.Write).ThrowIfFailure(); using var file = new UniqueRef<IFile>();
file.Write(0, data1, WriteOption.Flush).ThrowIfFailure(); fs.OpenFile(ref file.Ref(), "/dir/file", OpenMode.Write).ThrowIfFailure();
file.Dispose(); file.Get.Write(0, data1, WriteOption.Flush).ThrowIfFailure();
file.Reset();
// Commit and reopen the file system // Commit and reopen the file system
fs.Commit().ThrowIfFailure(); fs.Commit().ThrowIfFailure();
@ -120,22 +120,19 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
fs = fsCreator.Create(); fs = fsCreator.Create();
// Make changes to the file // Make changes to the file
fs.OpenFile(out file, "/dir/file", OpenMode.Write).ThrowIfFailure(); fs.OpenFile(ref file.Ref(), "/dir/file", OpenMode.Write).ThrowIfFailure();
file.Write(0, data2, WriteOption.Flush).ThrowIfFailure(); file.Get.Write(0, data2, WriteOption.Flush).ThrowIfFailure();
file.Dispose(); file.Reset();
Assert.Success(fs.Rollback()); Assert.Success(fs.Rollback());
// The file should contain the original data after the rollback // The file should contain the original data after the rollback
byte[] readData = new byte[data1.Length]; byte[] readData = new byte[data1.Length];
Assert.Success(fs.OpenFile(out file, "/dir/file", OpenMode.Read)); Assert.Success(fs.OpenFile(ref file.Ref(), "/dir/file", OpenMode.Read));
using (file) Assert.Success(file.Get.Read(out long bytesRead, 0, readData, ReadOption.None));
{ Assert.Equal(data1.Length, bytesRead);
Assert.Success(file.Read(out long bytesRead, 0, readData, ReadOption.None));
Assert.Equal(data1.Length, bytesRead);
}
Assert.Equal(data1, readData); Assert.Equal(data1, readData);
} }

View File

@ -1,4 +1,5 @@
using LibHac.Fs; using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
using Xunit; using Xunit;
@ -72,9 +73,10 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
fs.CreateFile("/file", expectedSize, CreateFileOptions.None); fs.CreateFile("/file", expectedSize, CreateFileOptions.None);
fs.OpenFile(out IFile file, "/file", OpenMode.Read); using var file = new UniqueRef<IFile>();
fs.OpenFile(ref file.Ref(), "/file", OpenMode.Read);
Assert.Success(file.GetSize(out long fileSize)); Assert.Success(file.Get.GetSize(out long fileSize));
Assert.Equal(expectedSize, fileSize); Assert.Equal(expectedSize, fileSize);
} }

View File

@ -15,9 +15,10 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
IFileSystem fs = CreateFileSystem(); IFileSystem fs = CreateFileSystem();
Span<DirectoryEntry> entries = stackalloc DirectoryEntry[1]; Span<DirectoryEntry> entries = stackalloc DirectoryEntry[1];
Assert.Success(fs.OpenDirectory(out IDirectory directory, "/", OpenDirectoryMode.All)); using var directory = new UniqueRef<IDirectory>();
Assert.Success(fs.OpenDirectory(ref directory.Ref(), "/", OpenDirectoryMode.All));
Assert.Success(directory.Read(out long entriesRead, entries)); Assert.Success(directory.Get.Read(out long entriesRead, entries));
Assert.Equal(0, entriesRead); Assert.Equal(0, entriesRead);
} }
@ -26,9 +27,10 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
{ {
IFileSystem fs = CreateFileSystem(); IFileSystem fs = CreateFileSystem();
Assert.Success(fs.OpenDirectory(out IDirectory directory, "/", OpenDirectoryMode.All)); using var directory = new UniqueRef<IDirectory>();
Assert.Success(fs.OpenDirectory(ref directory.Ref(), "/", OpenDirectoryMode.All));
Assert.Success(directory.GetEntryCount(out long entryCount)); Assert.Success(directory.Get.GetEntryCount(out long entryCount));
Assert.Equal(0, entryCount); Assert.Equal(0, entryCount);
} }
@ -42,17 +44,18 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
fs.CreateFile("/dir/file1", 0, CreateFileOptions.None); fs.CreateFile("/dir/file1", 0, CreateFileOptions.None);
fs.CreateFile("/dir/file2", 0, CreateFileOptions.None); fs.CreateFile("/dir/file2", 0, CreateFileOptions.None);
Assert.Success(fs.OpenDirectory(out IDirectory dir, "/dir", OpenDirectoryMode.All)); using var dir = new UniqueRef<IDirectory>();
Assert.Success(fs.OpenDirectory(ref dir.Ref(), "/dir", OpenDirectoryMode.All));
var entry1 = new DirectoryEntry(); var entry1 = new DirectoryEntry();
var entry2 = new DirectoryEntry(); var entry2 = new DirectoryEntry();
var entry3 = new DirectoryEntry(); var entry3 = new DirectoryEntry();
var entry4 = new DirectoryEntry(); var entry4 = new DirectoryEntry();
Assert.Success(dir.Read(out long entriesRead1, SpanHelpers.AsSpan(ref entry1))); Assert.Success(dir.Get.Read(out long entriesRead1, SpanHelpers.AsSpan(ref entry1)));
Assert.Success(dir.Read(out long entriesRead2, SpanHelpers.AsSpan(ref entry2))); Assert.Success(dir.Get.Read(out long entriesRead2, SpanHelpers.AsSpan(ref entry2)));
Assert.Success(dir.Read(out long entriesRead3, SpanHelpers.AsSpan(ref entry3))); Assert.Success(dir.Get.Read(out long entriesRead3, SpanHelpers.AsSpan(ref entry3)));
Assert.Success(dir.Read(out long entriesRead4, SpanHelpers.AsSpan(ref entry4))); Assert.Success(dir.Get.Read(out long entriesRead4, SpanHelpers.AsSpan(ref entry4)));
Assert.Equal(1, entriesRead1); Assert.Equal(1, entriesRead1);
Assert.Equal(1, entriesRead2); Assert.Equal(1, entriesRead2);

View File

@ -1,4 +1,5 @@
using System; using System;
using LibHac.Common;
using LibHac.Fs; using LibHac.Fs;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
using Xunit; using Xunit;
@ -15,12 +16,11 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
fs.CreateFile("/file", 100, CreateFileOptions.None); fs.CreateFile("/file", 100, CreateFileOptions.None);
byte[] buffer = new byte[20]; byte[] buffer = new byte[20];
fs.OpenFile(out IFile file, "/file", OpenMode.Read); using var file = new UniqueRef<IFile>();
using (file) fs.OpenFile(ref file.Ref(), "/file", OpenMode.Read);
{
Assert.Success(file.Read(out long bytesRead, 50, buffer, ReadOption.None)); Assert.Success(file.Get.Read(out long bytesRead, 50, buffer, ReadOption.None));
Assert.Equal(20, bytesRead); Assert.Equal(20, bytesRead);
}
} }
[Fact] [Fact]
@ -31,12 +31,11 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
fs.CreateFile("/file", 0, CreateFileOptions.None); fs.CreateFile("/file", 0, CreateFileOptions.None);
byte[] buffer = new byte[10]; byte[] buffer = new byte[10];
fs.OpenFile(out IFile file, "/file", OpenMode.Read); using var file = new UniqueRef<IFile>();
using (file) fs.OpenFile(ref file.Ref(), "/file", OpenMode.Read);
{
Result rc = file.Read(out _, 1, buffer, ReadOption.None); Result rc = file.Get.Read(out _, 1, buffer, ReadOption.None);
Assert.Result(ResultFs.OutOfRange, rc); Assert.Result(ResultFs.OutOfRange, rc);
}
} }
[Fact] [Fact]
@ -47,12 +46,11 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
fs.CreateFile("/file", 0, CreateFileOptions.None); fs.CreateFile("/file", 0, CreateFileOptions.None);
byte[] buffer = new byte[10]; byte[] buffer = new byte[10];
fs.OpenFile(out IFile file, "/file", OpenMode.Write); using var file = new UniqueRef<IFile>();
using (file) fs.OpenFile(ref file.Ref(), "/file", OpenMode.Write);
{
Result rc = file.Read(out _, 0, buffer, ReadOption.None); Result rc = file.Get.Read(out _, 0, buffer, ReadOption.None);
Assert.Result(ResultFs.ReadUnpermitted, rc); Assert.Result(ResultFs.ReadUnpermitted, rc);
}
} }
[Fact] [Fact]
@ -63,12 +61,11 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
fs.CreateFile("/file", 0, CreateFileOptions.None); fs.CreateFile("/file", 0, CreateFileOptions.None);
byte[] buffer = new byte[10]; byte[] buffer = new byte[10];
fs.OpenFile(out IFile file, "/file", OpenMode.Write); using var file = new UniqueRef<IFile>();
using (file) fs.OpenFile(ref file.Ref(), "/file", OpenMode.Write);
{
Result rc = file.Read(out _, -5, buffer, ReadOption.None); Result rc = file.Get.Read(out _, -5, buffer, ReadOption.None);
Assert.Result(ResultFs.OutOfRange, rc); Assert.Result(ResultFs.OutOfRange, rc);
}
} }
[Fact] [Fact]
@ -79,12 +76,11 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
fs.CreateFile("/file", 0, CreateFileOptions.None); fs.CreateFile("/file", 0, CreateFileOptions.None);
byte[] buffer = new byte[10]; byte[] buffer = new byte[10];
fs.OpenFile(out IFile file, "/file", OpenMode.Write); using var file = new UniqueRef<IFile>();
using (file) fs.OpenFile(ref file.Ref(), "/file", OpenMode.Write);
{
Result rc = file.Read(out _, long.MaxValue - 5, buffer, ReadOption.None); Result rc = file.Get.Read(out _, long.MaxValue - 5, buffer, ReadOption.None);
Assert.Result(ResultFs.OutOfRange, rc); Assert.Result(ResultFs.OutOfRange, rc);
}
} }
[Fact] [Fact]
@ -95,12 +91,11 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
fs.CreateFile("/file", 100, CreateFileOptions.None); fs.CreateFile("/file", 100, CreateFileOptions.None);
byte[] buffer = new byte[200]; byte[] buffer = new byte[200];
fs.OpenFile(out IFile file, "/file", OpenMode.Read); using var file = new UniqueRef<IFile>();
using (file) fs.OpenFile(ref file.Ref(), "/file", OpenMode.Read);
{
Assert.Success(file.Read(out long bytesRead, 90, buffer, ReadOption.None)); Assert.Success(file.Get.Read(out long bytesRead, 90, buffer, ReadOption.None));
Assert.Equal(10, bytesRead); Assert.Equal(10, bytesRead);
}
} }
[Fact] [Fact]
@ -111,11 +106,10 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
fs.CreateFile("/file", 100, CreateFileOptions.None); fs.CreateFile("/file", 100, CreateFileOptions.None);
// The contents of a created file are undefined, so zero the file // The contents of a created file are undefined, so zero the file
fs.OpenFile(out IFile file, "/file", OpenMode.Write); using var file = new UniqueRef<IFile>();
using (file) fs.OpenFile(ref file.Ref(), "/file", OpenMode.Write);
{ file.Get.Write(0, new byte[100], WriteOption.None);
file.Write(0, new byte[100], WriteOption.None); file.Reset();
}
byte[] bufferExpected = new byte[200]; byte[] bufferExpected = new byte[200];
bufferExpected.AsSpan(10).Fill(0xCC); bufferExpected.AsSpan(10).Fill(0xCC);
@ -123,12 +117,10 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
byte[] buffer = new byte[200]; byte[] buffer = new byte[200];
buffer.AsSpan().Fill(0xCC); buffer.AsSpan().Fill(0xCC);
fs.OpenFile(out file, "/file", OpenMode.Read); fs.OpenFile(ref file.Ref(), "/file", OpenMode.Read);
using (file)
{ Assert.Success(file.Get.Read(out _, 90, buffer, ReadOption.None));
Assert.Success(file.Read(out _, 90, buffer, ReadOption.None)); Assert.Equal(bufferExpected, buffer);
Assert.Equal(bufferExpected, buffer);
}
} }
} }
} }

View File

@ -1,4 +1,5 @@
using LibHac.Fs; using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
using Xunit; using Xunit;
@ -12,13 +13,13 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
IFileSystem fs = CreateFileSystem(); IFileSystem fs = CreateFileSystem();
fs.CreateFile("/file", 0, CreateFileOptions.None); fs.CreateFile("/file", 0, CreateFileOptions.None);
fs.OpenFile(out IFile file, "/file", OpenMode.All); using var file = new UniqueRef<IFile>();
Result rc = file.SetSize(54321); fs.OpenFile(ref file.Ref(), "/file", OpenMode.All);
file.Dispose(); Result rc = file.Get.SetSize(54321);
file.Reset();
fs.OpenFile(out file, "/file", OpenMode.All); fs.OpenFile(ref file.Ref(), "/file", OpenMode.All);
file.GetSize(out long fileSize); file.Get.GetSize(out long fileSize);
file.Dispose();
Assert.Success(rc); Assert.Success(rc);
Assert.Equal(54321, fileSize); Assert.Equal(54321, fileSize);

View File

@ -1,4 +1,5 @@
using System; using System;
using LibHac.Common;
using LibHac.Fs; using LibHac.Fs;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
using Xunit; using Xunit;
@ -16,18 +17,16 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
fs.CreateFile("/file", data.Length, CreateFileOptions.None); fs.CreateFile("/file", data.Length, CreateFileOptions.None);
fs.OpenFile(out IFile file, "/file", OpenMode.Write); using var file = new UniqueRef<IFile>();
file.Write(0, data, WriteOption.None); fs.OpenFile(ref file.Ref(), "/file", OpenMode.Write);
file.Dispose(); file.Get.Write(0, data, WriteOption.None);
file.Reset();
byte[] readData = new byte[data.Length]; byte[] readData = new byte[data.Length];
fs.OpenFile(ref file.Ref(), "/file", OpenMode.Read);
fs.OpenFile(out file, "/file", OpenMode.Read); Assert.Success(file.Get.Read(out long bytesRead, 0, readData, ReadOption.None));
using (file) Assert.Equal(data.Length, bytesRead);
{
Assert.Success(file.Read(out long bytesRead, 0, readData, ReadOption.None));
Assert.Equal(data.Length, bytesRead);
}
Assert.Equal(data, readData); Assert.Equal(data, readData);
} }
@ -40,12 +39,11 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
fs.CreateFile("/file", 10, CreateFileOptions.None); fs.CreateFile("/file", 10, CreateFileOptions.None);
byte[] buffer = new byte[10]; byte[] buffer = new byte[10];
fs.OpenFile(out IFile file, "/file", OpenMode.Write); using var file = new UniqueRef<IFile>();
using (file) fs.OpenFile(ref file.Ref(), "/file", OpenMode.Write);
{
Result rc = file.Write(5, buffer, WriteOption.None); Result rc = file.Get.Write(5, buffer, WriteOption.None);
Assert.Result(ResultFs.FileExtensionWithoutOpenModeAllowAppend, rc); Assert.Result(ResultFs.FileExtensionWithoutOpenModeAllowAppend, rc);
}
} }
[Fact] [Fact]
@ -56,12 +54,11 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
fs.CreateFile("/file", 10, CreateFileOptions.None); fs.CreateFile("/file", 10, CreateFileOptions.None);
byte[] buffer = new byte[10]; byte[] buffer = new byte[10];
fs.OpenFile(out IFile file, "/file", OpenMode.Read); using var file = new UniqueRef<IFile>();
using (file) fs.OpenFile(ref file.Ref(), "/file", OpenMode.Read);
{
Result rc = file.Write(5, buffer, WriteOption.None); Result rc = file.Get.Write(5, buffer, WriteOption.None);
Assert.Result(ResultFs.WriteUnpermitted, rc); Assert.Result(ResultFs.WriteUnpermitted, rc);
}
} }
[Fact] [Fact]
@ -72,12 +69,11 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
fs.CreateFile("/file", 10, CreateFileOptions.None); fs.CreateFile("/file", 10, CreateFileOptions.None);
byte[] buffer = new byte[10]; byte[] buffer = new byte[10];
fs.OpenFile(out IFile file, "/file", OpenMode.Read); using var file = new UniqueRef<IFile>();
using (file) fs.OpenFile(ref file.Ref(), "/file", OpenMode.Read);
{
Result rc = file.Write(-5, buffer, WriteOption.None); Result rc = file.Get.Write(-5, buffer, WriteOption.None);
Assert.Result(ResultFs.OutOfRange, rc); Assert.Result(ResultFs.OutOfRange, rc);
}
} }
[Fact] [Fact]
@ -88,12 +84,11 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
fs.CreateFile("/file", 10, CreateFileOptions.None); fs.CreateFile("/file", 10, CreateFileOptions.None);
byte[] buffer = new byte[10]; byte[] buffer = new byte[10];
fs.OpenFile(out IFile file, "/file", OpenMode.Read); using var file = new UniqueRef<IFile>();
using (file) fs.OpenFile(ref file.Ref(), "/file", OpenMode.Read);
{
Result rc = file.Write(long.MaxValue - 5, buffer, WriteOption.None); Result rc = file.Get.Write(long.MaxValue - 5, buffer, WriteOption.None);
Assert.Result(ResultFs.OutOfRange, rc); Assert.Result(ResultFs.OutOfRange, rc);
}
} }
[Fact] [Fact]
@ -104,14 +99,13 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
fs.CreateFile("/file", 10, CreateFileOptions.None); fs.CreateFile("/file", 10, CreateFileOptions.None);
byte[] buffer = new byte[10]; byte[] buffer = new byte[10];
fs.OpenFile(out IFile file, "/file", OpenMode.All); using var file = new UniqueRef<IFile>();
using (file) fs.OpenFile(ref file.Ref(), "/file", OpenMode.All);
{
Assert.Success(file.Write(5, buffer, WriteOption.None));
file.GetSize(out long newSize); Assert.Success(file.Get.Write(5, buffer, WriteOption.None));
Assert.Equal(15, newSize);
} file.Get.GetSize(out long newSize);
Assert.Equal(15, newSize);
} }
[Fact] [Fact]
@ -122,14 +116,13 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
fs.CreateFile("/file", 10, CreateFileOptions.None); fs.CreateFile("/file", 10, CreateFileOptions.None);
byte[] buffer = new byte[10]; byte[] buffer = new byte[10];
fs.OpenFile(out IFile file, "/file", OpenMode.All); using var file = new UniqueRef<IFile>();
using (file) fs.OpenFile(ref file.Ref(), "/file", OpenMode.All);
{
Assert.Success(file.Write(15, buffer, WriteOption.None));
file.GetSize(out long newSize); Assert.Success(file.Get.Write(15, buffer, WriteOption.None));
Assert.Equal(25, newSize);
} file.Get.GetSize(out long newSize);
Assert.Equal(25, newSize);
} }
[Fact] [Fact]
@ -145,23 +138,21 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
byte[] writeBuffer = new byte[10]; byte[] writeBuffer = new byte[10];
writeBuffer.AsSpan().Fill(0xCC); writeBuffer.AsSpan().Fill(0xCC);
fs.OpenFile(out IFile file, "/file", OpenMode.All); using var file = new UniqueRef<IFile>();
using (file) fs.OpenFile(ref file.Ref(), "/file", OpenMode.All);
{
Assert.Success(file.Write(15, writeBuffer, WriteOption.None));
// Unwritten portions of new files are undefined, so write to the other portions Assert.Success(file.Get.Write(15, writeBuffer, WriteOption.None));
file.Write(0, new byte[15], WriteOption.None);
} // Unwritten portions of new files are undefined, so write to the other portions
file.Get.Write(0, new byte[15], WriteOption.None);
file.Reset();
byte[] readBuffer = new byte[25]; byte[] readBuffer = new byte[25];
fs.OpenFile(out file, "/file", OpenMode.Read); fs.OpenFile(ref file.Ref(), "/file", OpenMode.Read);
using (file)
{ file.Get.Read(out _, 0, readBuffer, ReadOption.None);
file.Read(out _, 0, readBuffer, ReadOption.None); Assert.Equal(bufferExpected, readBuffer);
Assert.Equal(bufferExpected, readBuffer);
}
} }
} }
} }

View File

@ -1,4 +1,5 @@
using LibHac.Fs; using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
using Xunit; using Xunit;
@ -13,7 +14,8 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
fs.CreateFile("/file", 0, CreateFileOptions.None); fs.CreateFile("/file", 0, CreateFileOptions.None);
Result rc = fs.OpenDirectory(out _, "/file", OpenDirectoryMode.All); using var directory = new UniqueRef<IDirectory>();
Result rc = fs.OpenDirectory(ref directory.Ref(), "/file", OpenDirectoryMode.All);
Assert.Result(ResultFs.PathNotFound, rc); Assert.Result(ResultFs.PathNotFound, rc);
} }

View File

@ -1,4 +1,5 @@
using LibHac.Fs; using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
using Xunit; using Xunit;
@ -13,7 +14,8 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
fs.CreateDirectory("/dir"); fs.CreateDirectory("/dir");
Result rc = fs.OpenFile(out _, "/dir", OpenMode.All); using var file = new UniqueRef<IFile>();
Result rc = fs.OpenFile(ref file.Ref(), "/dir", OpenMode.All);
Assert.Result(ResultFs.PathNotFound, rc); Assert.Result(ResultFs.PathNotFound, rc);
} }

View File

@ -1,4 +1,5 @@
using LibHac.Fs; using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
using Xunit; using Xunit;
@ -74,18 +75,17 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
fs.RenameFile("/file1", "/file2"); fs.RenameFile("/file1", "/file2");
Assert.Success(fs.OpenFile(out IFile file1, "/file1", OpenMode.Read)); using var file1 = new UniqueRef<IFile>();
Assert.Success(fs.OpenFile(out IFile file2, "/file2", OpenMode.Read)); using var file2 = new UniqueRef<IFile>();
using (file1) Assert.Success(fs.OpenFile(ref file1.Ref(), "/file1", OpenMode.Read));
using (file2) Assert.Success(fs.OpenFile(ref file2.Ref(), "/file2", OpenMode.Read));
{
Assert.Success(file1.GetSize(out long file1Size));
Assert.Success(file2.GetSize(out long file2Size));
Assert.Equal(54321, file1Size); Assert.Success(file1.Get.GetSize(out long file1Size));
Assert.Equal(12345, file2Size); Assert.Success(file2.Get.GetSize(out long file2Size));
}
Assert.Equal(54321, file1Size);
Assert.Equal(12345, file2Size);
} }
[Fact] [Fact]
@ -97,17 +97,17 @@ namespace LibHac.Tests.Fs.IFileSystemTestBase
fs.CreateFile("/file", data.Length, CreateFileOptions.None); fs.CreateFile("/file", data.Length, CreateFileOptions.None);
fs.OpenFile(out IFile file, "/file", OpenMode.Write); using var file = new UniqueRef<IFile>();
file.Write(0, data, WriteOption.None); fs.OpenFile(ref file.Ref(), "/file", OpenMode.Write);
file.Dispose(); file.Get.Write(0, data, WriteOption.None);
file.Reset();
fs.RenameFile("/file", "/renamed"); fs.RenameFile("/file", "/renamed");
byte[] readData = new byte[data.Length]; byte[] readData = new byte[data.Length];
fs.OpenFile(out file, "/renamed", OpenMode.Read); fs.OpenFile(ref file.Ref(), "/renamed", OpenMode.Read);
Result rc = file.Read(out long bytesRead, 0, readData, ReadOption.None); Result rc = file.Get.Read(out long bytesRead, 0, readData, ReadOption.None);
file.Dispose();
Assert.Success(rc); Assert.Success(rc);
Assert.Equal(data.Length, bytesRead); Assert.Equal(data.Length, bytesRead);

View File

@ -58,7 +58,8 @@ namespace LibHac.Tests.Fs
{ {
IFileSystem fs = CreateFileSystem(); IFileSystem fs = CreateFileSystem();
Assert.Result(ResultFs.PathNotFound, fs.OpenFile(out _, "/fakefile", OpenMode.All)); using var file = new UniqueRef<IFile>();
Assert.Result(ResultFs.PathNotFound, fs.OpenFile(ref file.Ref(), "/fakefile", OpenMode.All));
} }
[Fact] [Fact]
@ -66,8 +67,9 @@ namespace LibHac.Tests.Fs
{ {
IFileSystem fs = CreateFileSystem(); IFileSystem fs = CreateFileSystem();
Assert.Success(fs.OpenFile(out IFile file, "/dir/replacedFile", OpenMode.All)); using var file = new UniqueRef<IFile>();
Assert.Success(file.GetSize(out long fileSize)); Assert.Success(fs.OpenFile(ref file.Ref(), "/dir/replacedFile", OpenMode.All));
Assert.Success(file.Get.GetSize(out long fileSize));
Assert.Equal(2, fileSize); Assert.Equal(2, fileSize);
} }
@ -77,8 +79,9 @@ namespace LibHac.Tests.Fs
{ {
IFileSystem fs = CreateFileSystem(); IFileSystem fs = CreateFileSystem();
Assert.Success(fs.OpenFile(out _, "/dir2/lowerFile", OpenMode.All)); using var file = new UniqueRef<IFile>();
Assert.Success(fs.OpenFile(out _, "/dir2/upperFile", OpenMode.All)); Assert.Success(fs.OpenFile(ref file.Ref(), "/dir2/lowerFile", OpenMode.All));
Assert.Success(fs.OpenFile(ref file.Ref(), "/dir2/upperFile", OpenMode.All));
} }
[Fact] [Fact]
@ -86,7 +89,8 @@ namespace LibHac.Tests.Fs
{ {
IFileSystem fs = CreateFileSystem(); IFileSystem fs = CreateFileSystem();
Assert.Result(ResultFs.PathNotFound, fs.OpenDirectory(out _, "/fakedir", OpenDirectoryMode.All)); using var directory = new UniqueRef<IDirectory>();
Assert.Result(ResultFs.PathNotFound, fs.OpenDirectory(ref directory.Ref(), "/fakedir", OpenDirectoryMode.All));
} }
[Fact] [Fact]
@ -94,8 +98,9 @@ namespace LibHac.Tests.Fs
{ {
IFileSystem fs = CreateFileSystem(); IFileSystem fs = CreateFileSystem();
Assert.Success(fs.OpenDirectory(out IDirectory dir, "/lowerDir", OpenDirectoryMode.All)); using var directory = new UniqueRef<IDirectory>();
Assert.Equal(typeof(InMemoryFileSystem), dir.GetType().DeclaringType); Assert.Success(fs.OpenDirectory(ref directory.Ref(), "/lowerDir", OpenDirectoryMode.All));
Assert.Equal(typeof(InMemoryFileSystem), directory.Get.GetType().DeclaringType);
} }
[Fact] [Fact]
@ -103,8 +108,9 @@ namespace LibHac.Tests.Fs
{ {
IFileSystem fs = CreateFileSystem(); IFileSystem fs = CreateFileSystem();
Assert.Success(fs.OpenDirectory(out IDirectory dir, "/dir", OpenDirectoryMode.All)); using var directory = new UniqueRef<IDirectory>();
Assert.Equal(typeof(LayeredFileSystem), dir.GetType().DeclaringType); Assert.Success(fs.OpenDirectory(ref directory.Ref(), "/dir", OpenDirectoryMode.All));
Assert.Equal(typeof(LayeredFileSystem), directory.Get.GetType().DeclaringType);
} }
[Fact] [Fact]
@ -122,9 +128,10 @@ namespace LibHac.Tests.Fs
IFileSystem fs = CreateFileSystem(); IFileSystem fs = CreateFileSystem();
Span<DirectoryEntry> entries = stackalloc DirectoryEntry[4]; Span<DirectoryEntry> entries = stackalloc DirectoryEntry[4];
Assert.Success(fs.OpenDirectory(out IDirectory directory, "/dir3", OpenDirectoryMode.All)); using var directory = new UniqueRef<IDirectory>();
Assert.Success(fs.OpenDirectory(ref directory.Ref(), "/dir3", OpenDirectoryMode.All));
Assert.Success(directory.Read(out long entriesRead, entries)); Assert.Success(directory.Get.Read(out long entriesRead, entries));
Assert.Equal(3, entriesRead); Assert.Equal(3, entriesRead);
} }
@ -134,9 +141,10 @@ namespace LibHac.Tests.Fs
IFileSystem fs = CreateFileSystem(); IFileSystem fs = CreateFileSystem();
var entry = new DirectoryEntry(); var entry = new DirectoryEntry();
Assert.Success(fs.OpenDirectory(out IDirectory directory, "/dir", OpenDirectoryMode.All)); using var directory = new UniqueRef<IDirectory>();
Assert.Success(fs.OpenDirectory(ref directory.Ref(), "/dir", OpenDirectoryMode.All));
Assert.Success(directory.Read(out _, SpanHelpers.AsSpan(ref entry))); Assert.Success(directory.Get.Read(out _, SpanHelpers.AsSpan(ref entry)));
Assert.Equal("replacedFile", StringUtils.Utf8ZToString(entry.Name)); Assert.Equal("replacedFile", StringUtils.Utf8ZToString(entry.Name));
Assert.Equal(2, entry.Size); Assert.Equal(2, entry.Size);
} }
@ -147,9 +155,10 @@ namespace LibHac.Tests.Fs
IFileSystem fs = CreateEmptyFileSystem(); IFileSystem fs = CreateEmptyFileSystem();
var entry = new DirectoryEntry(); var entry = new DirectoryEntry();
Assert.Success(fs.OpenDirectory(out IDirectory directory, "/", OpenDirectoryMode.All)); using var directory = new UniqueRef<IDirectory>();
Assert.Success(fs.OpenDirectory(ref directory.Ref(), "/", OpenDirectoryMode.All));
Assert.Success(directory.Read(out long entriesRead, SpanHelpers.AsSpan(ref entry))); Assert.Success(directory.Get.Read(out long entriesRead, SpanHelpers.AsSpan(ref entry)));
Assert.Equal(0, entriesRead); Assert.Equal(0, entriesRead);
} }
@ -158,9 +167,10 @@ namespace LibHac.Tests.Fs
{ {
IFileSystem fs = CreateFileSystem(); IFileSystem fs = CreateFileSystem();
Assert.Success(fs.OpenDirectory(out IDirectory directory, "/dir3", OpenDirectoryMode.All)); using var directory = new UniqueRef<IDirectory>();
Assert.Success(fs.OpenDirectory(ref directory.Ref(), "/dir3", OpenDirectoryMode.All));
Assert.Success(directory.GetEntryCount(out long entryCount)); Assert.Success(directory.Get.GetEntryCount(out long entryCount));
Assert.Equal(3, entryCount); Assert.Equal(3, entryCount);
} }
@ -170,16 +180,17 @@ namespace LibHac.Tests.Fs
IFileSystem fs = CreateFileSystem(); IFileSystem fs = CreateFileSystem();
var entry = new DirectoryEntry(); var entry = new DirectoryEntry();
Assert.Success(fs.OpenDirectory(out IDirectory directory, "/dir3", OpenDirectoryMode.All)); using var directory = new UniqueRef<IDirectory>();
Assert.Success(fs.OpenDirectory(ref directory.Ref(), "/dir3", OpenDirectoryMode.All));
// Read all entries // Read all entries
long entriesRead; long entriesRead;
do do
{ {
Assert.Success(directory.Read(out entriesRead, SpanHelpers.AsSpan(ref entry))); Assert.Success(directory.Get.Read(out entriesRead, SpanHelpers.AsSpan(ref entry)));
} while (entriesRead != 0); } while (entriesRead != 0);
Assert.Success(directory.GetEntryCount(out long entryCount)); Assert.Success(directory.Get.GetEntryCount(out long entryCount));
Assert.Equal(3, entryCount); Assert.Equal(3, entryCount);
} }
@ -188,9 +199,10 @@ namespace LibHac.Tests.Fs
{ {
IFileSystem fs = CreateEmptyFileSystem(); IFileSystem fs = CreateEmptyFileSystem();
Assert.Success(fs.OpenDirectory(out IDirectory directory, "/", OpenDirectoryMode.All)); using var directory = new UniqueRef<IDirectory>();
Assert.Success(fs.OpenDirectory(ref directory.Ref(), "/", OpenDirectoryMode.All));
Assert.Success(directory.GetEntryCount(out long entryCount)); Assert.Success(directory.Get.GetEntryCount(out long entryCount));
Assert.Equal(0, entryCount); Assert.Equal(0, entryCount);
} }
} }