Add more FS IPC interfaces with adapters

This commit is contained in:
Alex Barney 2020-09-13 22:02:03 -07:00
parent 5dc7c57851
commit 3837ed7eea
46 changed files with 1608 additions and 168 deletions

View File

@ -106,6 +106,7 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
2,3355,,AllocationFailureInSubdirectoryFileSystemA,In Initialize allocating RootPathBuffer
2,3383,,AllocationFailureInAesXtsFileE,In Initialize
2,3394,,AllocationFailureInEncryptedFileSystemCreatorA,In Create allocating AesXtsFileSystem
2,3407,,AllocationFailureInFileSystemInterfaceAdapter, In OpenFile or OpenDirectory
2,3420,,AllocationFailureInNew,
2,3421,,AllocationFailureInCreateShared,
2,3422,,AllocationFailureInMakeUnique,
@ -238,7 +239,7 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
2,6030,6059,InvalidPathForOperation,
2,6031,,DirectoryNotDeletable,
2,6032,,DestinationIsSubPathOfSource,
2,6032,,DirectoryNotRenamable,
2,6033,,PathNotFoundInSaveDataFileTable,
2,6034,,DifferentDestFileSystem,
@ -277,6 +278,7 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
2,6350,,UnsupportedOperationInRoGameCardStorageWrite,
2,6351,,UnsupportedOperationInRoGameCardStorageSetSize,
2,6359,,UnsupportedOperationInConcatFsQueryEntry,
2,6362,,UnsupportedOperationInFileServiceObjectAdapterA,Called OperateRange with an invalid operation ID.
2,6364,,UnsupportedOperationModifyRomFsFileSystem,
2,6365,,UnsupportedOperationInRomFsFileSystem,Called RomFsFileSystem::CommitProvisionally.
2,6366,,UnsupportedOperationRomFsFileSystemGetSpace,

Can't render this file because it has a wrong number of fields in line 220.

View File

@ -1,9 +1,13 @@
namespace LibHac.Fs
using System.Runtime.InteropServices;
namespace LibHac.Fs
{
[StructLayout(LayoutKind.Sequential, Size = 0x20)]
public struct FileTimeStampRaw
{
public long Created;
public long Accessed;
public long Modified;
public bool IsLocalTime;
}
}

View File

@ -1,4 +1,5 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.FsSystem;
@ -167,7 +168,7 @@ namespace LibHac.Fs.Fsa
/// <paramref name="oldPath"/> does not exist or is a file: <see cref="ResultFs.PathNotFound"/>
/// <paramref name="newPath"/>'s parent directory does not exist: <see cref="ResultFs.PathNotFound"/>
/// <paramref name="newPath"/> already exists as either a file or directory: <see cref="ResultFs.PathAlreadyExists"/>
/// Either <paramref name="oldPath"/> or <paramref name="newPath"/> is a subpath of the other: <see cref="ResultFs.DestinationIsSubPathOfSource"/>
/// Either <paramref name="oldPath"/> or <paramref name="newPath"/> is a subpath of the other: <see cref="ResultFs.DirectoryNotRenamable"/>
/// </remarks>
public Result RenameDirectory(U8Span oldPath, U8Span newPath)
{
@ -391,7 +392,7 @@ namespace LibHac.Fs.Fsa
protected virtual Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, U8Span path)
{
timeStamp = default;
Unsafe.SkipInit(out timeStamp);
return ResultFs.NotImplemented.Log();
}

View File

@ -0,0 +1,9 @@
using LibHac.FsSrv.Sf;
namespace LibHac.Fs.Fsa
{
public interface IMultiCommitTarget
{
ReferenceCountedDisposable<IFileSystemSf> GetMultiCommitTarget();
}
}

View File

@ -0,0 +1,42 @@
using System;
using System.Runtime.InteropServices;
using LibHac.Fs.Fsa;
using LibHac.FsSrv.Sf;
namespace LibHac.Fs.Impl
{
/// <summary>
/// An adapter for using an <see cref="IDirectorySf"/> service object as an <see cref="IDirectory"/>. Used
/// when receiving a Horizon IPC directory object so it can be used as an <see cref="IDirectory"/> locally.
/// </summary>
internal class DirectoryServiceObjectAdapter : IDirectory
{
private ReferenceCountedDisposable<IDirectorySf> BaseDirectory { get; }
public DirectoryServiceObjectAdapter(ReferenceCountedDisposable<IDirectorySf> baseDirectory)
{
BaseDirectory = baseDirectory.AddReference();
}
protected override Result DoRead(out long entriesRead, Span<DirectoryEntry> entryBuffer)
{
Span<byte> buffer = MemoryMarshal.Cast<DirectoryEntry, byte>(entryBuffer);
return BaseDirectory.Target.Read(out entriesRead, buffer);
}
protected override Result DoGetEntryCount(out long entryCount)
{
return BaseDirectory.Target.GetEntryCount(out entryCount);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
BaseDirectory?.Dispose();
}
base.Dispose(disposing);
}
}
}

View File

@ -0,0 +1,75 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.FsSrv.Sf;
namespace LibHac.Fs.Impl
{
/// <summary>
/// An adapter for using an <see cref="IFileSf"/> service object as an <see cref="IFile"/>. Used
/// when receiving a Horizon IPC file object so it can be used as an <see cref="IFile"/> locally.
/// </summary>
internal class FileServiceObjectAdapter : IFile
{
private ReferenceCountedDisposable<IFileSf> BaseFile { get; }
public FileServiceObjectAdapter(ReferenceCountedDisposable<IFileSf> baseFile)
{
BaseFile = baseFile.AddReference();
}
protected override Result DoRead(out long bytesRead, long offset, Span<byte> destination, in ReadOption option)
{
return BaseFile.Target.Read(out bytesRead, offset, destination, option);
}
protected override Result DoWrite(long offset, ReadOnlySpan<byte> source, in WriteOption option)
{
return BaseFile.Target.Write(offset, source, option);
}
protected override Result DoFlush()
{
return BaseFile.Target.Flush();
}
protected override Result DoSetSize(long size)
{
return BaseFile.Target.SetSize(size);
}
protected override Result DoGetSize(out long size)
{
return BaseFile.Target.GetSize(out size);
}
protected override Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan<byte> inBuffer)
{
switch (operationId)
{
case OperationId.InvalidateCache:
return BaseFile.Target.OperateRange(out _, (int)OperationId.InvalidateCache, offset, size);
case OperationId.QueryRange:
if (outBuffer.Length != Unsafe.SizeOf<QueryRangeInfo>())
return ResultFs.InvalidSize.Log();
ref QueryRangeInfo info = ref SpanHelpers.AsStruct<QueryRangeInfo>(outBuffer);
return BaseFile.Target.OperateRange(out info, (int)OperationId.QueryRange, offset, size);
default:
return ResultFs.UnsupportedOperationInFileServiceObjectAdapterA.Log();
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
BaseFile?.Dispose();
}
base.Dispose(disposing);
}
}
}

View File

@ -0,0 +1,211 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.FsSrv.Sf;
using LibHac.Util;
namespace LibHac.Fs.Impl
{
/// <summary>
/// An adapter for using an <see cref="IFileSystemSf"/> service object as an <see cref="IFileSystem"/>. Used
/// when receiving a Horizon IPC file system object so it can be used as an <see cref="IFileSystem"/> locally.
/// </summary>
internal class FileSystemServiceObjectAdapter : IFileSystem, IMultiCommitTarget
{
private ReferenceCountedDisposable<IFileSystemSf> BaseFs { get; }
public FileSystemServiceObjectAdapter(ReferenceCountedDisposable<IFileSystemSf> baseFileSystem)
{
BaseFs = baseFileSystem.AddReference();
}
protected override Result DoCreateFile(U8Span path, long size, CreateFileOptions option)
{
Result rc = GetPathForServiceObject(out Path sfPath, path);
if (rc.IsFailure()) return rc;
return BaseFs.Target.CreateFile(in sfPath, size, (int)option);
}
protected override Result DoDeleteFile(U8Span path)
{
Result rc = GetPathForServiceObject(out Path sfPath, path);
if (rc.IsFailure()) return rc;
return BaseFs.Target.DeleteFile(in sfPath);
}
protected override Result DoCreateDirectory(U8Span path)
{
Result rc = GetPathForServiceObject(out Path sfPath, path);
if (rc.IsFailure()) return rc;
return BaseFs.Target.DeleteFile(in sfPath);
}
protected override Result DoDeleteDirectory(U8Span path)
{
Result rc = GetPathForServiceObject(out Path sfPath, path);
if (rc.IsFailure()) return rc;
return BaseFs.Target.DeleteDirectory(in sfPath);
}
protected override Result DoDeleteDirectoryRecursively(U8Span path)
{
Result rc = GetPathForServiceObject(out Path sfPath, path);
if (rc.IsFailure()) return rc;
return BaseFs.Target.DeleteDirectoryRecursively(in sfPath);
}
protected override Result DoCleanDirectoryRecursively(U8Span path)
{
Result rc = GetPathForServiceObject(out Path sfPath, path);
if (rc.IsFailure()) return rc;
return BaseFs.Target.CleanDirectoryRecursively(in sfPath);
}
protected override Result DoRenameFile(U8Span oldPath, U8Span newPath)
{
Result rc = GetPathForServiceObject(out Path oldSfPath, oldPath);
if (rc.IsFailure()) return rc;
rc = GetPathForServiceObject(out Path newSfPath, newPath);
if (rc.IsFailure()) return rc;
return BaseFs.Target.RenameFile(in oldSfPath, in newSfPath);
}
protected override Result DoRenameDirectory(U8Span oldPath, U8Span newPath)
{
Result rc = GetPathForServiceObject(out Path oldSfPath, oldPath);
if (rc.IsFailure()) return rc;
rc = GetPathForServiceObject(out Path newSfPath, newPath);
if (rc.IsFailure()) return rc;
return BaseFs.Target.RenameDirectory(in oldSfPath, in newSfPath);
}
protected override Result DoGetEntryType(out DirectoryEntryType entryType, U8Span path)
{
Unsafe.SkipInit(out entryType);
Result rc = GetPathForServiceObject(out Path sfPath, path);
if (rc.IsFailure()) return rc;
ref uint sfEntryType = ref Unsafe.As<DirectoryEntryType, uint>(ref entryType);
return BaseFs.Target.GetEntryType(out sfEntryType, in sfPath);
}
protected override Result DoGetFreeSpaceSize(out long freeSpace, U8Span path)
{
Unsafe.SkipInit(out freeSpace);
Result rc = GetPathForServiceObject(out Path sfPath, path);
if (rc.IsFailure()) return rc;
return BaseFs.Target.GetFreeSpaceSize(out freeSpace, in sfPath);
}
protected override Result DoGetTotalSpaceSize(out long totalSpace, U8Span path)
{
Unsafe.SkipInit(out totalSpace);
Result rc = GetPathForServiceObject(out Path sfPath, path);
if (rc.IsFailure()) return rc;
return BaseFs.Target.GetTotalSpaceSize(out totalSpace, in sfPath);
}
protected override Result DoOpenFile(out IFile file, U8Span path, OpenMode mode)
{
file = default;
Result rc = GetPathForServiceObject(out Path sfPath, path);
if (rc.IsFailure()) return rc;
rc = BaseFs.Target.OpenFile(out ReferenceCountedDisposable<IFileSf> sfFile, in sfPath, (uint)mode);
if (rc.IsFailure()) return rc;
file = new FileServiceObjectAdapter(sfFile);
return Result.Success;
}
protected override Result DoOpenDirectory(out IDirectory directory, U8Span path, OpenDirectoryMode mode)
{
directory = default;
Result rc = GetPathForServiceObject(out Path sfPath, path);
if (rc.IsFailure()) return rc;
rc = BaseFs.Target.OpenDirectory(out ReferenceCountedDisposable<IDirectorySf> sfDir, in sfPath, (uint)mode);
if (rc.IsFailure()) return rc;
directory = new DirectoryServiceObjectAdapter(sfDir);
return Result.Success;
}
protected override Result DoCommit()
{
return BaseFs.Target.Commit();
}
protected override Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, U8Span path)
{
Unsafe.SkipInit(out timeStamp);
Result rc = GetPathForServiceObject(out Path sfPath, path);
if (rc.IsFailure()) return rc;
return BaseFs.Target.GetFileTimeStampRaw(out timeStamp, in sfPath);
}
protected override Result DoQueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId, U8Span path)
{
Result rc = GetPathForServiceObject(out Path sfPath, path);
if (rc.IsFailure()) return rc;
return BaseFs.Target.QueryEntry(outBuffer, inBuffer, (int)queryId, in sfPath);
}
public ReferenceCountedDisposable<IFileSystemSf> GetMultiCommitTarget()
{
return BaseFs;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
BaseFs?.Dispose();
}
base.Dispose(disposing);
}
private Result GetPathForServiceObject(out Path sfPath, U8Span path)
{
// This is the function used to create Sf.Path structs. Get an unsafe byte span for init only.
Unsafe.SkipInit(out sfPath);
Span<byte> outPath = SpanHelpers.AsByteSpan(ref sfPath);
// Copy and null terminate
StringUtils.Copy(outPath, path);
outPath[Unsafe.SizeOf<Path>() - 1] = StringTraits.NullTerminator;
// Replace directory separators
PathUtility.Replace(outPath, StringTraits.AltDirectorySeparator, StringTraits.DirectorySeparator);
// Get lengths
int windowsSkipLength = PathUtility.GetWindowsPathSkipLength(path);
var nonWindowsPath = new U8Span(sfPath.Str.Slice(windowsSkipLength));
int maxLength = PathTool.EntryNameLengthMax - windowsSkipLength;
return PathUtility.VerifyPath(nonWindowsPath, maxLength, maxLength);
}
}
}

View File

@ -0,0 +1,53 @@
using System;
using System.Threading;
namespace LibHac.Fs.Impl
{
/// <summary>
/// A wrapper for handling write access to a reader-writer lock.
/// </summary>
public class UniqueLock : IDisposable
{
private ReaderWriterLockSlim _lock;
private bool _hasLock;
public UniqueLock(ReaderWriterLockSlim readerWriterLock)
{
_lock = readerWriterLock;
readerWriterLock.EnterWriteLock();
_hasLock = true;
}
public void Dispose()
{
if (_hasLock)
{
_lock.ExitWriteLock();
}
}
}
/// <summary>
/// A wrapper for handling read access to a reader-writer lock.
/// </summary>
public class SharedLock : IDisposable
{
private ReaderWriterLockSlim _lock;
private bool _hasLock;
public SharedLock(ReaderWriterLockSlim readerWriterLock)
{
_lock = readerWriterLock;
readerWriterLock.EnterReadLock();
_hasLock = true;
}
public void Dispose()
{
if (_hasLock)
{
_lock.EnterReadLock();
}
}
}
}

View File

@ -0,0 +1,73 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.FsSrv.Sf;
namespace LibHac.Fs.Impl
{
/// <summary>
/// An adapter for using an <see cref="IStorageSf"/> service object as an <see cref="IStorage"/>. Used
/// when receiving a Horizon IPC storage object so it can be used as an <see cref="IStorage"/> locally.
/// </summary>
internal class StorageServiceObjectAdapter : IStorage
{
private ReferenceCountedDisposable<IStorageSf> BaseStorage { get; }
public StorageServiceObjectAdapter(ReferenceCountedDisposable<IStorageSf> baseStorage)
{
BaseStorage = baseStorage.AddReference();
}
protected override Result DoRead(long offset, Span<byte> destination)
{
return BaseStorage.Target.Read(offset, destination);
}
protected override Result DoWrite(long offset, ReadOnlySpan<byte> source)
{
return BaseStorage.Target.Write(offset, source);
}
protected override Result DoFlush()
{
return BaseStorage.Target.Flush();
}
protected override Result DoSetSize(long size)
{
return BaseStorage.Target.SetSize(size);
}
protected override Result DoGetSize(out long size)
{
return BaseStorage.Target.GetSize(out size);
}
protected override Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan<byte> inBuffer)
{
switch (operationId)
{
case OperationId.InvalidateCache:
return BaseStorage.Target.OperateRange(out _, (int)OperationId.InvalidateCache, offset, size);
case OperationId.QueryRange:
if (outBuffer.Length != Unsafe.SizeOf<QueryRangeInfo>())
return ResultFs.InvalidSize.Log();
ref QueryRangeInfo info = ref SpanHelpers.AsStruct<QueryRangeInfo>(outBuffer);
return BaseStorage.Target.OperateRange(out info, (int)OperationId.QueryRange, offset, size);
default:
return ResultFs.UnsupportedOperationInFileServiceObjectAdapterA.Log();
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
BaseStorage?.Dispose();
}
base.Dispose(disposing);
}
}
}

View File

@ -19,6 +19,7 @@ namespace LibHac.Fs
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsWindowsDriveCharacter(byte c)
{
// Mask lowercase letters to uppercase and check if it's in range
return (0b1101_1111 & c) - 'A' <= 'Z' - 'A';
//return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z';
}

View File

@ -1,11 +1,31 @@
using System.Runtime.InteropServices;
using System;
using System.Runtime.InteropServices;
namespace LibHac.Fs
{
[StructLayout(LayoutKind.Sequential, Size = 0x40)]
public struct QueryRangeInfo
{
public uint AesCtrKeyType;
public uint SpeedEmulationType;
public int AesCtrKeyType;
public int SpeedEmulationType;
public void Clear()
{
this = default;
}
public void Merge(in QueryRangeInfo other)
{
AesCtrKeyType |= other.AesCtrKeyType;
SpeedEmulationType |= other.SpeedEmulationType;
}
[Flags]
public enum AesCtrKeyTypeFlag
{
InternalKeyForSoftwareAes = 1 << 0,
InternalKeyForHardwareAes = 1 << 1,
ExternalKeyForHardwareAes = 1 << 2,
}
}
}

View File

@ -130,6 +130,8 @@ namespace LibHac.Fs
public static Result.Base AllocationFailureInAesXtsFileE => new Result.Base(ModuleFs, 3383);
/// <summary>In Create allocating AesXtsFileSystem<br/>Error code: 2002-3394; Inner value: 0x1a8402</summary>
public static Result.Base AllocationFailureInEncryptedFileSystemCreatorA => new Result.Base(ModuleFs, 3394);
/// <summary> In OpenFile or OpenDirectory<br/>Error code: 2002-3407; Inner value: 0x1a9e02</summary>
public static Result.Base AllocationFailureInFileSystemInterfaceAdapter => new Result.Base(ModuleFs, 3407);
/// <summary>Error code: 2002-3420; Inner value: 0x1ab802</summary>
public static Result.Base AllocationFailureInNew => new Result.Base(ModuleFs, 3420);
/// <summary>Error code: 2002-3421; Inner value: 0x1aba02</summary>
@ -370,7 +372,7 @@ namespace LibHac.Fs
/// <summary>Error code: 2002-6031; Inner value: 0x2f1e02</summary>
public static Result.Base DirectoryNotDeletable => new Result.Base(ModuleFs, 6031);
/// <summary>Error code: 2002-6032; Inner value: 0x2f2002</summary>
public static Result.Base DestinationIsSubPathOfSource => new Result.Base(ModuleFs, 6032);
public static Result.Base DirectoryNotRenamable => new Result.Base(ModuleFs, 6032);
/// <summary>Error code: 2002-6033; Inner value: 0x2f2202</summary>
public static Result.Base PathNotFoundInSaveDataFileTable => new Result.Base(ModuleFs, 6033);
/// <summary>Error code: 2002-6034; Inner value: 0x2f2402</summary>
@ -443,6 +445,8 @@ namespace LibHac.Fs
public static Result.Base UnsupportedOperationInRoGameCardStorageSetSize => new Result.Base(ModuleFs, 6351);
/// <summary>Error code: 2002-6359; Inner value: 0x31ae02</summary>
public static Result.Base UnsupportedOperationInConcatFsQueryEntry => new Result.Base(ModuleFs, 6359);
/// <summary>Called OperateRange with an invalid operation ID.<br/>Error code: 2002-6362; Inner value: 0x31b402</summary>
public static Result.Base UnsupportedOperationInFileServiceObjectAdapterA => new Result.Base(ModuleFs, 6362);
/// <summary>Error code: 2002-6364; Inner value: 0x31b802</summary>
public static Result.Base UnsupportedOperationModifyRomFsFileSystem => new Result.Base(ModuleFs, 6364);
/// <summary>Called RomFsFileSystem::CommitProvisionally.<br/>Error code: 2002-6365; Inner value: 0x31ba02</summary>

View File

@ -1,6 +1,6 @@
using System;
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.Fs.Impl;
using LibHac.FsSrv;
using LibHac.FsSrv.Sf;
@ -44,11 +44,16 @@ namespace LibHac.Fs.Shim
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
rc = fsProxy.OpenFileSystemWithId(out ReferenceCountedDisposable<IFileSystem> fileSystem, in sfPath,
rc = fsProxy.OpenFileSystemWithId(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, in sfPath,
default, FileSystemProxyType.Package);
if (rc.IsFailure()) return rc;
return fs.Register(mountName, fileSystem.Target);
using (fileSystem)
{
var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem);
return fs.Register(mountName, fileSystemAdapter);
}
}
}
}

View File

@ -1,7 +1,7 @@
using System;
using System.Diagnostics;
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.Fs.Impl;
using LibHac.FsSrv;
using LibHac.FsSrv.Sf;
using LibHac.FsSystem;
@ -91,13 +91,17 @@ namespace LibHac.Fs.Shim
// Nintendo doesn't use the provided rootPath
FspPath.CreateEmpty(out FspPath sfPath);
rc = fsProxy.OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, in sfPath,
rc = fsProxy.OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, in sfPath,
partitionId);
if (rc.IsFailure()) return rc;
var nameGenerator = new BisCommonMountNameGenerator(partitionId);
using (fileSystem)
{
var nameGenerator = new BisCommonMountNameGenerator(partitionId);
var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem);
return fs.Register(mountName, fileSystem.Target, nameGenerator);
return fs.Register(mountName, fileSystemAdapter, nameGenerator);
}
}
public static U8Span GetBisMountName(BisPartitionId partitionId)
@ -165,11 +169,16 @@ namespace LibHac.Fs.Shim
partitionStorage = default;
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
Result rc = fsProxy.OpenBisStorage(out IStorage storage, partitionId);
Result rc = fsProxy.OpenBisStorage(out ReferenceCountedDisposable<IStorageSf> storage, partitionId);
if (rc.IsFailure()) return rc;
partitionStorage = storage;
return Result.Success;
using (storage)
{
var storageAdapter = new StorageServiceObjectAdapter(storage);
partitionStorage = storageAdapter;
return Result.Success;
}
}
public static Result InvalidateBisCache(this FileSystemClient fs)

View File

@ -1,7 +1,7 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.Fs.Impl;
using LibHac.FsSrv.Sf;
using LibHac.Ncm;
@ -49,11 +49,16 @@ namespace LibHac.Fs.Shim
IFileSystemProxyForLoader fsProxy = fs.GetFileSystemProxyForLoaderServiceObject();
rc = fsProxy.OpenCodeFileSystem(out ReferenceCountedDisposable<IFileSystem> codeFs, out verificationData,
rc = fsProxy.OpenCodeFileSystem(out ReferenceCountedDisposable<IFileSystemSf> codeFs, out verificationData,
in fsPath, programId);
if (rc.IsFailure()) return rc;
return fs.Register(mountName, codeFs.Target);
using (codeFs)
{
var fileSystemAdapter = new FileSystemServiceObjectAdapter(codeFs);
return fs.Register(mountName, fileSystemAdapter);
}
}
}
}

View File

@ -1,6 +1,6 @@
using System;
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.Fs.Impl;
using LibHac.FsSrv;
using LibHac.FsSrv.Sf;
using LibHac.Ncm;
@ -27,11 +27,16 @@ namespace LibHac.Fs.Shim
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
rc = fsProxy.OpenFileSystemWithPatch(out ReferenceCountedDisposable<IFileSystem> fileSystem, programId,
rc = fsProxy.OpenFileSystemWithPatch(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, programId,
fspType);
if (rc.IsFailure()) return rc;
return fs.Register(mountName, fileSystem.Target);
using (fileSystem)
{
var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem);
return fs.Register(mountName, fileSystemAdapter);
}
}
public static Result MountContent(this FileSystemClient fs, U8Span mountName, U8Span path, ProgramId programId, ContentType type)
@ -60,11 +65,16 @@ namespace LibHac.Fs.Shim
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
Result rc = fsProxy.OpenFileSystemWithId(out ReferenceCountedDisposable<IFileSystem> fileSystem, in fsPath,
Result rc = fsProxy.OpenFileSystemWithId(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, in fsPath,
id, type);
if (rc.IsFailure()) return rc;
return fs.Register(mountName, fileSystem.Target);
using (fileSystem)
{
var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem);
return fs.Register(mountName, fileSystemAdapter);
}
}
private static FileSystemProxyType ConvertToFileSystemProxyType(ContentType type) => type switch

View File

@ -1,7 +1,8 @@
using System;
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.Fs.Impl;
using LibHac.FsSrv;
using LibHac.FsSrv.Sf;
using LibHac.Util;
namespace LibHac.Fs.Shim
@ -20,12 +21,17 @@ namespace LibHac.Fs.Shim
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
rc = fsProxy.OpenContentStorageFileSystem(out ReferenceCountedDisposable<IFileSystem> contentFs, storageId);
rc = fsProxy.OpenContentStorageFileSystem(out ReferenceCountedDisposable<IFileSystemSf> contentFs, storageId);
if (rc.IsFailure()) return rc;
var mountNameGenerator = new ContentStorageCommonMountNameGenerator(storageId);
using (contentFs)
{
var mountNameGenerator = new ContentStorageCommonMountNameGenerator(storageId);
return fs.Register(mountName, contentFs.Target, mountNameGenerator);
var fileSystemAdapter = new FileSystemServiceObjectAdapter(contentFs);
return fs.Register(mountName, fileSystemAdapter, mountNameGenerator);
}
}
public static U8String GetContentStorageMountName(ContentStorageId storageId)

View File

@ -1,7 +1,8 @@
using System;
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.Fs.Impl;
using LibHac.FsSrv;
using LibHac.FsSrv.Sf;
namespace LibHac.Fs.Shim
{
@ -48,12 +49,16 @@ namespace LibHac.Fs.Shim
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
rc = fsProxy.OpenGameCardFileSystem(out ReferenceCountedDisposable<IFileSystem> cardFs, handle, partitionId);
rc = fsProxy.OpenGameCardFileSystem(out ReferenceCountedDisposable<IFileSystemSf> cardFs, handle, partitionId);
if (rc.IsFailure()) return rc;
var mountNameGenerator = new GameCardCommonMountNameGenerator(handle, partitionId);
using (cardFs)
{
var mountNameGenerator = new GameCardCommonMountNameGenerator(handle, partitionId);
var fileSystemAdapter = new FileSystemServiceObjectAdapter(cardFs);
return fs.Register(mountName, cardFs.Target, mountNameGenerator);
return fs.Register(mountName, fileSystemAdapter, mountNameGenerator);
}
}
private class GameCardCommonMountNameGenerator : ICommonMountNameGenerator

View File

@ -1,7 +1,8 @@
using System;
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.Fs.Impl;
using LibHac.FsSrv;
using LibHac.FsSrv.Sf;
namespace LibHac.Fs.Shim
{
@ -41,10 +42,15 @@ namespace LibHac.Fs.Shim
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
rc = fsProxy.OpenSdCardFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem);
rc = fsProxy.OpenSdCardFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem);
if (rc.IsFailure()) return rc;
return fs.Register(mountName, fileSystem.Target);
using (fileSystem)
{
var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem);
return fs.Register(mountName, fileSystemAdapter);
}
}
}

View File

@ -17,7 +17,7 @@ namespace LibHac.FsSrv
_processId = processId;
}
public Result OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, in FspPath rootPath,
public Result OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, in FspPath rootPath,
BisPartitionId partitionId)
{
fileSystem = default;
@ -50,12 +50,24 @@ namespace LibHac.FsSrv
var normalizer = new PathNormalizer(rootPath, PathNormalizer.Option.AcceptEmpty);
if (normalizer.Result.IsFailure()) return normalizer.Result;
rc = _serviceImpl.OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystem> bisFs, normalizer.Path,
partitionId);
if (rc.IsFailure()) return rc;
ReferenceCountedDisposable<IFileSystem> fs = null;
fileSystem = bisFs;
return Result.Success;
try
{
// Open the file system
rc = _serviceImpl.OpenBisFileSystem(out fs, normalizer.Path,
partitionId);
if (rc.IsFailure()) return rc;
// Create an SF adapter for the file system
fileSystem = FileSystemInterfaceAdapter.CreateSharedSfFileSystem(ref fs);
return Result.Success;
}
finally
{
fs?.Dispose();
}
}
public Result CreatePaddingFile(long size)
@ -86,7 +98,7 @@ namespace LibHac.FsSrv
return _serviceImpl.DeleteAllPaddingFiles();
}
public Result OpenGameCardFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, GameCardHandle handle,
public Result OpenGameCardFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, GameCardHandle handle,
GameCardPartition partitionId)
{
fileSystem = default;
@ -97,14 +109,25 @@ namespace LibHac.FsSrv
if (!programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountGameCard).CanRead)
return ResultFs.PermissionDenied.Log();
rc = _serviceImpl.OpenGameCardFileSystem(out ReferenceCountedDisposable<IFileSystem> gcFs, handle, partitionId);
if (rc.IsFailure()) return rc;
ReferenceCountedDisposable<IFileSystem> fs = null;
fileSystem = gcFs;
return Result.Success;
try
{
rc = _serviceImpl.OpenGameCardFileSystem(out fs, handle, partitionId);
if (rc.IsFailure()) return rc;
// Create an SF adapter for the file system
fileSystem = FileSystemInterfaceAdapter.CreateSharedSfFileSystem(ref fs);
return Result.Success;
}
finally
{
fs?.Dispose();
}
}
public Result OpenSdCardFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem)
public Result OpenSdCardFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem)
{
fileSystem = default;
@ -116,11 +139,22 @@ namespace LibHac.FsSrv
if (!accessibility.CanRead || !accessibility.CanWrite)
return ResultFs.PermissionDenied.Log();
rc = _serviceImpl.OpenSdCardProxyFileSystem(out ReferenceCountedDisposable<IFileSystem> sdCardFs);
if (rc.IsFailure()) return rc;
ReferenceCountedDisposable<IFileSystem> fs = null;
fileSystem = sdCardFs;
return Result.Success;
try
{
rc = _serviceImpl.OpenSdCardProxyFileSystem(out fs);
if (rc.IsFailure()) return rc;
// Create an SF adapter for the file system
fileSystem = FileSystemInterfaceAdapter.CreateSharedSfFileSystem(ref fs);
return Result.Success;
}
finally
{
fs?.Dispose();
}
}
public Result FormatSdCardFileSystem()
@ -150,7 +184,7 @@ namespace LibHac.FsSrv
return Result.Success;
}
public Result OpenImageDirectoryFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
public Result OpenImageDirectoryFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem,
ImageDirectoryId directoryId)
{
fileSystem = default;
@ -178,15 +212,25 @@ namespace LibHac.FsSrv
default:
return ResultFs.InvalidArgument.Log();
}
ReferenceCountedDisposable<IFileSystem> fs = null;
rc = _serviceImpl.OpenBaseFileSystem(out ReferenceCountedDisposable<IFileSystem> imageFs, id);
if (rc.IsFailure()) return rc;
try
{
rc = _serviceImpl.OpenBaseFileSystem(out fs, id);
if (rc.IsFailure()) return rc;
fileSystem = imageFs;
return Result.Success;
// Create an SF adapter for the file system
fileSystem = FileSystemInterfaceAdapter.CreateSharedSfFileSystem(ref fs);
return Result.Success;
}
finally
{
fs?.Dispose();
}
}
public Result OpenBisWiper(out IWiper bisWiper, NativeHandle transferMemoryHandle, ulong transferMemorySize)
public Result OpenBisWiper(out ReferenceCountedDisposable<IWiper> bisWiper, NativeHandle transferMemoryHandle, ulong transferMemorySize)
{
bisWiper = default;
@ -200,7 +244,7 @@ namespace LibHac.FsSrv
rc = _serviceImpl.OpenBisWiper(out IWiper wiper, transferMemoryHandle, transferMemorySize);
if (rc.IsFailure()) return rc;
bisWiper = wiper;
bisWiper = new ReferenceCountedDisposable<IWiper>(wiper);
return Result.Success;
}

View File

@ -14,7 +14,6 @@ namespace LibHac.FsSrv
private FileSystemCreators FsCreators => Config.FsCreatorInterfaces;
internal ProgramRegistryImpl ProgramRegistry { get; }
private ExternalKeySet ExternalKeys { get; }
private IDeviceOperator DeviceOperator { get; }
private byte[] SdEncryptionSeed { get; } = new byte[0x10];
@ -26,11 +25,10 @@ namespace LibHac.FsSrv
internal ISaveDataIndexerManager SaveDataIndexerManager { get; private set; }
public FileSystemProxyCoreImpl(FileSystemProxyConfiguration config, ExternalKeySet externalKeys, IDeviceOperator deviceOperator)
public FileSystemProxyCoreImpl(FileSystemProxyConfiguration config, IDeviceOperator deviceOperator)
{
Config = config;
ProgramRegistry = new ProgramRegistryImpl(Config.ProgramRegistryService);
ExternalKeys = externalKeys ?? new ExternalKeySet();
DeviceOperator = deviceOperator;
}

View File

@ -18,7 +18,7 @@ namespace LibHac.FsSrv
public class FileSystemProxyImpl : IFileSystemProxy, IFileSystemProxyForLoader
{
private FileSystemProxyCoreImpl FsProxyCore { get; }
private ReferenceCountedDisposable<NcaFileSystemService> NcaFileSystemService { get; set; }
private ReferenceCountedDisposable<NcaFileSystemService> NcaFsService { get; set; }
internal HorizonClient Hos { get; }
public ulong CurrentProcess { get; private set; }
@ -44,7 +44,7 @@ namespace LibHac.FsSrv
return new ProgramRegistryService(FsProxyCore.Config.ProgramRegistryService, CurrentProcess);
}
public Result OpenFileSystemWithId(out ReferenceCountedDisposable<IFileSystem> fileSystem, in FspPath path,
public Result OpenFileSystemWithId(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, in FspPath path,
ulong id, FileSystemProxyType fsType)
{
@ -58,7 +58,7 @@ namespace LibHac.FsSrv
return ncaFsService.OpenFileSystemWithId(out fileSystem, in path, id, fsType);
}
public Result OpenFileSystemWithPatch(out ReferenceCountedDisposable<IFileSystem> fileSystem,
public Result OpenFileSystemWithPatch(out ReferenceCountedDisposable<IFileSystemSf> fileSystem,
ProgramId programId, FileSystemProxyType fsType)
{
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
@ -71,7 +71,7 @@ namespace LibHac.FsSrv
return ncaFsService.OpenFileSystemWithPatch(out fileSystem, programId, fsType);
}
public Result OpenCodeFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
public Result OpenCodeFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem,
out CodeVerificationData verificationData, in FspPath path, ProgramId programId)
{
Unsafe.SkipInit(out verificationData);
@ -101,9 +101,7 @@ namespace LibHac.FsSrv
CurrentProcess = processId;
// Initialize the NCA file system service
var ncaService = new NcaFileSystemService(FsProxyCore.Config.NcaFileSystemService, processId);
NcaFileSystemService = new ReferenceCountedDisposable<NcaFileSystemService>(ncaService);
NcaFileSystemService.Target.SetSelfReference(NcaFileSystemService);
NcaFsService = NcaFileSystemService.Create(FsProxyCore.Config.NcaFileSystemService, processId);
return Result.Success;
}
@ -113,7 +111,7 @@ namespace LibHac.FsSrv
throw new NotImplementedException();
}
public Result OpenDataFileSystemByCurrentProcess(out ReferenceCountedDisposable<IFileSystem> fileSystem)
public Result OpenDataFileSystemByCurrentProcess(out ReferenceCountedDisposable<IFileSystemSf> fileSystem)
{
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
if (rc.IsFailure())
@ -125,7 +123,7 @@ namespace LibHac.FsSrv
return ncaFsService.OpenDataFileSystemByCurrentProcess(out fileSystem);
}
public Result OpenDataFileSystemByProgramId(out ReferenceCountedDisposable<IFileSystem> fileSystem,
public Result OpenDataFileSystemByProgramId(out ReferenceCountedDisposable<IFileSystemSf> fileSystem,
ProgramId programId)
{
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
@ -138,7 +136,7 @@ namespace LibHac.FsSrv
return ncaFsService.OpenDataFileSystemByProgramId(out fileSystem, programId);
}
public Result OpenDataStorageByCurrentProcess(out ReferenceCountedDisposable<IStorage> storage)
public Result OpenDataStorageByCurrentProcess(out ReferenceCountedDisposable<IStorageSf> storage)
{
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
if (rc.IsFailure())
@ -150,7 +148,7 @@ namespace LibHac.FsSrv
return ncaFsService.OpenDataStorageByCurrentProcess(out storage);
}
public Result OpenDataStorageByProgramId(out ReferenceCountedDisposable<IStorage> storage, ProgramId programId)
public Result OpenDataStorageByProgramId(out ReferenceCountedDisposable<IStorageSf> storage, ProgramId programId)
{
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
if (rc.IsFailure())
@ -162,7 +160,7 @@ namespace LibHac.FsSrv
return ncaFsService.OpenDataStorageByProgramId(out storage, programId);
}
public Result OpenDataStorageByDataId(out ReferenceCountedDisposable<IStorage> storage, DataId dataId, StorageId storageId)
public Result OpenDataStorageByDataId(out ReferenceCountedDisposable<IStorageSf> storage, DataId dataId, StorageId storageId)
{
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
if (rc.IsFailure())
@ -174,13 +172,13 @@ namespace LibHac.FsSrv
return ncaFsService.OpenDataStorageByDataId(out storage, dataId, storageId);
}
public Result OpenPatchDataStorageByCurrentProcess(out IStorage storage)
public Result OpenPatchDataStorageByCurrentProcess(out ReferenceCountedDisposable<IStorageSf> storage)
{
storage = default;
return ResultFs.TargetNotFound.Log();
}
public Result OpenDataFileSystemWithProgramIndex(out ReferenceCountedDisposable<IFileSystem> fileSystem,
public Result OpenDataFileSystemWithProgramIndex(out ReferenceCountedDisposable<IFileSystemSf> fileSystem,
byte programIndex)
{
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
@ -193,7 +191,7 @@ namespace LibHac.FsSrv
return ncaFsService.OpenDataFileSystemWithProgramIndex(out fileSystem, programIndex);
}
public Result OpenDataStorageWithProgramIndex(out ReferenceCountedDisposable<IStorage> storage, byte programIndex)
public Result OpenDataStorageWithProgramIndex(out ReferenceCountedDisposable<IStorageSf> storage, byte programIndex)
{
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
if (rc.IsFailure())
@ -744,7 +742,7 @@ namespace LibHac.FsSrv
throw new NotImplementedException();
}
public Result OpenImageDirectoryFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
public Result OpenImageDirectoryFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem,
ImageDirectoryId directoryId)
{
return GetBaseFileSystemService().OpenImageDirectoryFileSystem(out fileSystem, directoryId);
@ -760,13 +758,13 @@ namespace LibHac.FsSrv
throw new NotImplementedException();
}
public Result OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, in FspPath rootPath,
public Result OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, in FspPath rootPath,
BisPartitionId partitionId)
{
return GetBaseFileSystemService().OpenBisFileSystem(out fileSystem, in rootPath, partitionId);
}
public Result OpenBisStorage(out IStorage storage, BisPartitionId partitionId)
public Result OpenBisStorage(out ReferenceCountedDisposable<IStorageSf> storage, BisPartitionId partitionId)
{
throw new NotImplementedException();
}
@ -788,7 +786,7 @@ namespace LibHac.FsSrv
return OpenHostFileSystemWithOption(out fileSystem, ref path, MountHostOption.None);
}
public Result OpenSdCardFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem)
public Result OpenSdCardFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem)
{
return GetBaseFileSystemService().OpenSdCardFileSystem(out fileSystem);
}
@ -952,7 +950,8 @@ namespace LibHac.FsSrv
}
}
public Result OpenSaveDataInternalStorageFileSystem(out IFileSystem fileSystem, SaveDataSpaceId spaceId, ulong saveDataId)
public Result OpenSaveDataInternalStorageFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem,
SaveDataSpaceId spaceId, ulong saveDataId)
{
throw new NotImplementedException();
}
@ -1062,7 +1061,7 @@ namespace LibHac.FsSrv
return Result.Success;
}
public Result OpenContentStorageFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, ContentStorageId storageId)
public Result OpenContentStorageFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, ContentStorageId storageId)
{
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
if (rc.IsFailure())
@ -1074,7 +1073,8 @@ namespace LibHac.FsSrv
return ncaFsService.OpenContentStorageFileSystem(out fileSystem, storageId);
}
public Result OpenCloudBackupWorkStorageFileSystem(out IFileSystem fileSystem, CloudBackupWorkStorageId storageId)
public Result OpenCloudBackupWorkStorageFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem,
CloudBackupWorkStorageId storageId)
{
throw new NotImplementedException();
}
@ -1086,7 +1086,7 @@ namespace LibHac.FsSrv
return FsProxyCore.OpenCustomStorageFileSystem(out fileSystem, storageId);
}
public Result OpenGameCardFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
public Result OpenGameCardFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem,
GameCardHandle handle, GameCardPartition partitionId)
{
return GetBaseFileSystemService().OpenGameCardFileSystem(out fileSystem, handle, partitionId);
@ -1245,7 +1245,7 @@ namespace LibHac.FsSrv
return ncaFsService.RegisterUpdatePartition();
}
public Result OpenRegisteredUpdatePartition(out ReferenceCountedDisposable<IFileSystem> fileSystem)
public Result OpenRegisteredUpdatePartition(out ReferenceCountedDisposable<IFileSystemSf> fileSystem)
{
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
if (rc.IsFailure())
@ -1296,7 +1296,8 @@ namespace LibHac.FsSrv
return Result.Success;
}
public Result OpenBisWiper(out IWiper bisWiper, NativeHandle transferMemoryHandle, ulong transferMemorySize)
public Result OpenBisWiper(out ReferenceCountedDisposable<IWiper> bisWiper, NativeHandle transferMemoryHandle,
ulong transferMemorySize)
{
return GetBaseFileSystemService().OpenBisWiper(out bisWiper, transferMemoryHandle, transferMemorySize);
}
@ -1357,13 +1358,13 @@ namespace LibHac.FsSrv
private Result GetNcaFileSystemService(out NcaFileSystemService ncaFsService)
{
if (NcaFileSystemService is null)
if (NcaFsService is null)
{
ncaFsService = null;
return ResultFs.PreconditionViolation.Log();
}
ncaFsService = NcaFileSystemService.Target;
ncaFsService = NcaFsService.Target;
return Result.Success;
}

View File

@ -40,12 +40,11 @@ namespace LibHac.FsSrv
IsDebugMode = false;
ExternalKeySet externalKeySet = config.ExternalKeySet ?? new ExternalKeySet();
Timer = config.TimeSpanGenerator ?? new StopWatchTimeSpanGenerator();
FileSystemProxyConfiguration fspConfig = InitializeFileSystemProxyConfiguration(config);
FsProxyCore = new FileSystemProxyCoreImpl(fspConfig, externalKeySet, config.DeviceOperator);
FsProxyCore = new FileSystemProxyCoreImpl(fspConfig, config.DeviceOperator);
FsProxyCore.SetSaveDataIndexerManager(new SaveDataIndexerManager(Hos.Fs, SaveIndexerId,
new ArrayPoolMemoryResource(), new SdHandleManager(), false));

View File

@ -13,16 +13,16 @@ namespace LibHac.FsSrv
public interface IFileSystemProxy
{
Result SetCurrentProcess(ulong processId);
Result OpenDataFileSystemByCurrentProcess(out ReferenceCountedDisposable<IFileSystem> fileSystem);
Result OpenFileSystemWithPatch(out ReferenceCountedDisposable<IFileSystem> fileSystem, ProgramId programId, FileSystemProxyType fsType);
Result OpenFileSystemWithId(out ReferenceCountedDisposable<IFileSystem> fileSystem, in FspPath path, ulong id, FileSystemProxyType fsType);
Result OpenDataFileSystemByProgramId(out ReferenceCountedDisposable<IFileSystem> fileSystem, ProgramId programId);
Result OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, in FspPath rootPath, BisPartitionId partitionId);
Result OpenBisStorage(out IStorage storage, BisPartitionId partitionId);
Result OpenDataFileSystemByCurrentProcess(out ReferenceCountedDisposable<IFileSystemSf> fileSystem);
Result OpenFileSystemWithPatch(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, ProgramId programId, FileSystemProxyType fsType);
Result OpenFileSystemWithId(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, in FspPath path, ulong id, FileSystemProxyType fsType);
Result OpenDataFileSystemByProgramId(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, ProgramId programId);
Result OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, in FspPath rootPath, BisPartitionId partitionId);
Result OpenBisStorage(out ReferenceCountedDisposable<IStorageSf> storage, BisPartitionId partitionId);
Result InvalidateBisCache();
Result OpenHostFileSystemWithOption(out IFileSystem fileSystem, ref FsPath path, MountHostOption option);
Result OpenHostFileSystem(out IFileSystem fileSystem, ref FsPath path);
Result OpenSdCardFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem);
Result OpenSdCardFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem);
Result FormatSdCardFileSystem();
Result DeleteSaveDataFileSystem(ulong saveDataId);
Result CreateSaveDataFileSystem(ref SaveDataAttribute attribute, ref SaveDataCreationInfo creationInfo, ref SaveMetaCreateInfo metaCreateInfo);
@ -33,7 +33,7 @@ namespace LibHac.FsSrv
Result IsExFatSupported(out bool isSupported);
Result DeleteSaveDataFileSystemBySaveDataAttribute(SaveDataSpaceId spaceId, ref SaveDataAttribute attribute);
Result OpenGameCardStorage(out IStorage storage, GameCardHandle handle, GameCardPartitionRaw partitionId);
Result OpenGameCardFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, GameCardHandle handle, GameCardPartition partitionId);
Result OpenGameCardFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, GameCardHandle handle, GameCardPartition partitionId);
Result ExtendSaveDataFileSystem(SaveDataSpaceId spaceId, ulong saveDataId, long dataSize, long journalSize);
Result DeleteCacheStorage(short index);
Result GetCacheStorageSize(out long dataSize, out long journalSize, short index);
@ -47,7 +47,7 @@ namespace LibHac.FsSrv
Result OpenSaveDataInfoReader(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader);
Result OpenSaveDataInfoReaderBySaveDataSpaceId(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader, SaveDataSpaceId spaceId);
Result OpenSaveDataInfoReaderOnlyCacheStorage(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader);
Result OpenSaveDataInternalStorageFileSystem(out IFileSystem fileSystem, SaveDataSpaceId spaceId, ulong saveDataId);
Result OpenSaveDataInternalStorageFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, SaveDataSpaceId spaceId, ulong saveDataId);
Result UpdateSaveDataMacForDebug(SaveDataSpaceId spaceId, ulong saveDataId);
Result WriteSaveDataFileSystemExtraDataWithMask(ulong saveDataId, SaveDataSpaceId spaceId, ReadOnlySpan<byte> extraDataBuffer, ReadOnlySpan<byte> maskBuffer);
Result FindSaveDataWithFilter(out long count, Span<byte> saveDataInfoBuffer, SaveDataSpaceId spaceId, ref SaveDataFilter filter);
@ -57,16 +57,16 @@ namespace LibHac.FsSrv
Result OpenSaveDataMetaFile(out IFile file, SaveDataSpaceId spaceId, ref SaveDataAttribute attribute, SaveDataMetaType type);
Result ListAccessibleSaveDataOwnerId(out int readCount, Span<Ncm.ApplicationId> idBuffer, ProgramId programId, int startIndex, int bufferIdCount);
Result OpenImageDirectoryFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, ImageDirectoryId directoryId);
Result OpenContentStorageFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, ContentStorageId storageId);
Result OpenCloudBackupWorkStorageFileSystem(out IFileSystem fileSystem, CloudBackupWorkStorageId storageId);
Result OpenImageDirectoryFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, ImageDirectoryId directoryId);
Result OpenContentStorageFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, ContentStorageId storageId);
Result OpenCloudBackupWorkStorageFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, CloudBackupWorkStorageId storageId);
Result OpenCustomStorageFileSystem(out IFileSystem fileSystem, CustomStorageId storageId);
Result OpenDataStorageByCurrentProcess(out ReferenceCountedDisposable<IStorage> storage);
Result OpenDataStorageByProgramId(out ReferenceCountedDisposable<IStorage> storage, ProgramId programId);
Result OpenDataStorageByDataId(out ReferenceCountedDisposable<IStorage> storage, DataId dataId, StorageId storageId);
Result OpenPatchDataStorageByCurrentProcess(out IStorage storage);
Result OpenDataFileSystemWithProgramIndex(out ReferenceCountedDisposable<IFileSystem> fileSystem, byte programIndex);
Result OpenDataStorageWithProgramIndex(out ReferenceCountedDisposable<IStorage> storage, byte programIndex);
Result OpenDataStorageByCurrentProcess(out ReferenceCountedDisposable<IStorageSf> storage);
Result OpenDataStorageByProgramId(out ReferenceCountedDisposable<IStorageSf> storage, ProgramId programId);
Result OpenDataStorageByDataId(out ReferenceCountedDisposable<IStorageSf> storage, DataId dataId, StorageId storageId);
Result OpenPatchDataStorageByCurrentProcess(out ReferenceCountedDisposable<IStorageSf> storage);
Result OpenDataFileSystemWithProgramIndex(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, byte programIndex);
Result OpenDataStorageWithProgramIndex(out ReferenceCountedDisposable<IStorageSf> storage, byte programIndex);
Result OpenDeviceOperator(out IDeviceOperator deviceOperator);
Result OpenSystemDataUpdateEventNotifier(out ReferenceCountedDisposable<IEventNotifier> eventNotifier);
@ -102,12 +102,12 @@ namespace LibHac.FsSrv
Result GetGlobalAccessLogMode(out GlobalAccessLogMode mode);
Result OutputAccessLogToSdCard(U8Span logString);
Result RegisterUpdatePartition();
Result OpenRegisteredUpdatePartition(out ReferenceCountedDisposable<IFileSystem> fileSystem);
Result OpenRegisteredUpdatePartition(out ReferenceCountedDisposable<IFileSystemSf> fileSystem);
Result GetProgramIndexForAccessLog(out int programIndex, out int programCount);
Result OverrideSaveDataTransferTokenSignVerificationKey(ReadOnlySpan<byte> key);
Result CorruptSaveDataFileSystemByOffset(SaveDataSpaceId spaceId, ulong saveDataId, long offset);
Result OpenMultiCommitManager(out IMultiCommitManager commitManager);
Result OpenBisWiper(out IWiper bisWiper, NativeHandle transferMemoryHandle, ulong transferMemorySize);
Result OpenBisWiper(out ReferenceCountedDisposable<IWiper> bisWiper, NativeHandle transferMemoryHandle, ulong transferMemorySize);
}
}

View File

@ -25,5 +25,9 @@ namespace LibHac.FsSrv.Impl
wiper = new BisWiper(memoryHandle, memorySize);
return Result.Success;
}
public void Dispose()
{
}
}
}

View File

@ -0,0 +1,64 @@
using System;
using System.Runtime.InteropServices;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSrv.Sf;
namespace LibHac.FsSrv.Impl
{
internal class DirectoryInterfaceAdapter : IDirectorySf
{
private ReferenceCountedDisposable<FileSystemInterfaceAdapter> ParentFs { get; }
private IDirectory BaseDirectory { get; }
public DirectoryInterfaceAdapter(IDirectory baseDirectory,
ref ReferenceCountedDisposable<FileSystemInterfaceAdapter> parentFileSystem)
{
BaseDirectory = baseDirectory;
ParentFs = parentFileSystem;
parentFileSystem = null;
}
public Result Read(out long entriesRead, Span<byte> entryBuffer)
{
const int maxTryCount = 2;
entriesRead = default;
Span<DirectoryEntry> entries = MemoryMarshal.Cast<byte, DirectoryEntry>(entryBuffer);
Result rc = Result.Success;
long tmpEntriesRead = 0;
for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
{
rc = BaseDirectory.Read(out tmpEntriesRead, entries);
// Retry on ResultDataCorrupted
if (!ResultFs.DataCorrupted.Includes(rc))
break;
}
if (rc.IsFailure()) return rc;
entriesRead = tmpEntriesRead;
return Result.Success;
}
public Result GetEntryCount(out long entryCount)
{
entryCount = default;
Result rc = BaseDirectory.GetEntryCount(out long tmpEntryCount);
if (rc.IsFailure()) return rc;
entryCount = tmpEntryCount;
return Result.Success;
}
public void Dispose()
{
BaseDirectory?.Dispose();
ParentFs?.Dispose();
}
}
}

View File

@ -0,0 +1,131 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSrv.Sf;
namespace LibHac.FsSrv.Impl
{
internal class FileInterfaceAdapter : IFileSf
{
private ReferenceCountedDisposable<FileSystemInterfaceAdapter> ParentFs { get; }
private IFile BaseFile { get; }
public FileInterfaceAdapter(IFile baseFile,
ref ReferenceCountedDisposable<FileSystemInterfaceAdapter> parentFileSystem)
{
BaseFile = baseFile;
ParentFs = parentFileSystem;
parentFileSystem = null;
}
public Result Read(out long bytesRead, long offset, Span<byte> destination, ReadOption option)
{
const int maxTryCount = 2;
bytesRead = default;
if (offset < 0)
return ResultFs.InvalidOffset.Log();
if (destination.Length < 0)
return ResultFs.InvalidSize.Log();
Result rc = Result.Success;
long tmpBytesRead = 0;
for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
{
rc = BaseFile.Read(out tmpBytesRead, offset, destination, option);
// Retry on ResultDataCorrupted
if (!ResultFs.DataCorrupted.Includes(rc))
break;
}
if (rc.IsFailure()) return rc;
bytesRead = tmpBytesRead;
return Result.Success;
}
public Result Write(long offset, ReadOnlySpan<byte> source, WriteOption option)
{
if (offset < 0)
return ResultFs.InvalidOffset.Log();
if (source.Length < 0)
return ResultFs.InvalidSize.Log();
// Note: Thread priority is temporarily when writing in FS
return BaseFile.Write(offset, source, option);
}
public Result Flush()
{
return BaseFile.Flush();
}
public Result SetSize(long size)
{
if (size < 0)
return ResultFs.InvalidSize.Log();
return BaseFile.SetSize(size);
}
public Result GetSize(out long size)
{
const int maxTryCount = 2;
size = default;
Result rc = Result.Success;
long tmpSize = 0;
for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
{
rc = BaseFile.GetSize(out tmpSize);
// Retry on ResultDataCorrupted
if (!ResultFs.DataCorrupted.Includes(rc))
break;
}
if (rc.IsFailure()) return rc;
size = tmpSize;
return Result.Success;
}
public Result OperateRange(out QueryRangeInfo rangeInfo, int operationId, long offset, long size)
{
rangeInfo = new QueryRangeInfo();
if (operationId == (int)OperationId.InvalidateCache)
{
Result rc = BaseFile.OperateRange(Span<byte>.Empty, OperationId.InvalidateCache, offset, size,
ReadOnlySpan<byte>.Empty);
if (rc.IsFailure()) return rc;
}
else if (operationId == (int)OperationId.QueryRange)
{
Unsafe.SkipInit(out QueryRangeInfo info);
Result rc = BaseFile.OperateRange(SpanHelpers.AsByteSpan(ref info), OperationId.QueryRange, offset, size,
ReadOnlySpan<byte>.Empty);
if (rc.IsFailure()) return rc;
rangeInfo.Merge(in info);
}
return Result.Success;
}
public void Dispose()
{
BaseFile?.Dispose();
ParentFs?.Dispose();
}
}
}

View File

@ -0,0 +1,288 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSrv.Sf;
using LibHac.Util;
namespace LibHac.FsSrv.Impl
{
internal class FileSystemInterfaceAdapter : IFileSystemSf
{
private ReferenceCountedDisposable<IFileSystem> BaseFileSystem { get; }
private bool IsHostFsRoot { get; }
// In FS, FileSystemInterfaceAdapter is derived from ISharedObject, so that's used for ref-counting when
// creating files and directories. We don't have an ISharedObject, so a self-reference is used instead.
private ReferenceCountedDisposable<FileSystemInterfaceAdapter>.WeakReference _selfReference;
/// <summary>
/// Initializes a new <see cref="FileSystemInterfaceAdapter"/> by creating
/// a new reference to <paramref name="fileSystem"/>.
/// </summary>
/// <param name="fileSystem">The base file system.</param>
/// <param name="isHostFsRoot">Does the base file system come from the root directory of a host file system?</param>
private FileSystemInterfaceAdapter(ReferenceCountedDisposable<IFileSystem> fileSystem,
bool isHostFsRoot = false)
{
BaseFileSystem = fileSystem.AddReference();
IsHostFsRoot = isHostFsRoot;
}
/// <summary>
/// Initializes a new <see cref="FileSystemInterfaceAdapter"/> by moving the file system object.
/// Avoids allocations from incrementing and then decrementing the ref-count.
/// </summary>
/// <param name="fileSystem">The base file system. Will be null upon returning.</param>
/// <param name="isHostFsRoot">Does the base file system come from the root directory of a host file system?</param>
private FileSystemInterfaceAdapter(ref ReferenceCountedDisposable<IFileSystem> fileSystem,
bool isHostFsRoot = false)
{
BaseFileSystem = fileSystem;
fileSystem = null;
IsHostFsRoot = isHostFsRoot;
}
/// <summary>
/// Initializes a new <see cref="FileSystemInterfaceAdapter"/>, creating a copy of the input file system object.
/// </summary>
/// <param name="baseFileSystem">The base file system.</param>
/// <param name="isHostFsRoot">Does the base file system come from the root directory of a host file system?</param>
public static ReferenceCountedDisposable<FileSystemInterfaceAdapter> CreateShared(
ReferenceCountedDisposable<IFileSystem> baseFileSystem, bool isHostFsRoot = false)
{
var adapter = new FileSystemInterfaceAdapter(baseFileSystem, isHostFsRoot);
return ReferenceCountedDisposable<FileSystemInterfaceAdapter>.Create(adapter, out adapter._selfReference);
}
/// <summary>
/// Initializes a new <see cref="FileSystemInterfaceAdapter"/> cast to an <see cref="IFileSystemSf"/>
/// by moving the input file system object. Avoids allocations from incrementing and then decrementing the ref-count.
/// </summary>
/// <param name="baseFileSystem">The base file system. Will be null upon returning.</param>
/// <param name="isHostFsRoot">Does the base file system come from the root directory of a host file system?</param>
public static ReferenceCountedDisposable<IFileSystemSf> CreateSharedSfFileSystem(
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, bool isHostFsRoot = false)
{
var adapter = new FileSystemInterfaceAdapter(ref baseFileSystem, isHostFsRoot);
return ReferenceCountedDisposable<IFileSystemSf>.Create(adapter, out adapter._selfReference);
}
private static ReadOnlySpan<byte> RootDir => new[] { (byte)'/' };
public Result GetImpl(out ReferenceCountedDisposable<IFileSystem> fileSystem)
{
fileSystem = BaseFileSystem.AddReference();
return Result.Success;
}
public Result CreateFile(in Path path, long size, int option)
{
if (size < 0)
return ResultFs.InvalidSize.Log();
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
if (normalizer.Result.IsFailure()) return normalizer.Result;
return BaseFileSystem.Target.CreateFile(normalizer.Path, size, (CreateFileOptions)option);
}
public Result DeleteFile(in Path path)
{
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
if (normalizer.Result.IsFailure()) return normalizer.Result;
return BaseFileSystem.Target.DeleteFile(normalizer.Path);
}
public Result CreateDirectory(in Path path)
{
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
if (normalizer.Result.IsFailure()) return normalizer.Result;
if (StringUtils.Compare(RootDir, normalizer.Path) != 0)
return ResultFs.PathAlreadyExists.Log();
return BaseFileSystem.Target.CreateDirectory(normalizer.Path);
}
public Result DeleteDirectory(in Path path)
{
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
if (normalizer.Result.IsFailure()) return normalizer.Result;
if (StringUtils.Compare(RootDir, normalizer.Path) != 0)
return ResultFs.DirectoryNotDeletable.Log();
return BaseFileSystem.Target.DeleteDirectory(normalizer.Path);
}
public Result DeleteDirectoryRecursively(in Path path)
{
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
if (normalizer.Result.IsFailure()) return normalizer.Result;
if (StringUtils.Compare(RootDir, normalizer.Path) != 0)
return ResultFs.DirectoryNotDeletable.Log();
return BaseFileSystem.Target.DeleteDirectoryRecursively(normalizer.Path);
}
public Result RenameFile(in Path oldPath, in Path newPath)
{
var normalizerOldPath = new PathNormalizer(new U8Span(oldPath.Str), GetPathNormalizerOption());
if (normalizerOldPath.Result.IsFailure()) return normalizerOldPath.Result;
var normalizerNewPath = new PathNormalizer(new U8Span(newPath.Str), GetPathNormalizerOption());
if (normalizerNewPath.Result.IsFailure()) return normalizerNewPath.Result;
return BaseFileSystem.Target.RenameFile(new U8Span(normalizerOldPath.Path),
new U8Span(normalizerNewPath.Path));
}
public Result RenameDirectory(in Path oldPath, in Path newPath)
{
var normalizerOldPath = new PathNormalizer(new U8Span(oldPath.Str), GetPathNormalizerOption());
if (normalizerOldPath.Result.IsFailure()) return normalizerOldPath.Result;
var normalizerNewPath = new PathNormalizer(new U8Span(newPath.Str), GetPathNormalizerOption());
if (normalizerNewPath.Result.IsFailure()) return normalizerNewPath.Result;
if (PathTool.IsSubpath(normalizerOldPath.Path, normalizerNewPath.Path))
return ResultFs.DirectoryNotRenamable.Log();
return BaseFileSystem.Target.RenameDirectory(normalizerOldPath.Path, normalizerNewPath.Path);
}
public Result GetEntryType(out uint entryType, in Path path)
{
entryType = default;
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
if (normalizer.Result.IsFailure()) return normalizer.Result;
ref DirectoryEntryType type = ref Unsafe.As<uint, DirectoryEntryType>(ref entryType);
return BaseFileSystem.Target.GetEntryType(out type, new U8Span(normalizer.Path));
}
public Result OpenFile(out ReferenceCountedDisposable<IFileSf> file, in Path path, uint mode)
{
const int maxTryCount = 2;
file = default;
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
if (normalizer.Result.IsFailure()) return normalizer.Result;
Result rc = Result.Success;
IFile fileInterface = null;
for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
{
rc = BaseFileSystem.Target.OpenFile(out fileInterface, new U8Span(normalizer.Path), (OpenMode)mode);
// Retry on ResultDataCorrupted
if (!ResultFs.DataCorrupted.Includes(rc))
break;
}
if (rc.IsFailure()) return rc;
ReferenceCountedDisposable<FileSystemInterfaceAdapter> selfReference = _selfReference.TryAddReference();
var adapter = new FileInterfaceAdapter(fileInterface, ref selfReference);
file = new ReferenceCountedDisposable<IFileSf>(adapter);
return Result.Success;
}
public Result OpenDirectory(out ReferenceCountedDisposable<IDirectorySf> directory, in Path path, uint mode)
{
const int maxTryCount = 2;
directory = default;
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
if (normalizer.Result.IsFailure()) return normalizer.Result;
Result rc = Result.Success;
IDirectory dirInterface = null;
for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
{
rc = BaseFileSystem.Target.OpenDirectory(out dirInterface, new U8Span(normalizer.Path), (OpenDirectoryMode)mode);
// Retry on ResultDataCorrupted
if (!ResultFs.DataCorrupted.Includes(rc))
break;
}
if (rc.IsFailure()) return rc;
ReferenceCountedDisposable<FileSystemInterfaceAdapter> selfReference = _selfReference.TryAddReference();
var adapter = new DirectoryInterfaceAdapter(dirInterface, ref selfReference);
directory = new ReferenceCountedDisposable<IDirectorySf>(adapter);
return Result.Success;
}
public Result Commit()
{
return BaseFileSystem.Target.Commit();
}
public Result GetFreeSpaceSize(out long freeSpace, in Path path)
{
freeSpace = default;
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
if (normalizer.Result.IsFailure()) return normalizer.Result;
return BaseFileSystem.Target.GetFreeSpaceSize(out freeSpace, normalizer.Path);
}
public Result GetTotalSpaceSize(out long totalSpace, in Path path)
{
totalSpace = default;
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
if (normalizer.Result.IsFailure()) return normalizer.Result;
return BaseFileSystem.Target.GetTotalSpaceSize(out totalSpace, normalizer.Path);
}
public Result CleanDirectoryRecursively(in Path path)
{
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
if (normalizer.Result.IsFailure()) return normalizer.Result;
return BaseFileSystem.Target.CleanDirectoryRecursively(normalizer.Path);
}
public Result GetFileTimeStampRaw(out FileTimeStampRaw timeStamp, in Path path)
{
timeStamp = default;
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
if (normalizer.Result.IsFailure()) return normalizer.Result;
return BaseFileSystem.Target.GetFileTimeStampRaw(out timeStamp, normalizer.Path);
}
public Result QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, int queryId, in Path path)
{
return BaseFileSystem.Target.QueryEntry(outBuffer, inBuffer, (QueryId)queryId, new U8Span(path.Str));
}
public void Dispose()
{
BaseFileSystem?.Dispose();
}
private PathNormalizer.Option GetPathNormalizerOption()
{
return IsHostFsRoot ? PathNormalizer.Option.PreserveUnc : PathNormalizer.Option.None;
}
}
}

View File

@ -0,0 +1,101 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Fs;
using LibHac.FsSrv.Sf;
namespace LibHac.FsSrv.Impl
{
internal class StorageInterfaceAdapter : IStorageSf
{
private ReferenceCountedDisposable<IStorage> BaseStorage { get; }
public StorageInterfaceAdapter(ReferenceCountedDisposable<IStorage> baseStorage)
{
BaseStorage = baseStorage.AddReference();
}
public Result Read(long offset, Span<byte> destination)
{
const int maxTryCount = 2;
if (offset < 0)
return ResultFs.InvalidOffset.Log();
if (destination.Length < 0)
return ResultFs.InvalidSize.Log();
Result rc = Result.Success;
for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
{
rc = BaseStorage.Target.Read(offset, destination);
// Retry on ResultDataCorrupted
if (!ResultFs.DataCorrupted.Includes(rc))
break;
}
return rc;
}
public Result Write(long offset, ReadOnlySpan<byte> source)
{
if (offset < 0)
return ResultFs.InvalidOffset.Log();
if (source.Length < 0)
return ResultFs.InvalidSize.Log();
// Note: Thread priority is temporarily when writing in FS
return BaseStorage.Target.Write(offset, source);
}
public Result Flush()
{
return BaseStorage.Target.Flush();
}
public Result SetSize(long size)
{
if (size < 0)
return ResultFs.InvalidSize.Log();
return BaseStorage.Target.SetSize(size);
}
public Result GetSize(out long size)
{
return BaseStorage.Target.GetSize(out size);
}
public Result OperateRange(out QueryRangeInfo rangeInfo, int operationId, long offset, long size)
{
rangeInfo = new QueryRangeInfo();
if (operationId == (int)OperationId.InvalidateCache)
{
Result rc = BaseStorage.Target.OperateRange(Span<byte>.Empty, OperationId.InvalidateCache, offset, size,
ReadOnlySpan<byte>.Empty);
if (rc.IsFailure()) return rc;
}
else if (operationId == (int)OperationId.QueryRange)
{
Unsafe.SkipInit(out QueryRangeInfo info);
Result rc = BaseStorage.Target.OperateRange(SpanHelpers.AsByteSpan(ref info), OperationId.QueryRange,
offset, size, ReadOnlySpan<byte>.Empty);
if (rc.IsFailure()) return rc;
rangeInfo.Merge(in info);
}
return Result.Success;
}
public void Dispose()
{
BaseStorage?.Dispose();
}
}
}

View File

@ -22,7 +22,7 @@ namespace LibHac.FsSrv
private SemaphoreAdaptor AocMountCountSemaphore { get; }
private SemaphoreAdaptor RomMountCountSemaphore { get; }
public NcaFileSystemService(NcaFileSystemServiceImpl serviceImpl, ulong processId)
private NcaFileSystemService(NcaFileSystemServiceImpl serviceImpl, ulong processId)
{
ServiceImpl = serviceImpl;
ProcessId = processId;
@ -30,19 +30,33 @@ namespace LibHac.FsSrv
RomMountCountSemaphore = new SemaphoreAdaptor(RomSemaphoreCount, RomSemaphoreCount);
}
public Result OpenFileSystemWithPatch(out ReferenceCountedDisposable<IFileSystem> fileSystem,
public static ReferenceCountedDisposable<NcaFileSystemService> Create(NcaFileSystemServiceImpl serviceImpl,
ulong processId)
{
// Create the service
var ncaService = new NcaFileSystemService(serviceImpl, processId);
// Wrap the service in a ref-counter and give the service a weak self-reference
var sharedService = new ReferenceCountedDisposable<NcaFileSystemService>(ncaService);
ncaService.SelfReference =
new ReferenceCountedDisposable<NcaFileSystemService>.WeakReference(sharedService);
return sharedService;
}
public Result OpenFileSystemWithPatch(out ReferenceCountedDisposable<IFileSystemSf> fileSystem,
ProgramId programId, FileSystemProxyType fsType)
{
throw new NotImplementedException();
}
public Result OpenCodeFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
public Result OpenCodeFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem,
out CodeVerificationData verificationData, in FspPath path, ProgramId programId)
{
throw new NotImplementedException();
}
public Result OpenDataFileSystemByCurrentProcess(out ReferenceCountedDisposable<IFileSystem> fileSystem)
public Result OpenDataFileSystemByCurrentProcess(out ReferenceCountedDisposable<IFileSystemSf> fileSystem)
{
throw new NotImplementedException();
}
@ -53,17 +67,17 @@ namespace LibHac.FsSrv
throw new NotImplementedException();
}
public Result OpenDataStorageByCurrentProcess(out ReferenceCountedDisposable<IStorage> storage)
public Result OpenDataStorageByCurrentProcess(out ReferenceCountedDisposable<IStorageSf> storage)
{
throw new NotImplementedException();
}
public Result OpenDataStorageByProgramId(out ReferenceCountedDisposable<IStorage> storage, ProgramId programId)
public Result OpenDataStorageByProgramId(out ReferenceCountedDisposable<IStorageSf> storage, ProgramId programId)
{
throw new NotImplementedException();
}
public Result OpenFileSystemWithId(out ReferenceCountedDisposable<IFileSystem> fileSystem, in FspPath path,
public Result OpenFileSystemWithId(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, in FspPath path,
ulong id, FileSystemProxyType fsType)
{
fileSystem = default;
@ -117,33 +131,44 @@ namespace LibHac.FsSrv
var normalizer = new PathNormalizer(path, GetPathNormalizerOptions(path));
if (normalizer.Result.IsFailure()) return normalizer.Result;
rc = ServiceImpl.OpenFileSystem(out ReferenceCountedDisposable<IFileSystem> baseFs, out _, path, fsType,
canMountSystemDataPrivate, id);
if (rc.IsFailure()) return rc;
ReferenceCountedDisposable<IFileSystem> fs = null;
fileSystem = baseFs;
return Result.Success;
try
{
rc = ServiceImpl.OpenFileSystem(out fs, out _, path, fsType,
canMountSystemDataPrivate, id);
if (rc.IsFailure()) return rc;
// Create an SF adapter for the file system
fileSystem = FileSystemInterfaceAdapter.CreateSharedSfFileSystem(ref fs);
return Result.Success;
}
finally
{
fs?.Dispose();
}
}
public Result OpenDataFileSystemByProgramId(out ReferenceCountedDisposable<IFileSystem> fileSystem,
public Result OpenDataFileSystemByProgramId(out ReferenceCountedDisposable<IFileSystemSf> fileSystem,
ProgramId programId)
{
throw new NotImplementedException();
}
public Result OpenDataStorageByDataId(out ReferenceCountedDisposable<IStorage> storage, DataId dataId,
public Result OpenDataStorageByDataId(out ReferenceCountedDisposable<IStorageSf> storage, DataId dataId,
StorageId storageId)
{
throw new NotImplementedException();
}
public Result OpenDataFileSystemWithProgramIndex(out ReferenceCountedDisposable<IFileSystem> fileSystem,
public Result OpenDataFileSystemWithProgramIndex(out ReferenceCountedDisposable<IFileSystemSf> fileSystem,
byte programIndex)
{
throw new NotImplementedException();
}
public Result OpenDataStorageWithProgramIndex(out ReferenceCountedDisposable<IStorage> storage,
public Result OpenDataStorageWithProgramIndex(out ReferenceCountedDisposable<IStorageSf> storage,
byte programIndex)
{
throw new NotImplementedException();
@ -165,7 +190,7 @@ namespace LibHac.FsSrv
throw new NotImplementedException();
}
public Result OpenContentStorageFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
public Result OpenContentStorageFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem,
ContentStorageId contentStorageId)
{
fileSystem = default;
@ -179,12 +204,22 @@ namespace LibHac.FsSrv
if (!accessibility.CanRead || !accessibility.CanWrite)
return ResultFs.PermissionDenied.Log();
rc = ServiceImpl.OpenContentStorageFileSystem(out ReferenceCountedDisposable<IFileSystem> contentFs,
contentStorageId);
if (rc.IsFailure()) return rc;
ReferenceCountedDisposable<IFileSystem> fs = null;
fileSystem = contentFs;
return Result.Success;
try
{
rc = ServiceImpl.OpenContentStorageFileSystem(out fs, contentStorageId);
if (rc.IsFailure()) return rc;
// Create an SF adapter for the file system
fileSystem = FileSystemInterfaceAdapter.CreateSharedSfFileSystem(ref fs);
return Result.Success;
}
finally
{
fs?.Dispose();
}
}
public Result RegisterExternalKey(in RightsId rightsId, in AccessKey accessKey)
@ -225,7 +260,7 @@ namespace LibHac.FsSrv
throw new NotImplementedException();
}
public Result OpenRegisteredUpdatePartition(out ReferenceCountedDisposable<IFileSystem> fileSystem)
public Result OpenRegisteredUpdatePartition(out ReferenceCountedDisposable<IFileSystemSf> fileSystem)
{
throw new NotImplementedException();
}
@ -282,11 +317,6 @@ namespace LibHac.FsSrv
return OpenDataStorageCore(out storage, out ncaHeaderDigest, id, storageId);
}
internal void SetSelfReference(ReferenceCountedDisposable<NcaFileSystemService> reference)
{
SelfReference = new ReferenceCountedDisposable<NcaFileSystemService>.WeakReference(reference);
}
private Result TryAcquireAddOnContentOpenCountSemaphore(out IUniqueLock semaphoreLock)
{
throw new NotImplementedException();

View File

@ -0,0 +1,10 @@
using System;
namespace LibHac.FsSrv.Sf
{
public interface IDirectorySf : IDisposable
{
Result Read(out long entriesRead, Span<byte> entryBuffer);
Result GetEntryCount(out long entryCount);
}
}

View File

@ -0,0 +1,15 @@
using System;
using LibHac.Fs;
namespace LibHac.FsSrv.Sf
{
public interface IFileSf : IDisposable
{
Result Read(out long bytesRead, long offset, Span<byte> destination, ReadOption option);
Result Write(long offset, ReadOnlySpan<byte> source, WriteOption option);
Result Flush();
Result SetSize(long size);
Result GetSize(out long size);
Result OperateRange(out QueryRangeInfo rangeInfo, int operationId, long offset, long size);
}
}

View File

@ -0,0 +1,27 @@
using System;
using LibHac.Fs;
using LibHac.Fs.Fsa;
namespace LibHac.FsSrv.Sf
{
public interface IFileSystemSf : IDisposable
{
Result GetImpl(out ReferenceCountedDisposable<IFileSystem> fileSystem);
Result CreateFile(in Path path, long size, int option);
Result DeleteFile(in Path path);
Result CreateDirectory(in Path path);
Result DeleteDirectory(in Path path);
Result DeleteDirectoryRecursively(in Path path);
Result RenameFile(in Path oldPath, in Path newPath);
Result RenameDirectory(in Path oldPath, in Path newPath);
Result GetEntryType(out uint entryType, in Path path);
Result OpenFile(out ReferenceCountedDisposable<IFileSf> file, in Path path, uint mode);
Result OpenDirectory(out ReferenceCountedDisposable<IDirectorySf> directory, in Path path, uint mode);
Result Commit();
Result GetFreeSpaceSize(out long freeSpace, in Path path);
Result GetTotalSpaceSize(out long totalSpace, in Path path);
Result CleanDirectoryRecursively(in Path path);
Result GetFileTimeStampRaw(out FileTimeStampRaw timeStamp, in Path path);
Result QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, int queryId, in Path path);
}
}

View File

@ -1,12 +1,11 @@
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.Ncm;
namespace LibHac.FsSrv.Sf
{
public interface IFileSystemProxyForLoader
{
Result OpenCodeFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
Result OpenCodeFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem,
out CodeVerificationData verificationData, in FspPath path, ProgramId programId);
Result IsArchivedProgram(out bool isArchived, ulong processId);

View File

@ -0,0 +1,15 @@
using System;
using LibHac.Fs;
namespace LibHac.FsSrv.Sf
{
public interface IStorageSf : IDisposable
{
Result Read(long offset, Span<byte> destination);
Result Write(long offset, ReadOnlySpan<byte> source);
Result Flush();
Result SetSize(long size);
Result GetSize(out long size);
Result OperateRange(out QueryRangeInfo rangeInfo, int operationId, long offset, long size);
}
}

View File

@ -1,6 +1,8 @@
namespace LibHac.FsSrv.Sf
using System;
namespace LibHac.FsSrv.Sf
{
public interface IWiper
public interface IWiper : IDisposable
{
public Result Startup(out long spaceToWipe);
public Result Process(out long remainingSpaceToWipe);

View File

@ -0,0 +1,21 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Fs;
namespace LibHac.FsSrv.Sf
{
[StructLayout(LayoutKind.Sequential, Size = PathTool.EntryNameLengthMax + 1)]
public readonly struct Path
{
#if DEBUG
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding000;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding100;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding200;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly byte Padding300;
#endif
public ReadOnlySpan<byte> Str => SpanHelpers.AsReadOnlyByteSpan(in this);
}
}

View File

@ -0,0 +1,15 @@
using System;
using LibHac.Fs.Impl;
namespace LibHac.FsSrv.Storage
{
internal interface IGameCardDeviceManager
{
Result AcquireReadLock(out UniqueLock locker, uint handle);
Result AcquireReadLockSecureMode(out UniqueLock locker, ref uint handle, ReadOnlySpan<byte> cardDeviceId, ReadOnlySpan<byte> cardImageHash);
Result AcquireWriteLock(out SharedLock locker);
Result HandleGameCardAccessResult(Result result);
Result GetHandle(out uint handle);
bool IsSecureMode();
}
}

View File

@ -0,0 +1,9 @@
using System;
namespace LibHac.FsSrv.Storage
{
public interface IGameCardKeyManager
{
void PresetInternalKeys(ReadOnlySpan<byte> gameCardKey, ReadOnlySpan<byte> gameCardCertificate);
}
}

View File

@ -0,0 +1,12 @@
using LibHac.Fs;
namespace LibHac.FsSrv.Storage
{
internal interface ISdmmcDeviceManager
{
Result Lock(out object locker, uint handle);
IStorage GetStorage();
SdmmcPort GetPortId();
Result NotifyCloseStorageDevice(uint handle);
}
}

View File

@ -0,0 +1,18 @@
using System;
using LibHac.Fs;
namespace LibHac.FsSrv.Storage.Sf
{
public interface IStorageDevice : IDisposable
{
Result GetHandle(out uint handle);
Result IsHandleValid(out bool isValid);
Result OpenOperator(out ReferenceCountedDisposable<IStorageDeviceOperator> deviceOperator);
Result Read(long offset, Span<byte> destination);
Result Write(long offset, ReadOnlySpan<byte> source);
Result Flush();
Result SetSize(long size);
Result GetSize(out long size);
Result OperateRange(out QueryRangeInfo rangeInfo, int operationId, long offset, long size);
}
}

View File

@ -0,0 +1,21 @@
using System;
using LibHac.Fs;
using LibHac.FsSrv.Sf;
namespace LibHac.FsSrv.Storage.Sf
{
public interface IStorageDeviceManager : IDisposable
{
Result IsInserted(out bool isInserted);
Result IsHandleValid(out bool isValid, uint handle);
Result OpenDetectionEvent(out ReferenceCountedDisposable<IEventNotifier> eventNotifier);
Result OpenOperator(out ReferenceCountedDisposable<IStorageDeviceOperator> deviceOperator);
Result OpenDevice(out ReferenceCountedDisposable<IStorageDevice> storageDevice, ulong attribute);
Result OpenStorage(out ReferenceCountedDisposable<IStorage> storage, ulong attribute);
Result PutToSleep();
Result Awaken();
Result Initialize();
Result Shutdown();
Result Invalidate();
}
}

View File

@ -0,0 +1,14 @@
using System;
namespace LibHac.FsSrv.Storage.Sf
{
public interface IStorageDeviceOperator : IDisposable
{
Result Operate(uint operationId);
Result OperateIn(ReadOnlySpan<byte> buffer, long offset, long size, uint operationId);
Result OperateOut(out long bytesWritten, Span<byte> buffer, long offset, long size, uint operationId);
Result OperateOut2(out long bytesWrittenBuffer1, Span<byte> buffer1, out long bytesWrittenBuffer2, Span<byte> buffer2, uint operationId);
Result OperateInOut(out long bytesWritten, Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, long offset, long size, uint operationId);
Result OperateIn2Out(out long bytesWritten, Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer1, ReadOnlySpan<byte> inBuffer2, long offset, long size, uint operationId);
}
}

View File

@ -61,7 +61,7 @@ namespace LibHac.FsSystem
if (PathTool.IsSubpath(normalizedPath1, normalizedPath2))
{
return ResultFs.DestinationIsSubPathOfSource.Log();
return ResultFs.DirectoryNotRenamable.Log();
}
return Result.Success;

View File

@ -347,7 +347,7 @@ namespace LibHac.FsSystem.Save
if (PathTools.IsSubPath(srcPath, dstPath))
{
return ResultFs.DestinationIsSubPathOfSource.Log();
return ResultFs.DirectoryNotRenamable.Log();
}
if (oldKey.Parent != newKey.Parent)

View File

@ -8,6 +8,7 @@
#nullable enable
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace LibHac
@ -26,14 +27,14 @@ namespace LibHac
/// released through either of the following actions:</para>
///
/// <list type="bullet">
/// <item>The reference is explicitly released by a call to <see cref="Dispose"/>.</item>
/// <item>The reference is explicitly released by a call to <see cref="Dispose()"/>.</item>
/// <item>The reference is no longer in use by managed code and gets reclaimed by the garbage collector.</item>
/// </list>
///
/// <para>While each instance of <see cref="ReferenceCountedDisposable{T}"/> should be explicitly disposed when
/// the object is no longer needed by the code owning the reference, this implementation will not leak resources
/// in the event one or more callers fail to do so. When all references to an object are explicitly released
/// (i.e. by calling <see cref="Dispose"/>), the target object will itself be deterministically released by a
/// (i.e. by calling <see cref="Dispose()"/>), the target object will itself be deterministically released by a
/// call to <see cref="IDisposable.Dispose"/> when the last reference to it is released. However, in the event
/// one or more references is not explicitly released, the underlying object will still become eligible for
/// non-deterministic release (i.e. finalization) as soon as each reference to it is released by one of the
@ -65,7 +66,7 @@ namespace LibHac
/// </summary>
/// <remarks>
/// <para>This value is only cleared in order to support cases where one or more references is garbage
/// collected without having <see cref="Dispose"/> called.</para>
/// collected without having <see cref="Dispose()"/> called.</para>
/// </remarks>
private T? _instance;
@ -110,16 +111,37 @@ namespace LibHac
/// Gets the target object.
/// </summary>
/// <remarks>
/// <para>This call is not valid after <see cref="Dispose"/> is called. If this property or the target
/// object is used concurrently with a call to <see cref="Dispose"/>, it is possible for the code to be
/// <para>This call is not valid after <see cref="Dispose()"/> is called. If this property or the target
/// object is used concurrently with a call to <see cref="Dispose()"/>, it is possible for the code to be
/// using a disposed object. After the current instance is disposed, this property throws
/// <see cref="ObjectDisposedException"/>. However, the exact time when this property starts throwing after
/// <see cref="Dispose"/> is called is unspecified; code is expected to not use this property or the object
/// it returns after any code invokes <see cref="Dispose"/>.</para>
/// <see cref="Dispose()"/> is called is unspecified; code is expected to not use this property or the object
/// it returns after any code invokes <see cref="Dispose()"/>.</para>
/// </remarks>
/// <value>The target object.</value>
public T Target => _instance ?? throw new ObjectDisposedException(nameof(ReferenceCountedDisposable<T>));
/// <summary>
/// Initializes a new reference counting wrapper around an <see cref="IDisposable"/> object.
/// Returns a reference counting wrapper of the base type of the input object, and creates a
/// <see cref="WeakReference"/> of the object's derived type.
/// </summary>
/// <typeparam name="TDerived">The derived type of the object to be wrapped.</typeparam>
/// <param name="instance">The object owned by this wrapper.</param>
/// <param name="derivedWeakReference">The weak reference of the derived type.</param>
/// <returns></returns>
public static ReferenceCountedDisposable<T> Create<TDerived>(TDerived instance,
out ReferenceCountedDisposable<TDerived>.WeakReference derivedWeakReference)
where TDerived : class, IDisposable, T
{
var baseStrongRef = new ReferenceCountedDisposable<T>(instance);
derivedWeakReference =
new ReferenceCountedDisposable<TDerived>.WeakReference(instance, baseStrongRef._boxedReferenceCount);
return baseStrongRef;
}
/// <summary>
/// Increments the reference count for the disposable object, and returns a new disposable reference to it.
/// </summary>
@ -272,6 +294,11 @@ namespace LibHac
}
}
// Print info on non-disposed references in debug mode
#if DEBUG
~ReferenceCountedDisposable() => Dispose(false);
#endif
/// <summary>
/// Releases the current reference, causing the underlying object to be disposed if this was the last
/// reference.
@ -283,26 +310,42 @@ namespace LibHac
/// </remarks>
public void Dispose()
{
T? instanceToDispose = null;
lock (_boxedReferenceCount)
Dispose(true);
#if DEBUG
GC.SuppressFinalize(this);
#endif
}
private void Dispose(bool disposing)
{
if (disposing)
{
if (_instance == null)
T? instanceToDispose = null;
lock (_boxedReferenceCount)
{
// Already disposed; allow multiple without error.
return;
if (_instance == null)
{
// Already disposed; allow multiple without error.
return;
}
_boxedReferenceCount.Value--;
if (_boxedReferenceCount.Value == 0)
{
instanceToDispose = _instance;
}
// Ensure multiple calls to Dispose for this instance are a NOP.
_instance = null;
}
_boxedReferenceCount.Value--;
if (_boxedReferenceCount.Value == 0)
{
instanceToDispose = _instance;
}
// Ensure multiple calls to Dispose for this instance are a NOP.
_instance = null;
instanceToDispose?.Dispose();
}
else
{
Trace.WriteLine($"Failed to dispose object with type {GetType().FullName}.");
}
instanceToDispose?.Dispose();
}
/// <summary>
@ -349,6 +392,24 @@ namespace LibHac
_boxedReferenceCount = referenceCount;
}
/// <summary>
///
/// </summary>
/// <param name="instance"></param>
/// <param name="referenceCount"></param>
internal WeakReference(T instance, StrongBox<int> referenceCount)
{
// This constructor is meant for internal use when creating a weak reference
// to an instance that is already wrapped by a ReferenceCountedDisposable.
if (instance == null)
{
throw new ArgumentNullException(nameof(instance));
}
_weakInstance = new WeakReference<T>(instance);
_boxedReferenceCount = referenceCount;
}
/// <summary>
/// Increments the reference count for the disposable object, and returns a new disposable reference to
/// it.