mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2025-02-09 13:14:46 +01:00
Add more NcaFileSystemService functions
This commit is contained in:
parent
6b6a31dcea
commit
0efaafd101
@ -7,20 +7,10 @@ namespace LibHac.FsSrv.Impl
|
||||
{
|
||||
public class AsynchronousAccessFileSystem : ForwardingFileSystem
|
||||
{
|
||||
public AsynchronousAccessFileSystem(ReferenceCountedDisposable<IFileSystem> baseFileSystem) : base(
|
||||
baseFileSystem)
|
||||
{ }
|
||||
|
||||
protected AsynchronousAccessFileSystem(ref ReferenceCountedDisposable<IFileSystem> baseFileSystem) : base(
|
||||
ref baseFileSystem)
|
||||
{ }
|
||||
|
||||
public static ReferenceCountedDisposable<IFileSystem> CreateShared(
|
||||
ReferenceCountedDisposable<IFileSystem> baseFileSystem)
|
||||
{
|
||||
return new ReferenceCountedDisposable<IFileSystem>(new AsynchronousAccessFileSystem(baseFileSystem));
|
||||
}
|
||||
|
||||
public static ReferenceCountedDisposable<IFileSystem> CreateShared(
|
||||
ref ReferenceCountedDisposable<IFileSystem> fileSystem)
|
||||
{
|
||||
|
58
src/LibHac/FsSrv/Impl/DeepRetryFileSystem.cs
Normal file
58
src/LibHac/FsSrv/Impl/DeepRetryFileSystem.cs
Normal file
@ -0,0 +1,58 @@
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSystem;
|
||||
|
||||
namespace LibHac.FsSrv.Impl
|
||||
{
|
||||
public class DeepRetryFileSystem : ForwardingFileSystem
|
||||
{
|
||||
private ReferenceCountedDisposable<DeepRetryFileSystem>.WeakReference SelfReference { get; set; }
|
||||
private ReferenceCountedDisposable<IRomFileSystemAccessFailureManager> AccessFailureManager { get; set; }
|
||||
|
||||
protected DeepRetryFileSystem(ref ReferenceCountedDisposable<IFileSystem> baseFileSystem,
|
||||
ref ReferenceCountedDisposable<IRomFileSystemAccessFailureManager> accessFailureManager) : base(
|
||||
ref baseFileSystem)
|
||||
{
|
||||
AccessFailureManager = Shared.Move(ref accessFailureManager);
|
||||
}
|
||||
|
||||
public static ReferenceCountedDisposable<IFileSystem> CreateShared(
|
||||
ref ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
ref ReferenceCountedDisposable<IRomFileSystemAccessFailureManager> accessFailureManager)
|
||||
{
|
||||
ReferenceCountedDisposable<DeepRetryFileSystem> sharedRetryFileSystem = null;
|
||||
try
|
||||
{
|
||||
var retryFileSystem = new DeepRetryFileSystem(ref fileSystem, ref accessFailureManager);
|
||||
sharedRetryFileSystem = new ReferenceCountedDisposable<DeepRetryFileSystem>(retryFileSystem);
|
||||
|
||||
retryFileSystem.SelfReference =
|
||||
new ReferenceCountedDisposable<DeepRetryFileSystem>.WeakReference(sharedRetryFileSystem);
|
||||
|
||||
return sharedRetryFileSystem.AddReference<IFileSystem>();
|
||||
}
|
||||
finally
|
||||
{
|
||||
sharedRetryFileSystem?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
AccessFailureManager?.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
// ReSharper disable once RedundantOverriddenMember
|
||||
protected override Result DoOpenFile(out IFile file, U8Span path, OpenMode mode)
|
||||
{
|
||||
// Todo: Implement
|
||||
return base.DoOpenFile(out file, path, mode);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,17 +1,18 @@
|
||||
using LibHac.Fs;
|
||||
using System;
|
||||
using LibHac.Fs;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.Ncm;
|
||||
|
||||
namespace LibHac.FsSrv
|
||||
namespace LibHac.FsSrv.Impl
|
||||
{
|
||||
public interface IRomFileSystemAccessFailureManager
|
||||
public interface IRomFileSystemAccessFailureManager : IDisposable
|
||||
{
|
||||
Result OpenDataStorageCore(out ReferenceCountedDisposable<IStorage> storage, out Hash ncaHeaderDigest, ulong id,
|
||||
StorageId storageId);
|
||||
|
||||
Result HandleResolubleAccessFailure(out bool wasDeferred, in Result nonDeferredResult);
|
||||
Result HandleResolubleAccessFailure(out bool wasDeferred, Result resultForNoFailureDetected);
|
||||
void IncrementRomFsRemountForDataCorruptionCount();
|
||||
void IncrementRomFsUnrecoverableDataCorruptionByRemountCount();
|
||||
void IncrementRomFsRecoveredByInvalidateCacheCount();
|
||||
}
|
||||
}
|
||||
}
|
@ -108,28 +108,28 @@ namespace LibHac.FsSrv.Impl
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result ResolveProgramPath(out Path path, ProgramId programId, StorageId storageId)
|
||||
public virtual Result ResolveProgramPath(out Path path, ulong id, StorageId storageId)
|
||||
{
|
||||
Path.InitEmpty(out path);
|
||||
|
||||
Result rc = GetLocationResolver(out LocationResolver resolver, storageId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = resolver.ResolveProgramPath(out path, programId);
|
||||
rc = resolver.ResolveProgramPath(out path, new ProgramId(id));
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
PathUtility.Replace(path.StrMutable, (byte)'\\', (byte)'/');
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result ResolveRomPath(out Path path, ProgramId programId, StorageId storageId)
|
||||
public Result ResolveRomPath(out Path path, ulong id, StorageId storageId)
|
||||
{
|
||||
Path.InitEmpty(out path);
|
||||
|
||||
Result rc = GetLocationResolver(out LocationResolver resolver, storageId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = resolver.ResolveProgramPath(out path, programId);
|
||||
rc = resolver.ResolveProgramPath(out path, new ProgramId(id));
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
PathUtility.Replace(path.StrMutable, (byte)'\\', (byte)'/');
|
||||
@ -159,7 +159,7 @@ namespace LibHac.FsSrv.Impl
|
||||
return resolver.ResolveDataPath(out path, dataId);
|
||||
}
|
||||
|
||||
public Result ResolveRegisteredProgramPath(out Path path, ProgramId programId)
|
||||
public Result ResolveRegisteredProgramPath(out Path path, ulong id)
|
||||
{
|
||||
Path.InitEmpty(out path);
|
||||
|
||||
@ -168,11 +168,11 @@ namespace LibHac.FsSrv.Impl
|
||||
|
||||
using (resolver)
|
||||
{
|
||||
return resolver.ResolveProgramPath(out path, programId);
|
||||
return resolver.ResolveProgramPath(out path, new ProgramId(id));
|
||||
}
|
||||
}
|
||||
|
||||
public Result ResolveRegisteredHtmlDocumentPath(out Path path, ProgramId programId)
|
||||
public Result ResolveRegisteredHtmlDocumentPath(out Path path, ulong id)
|
||||
{
|
||||
Path.InitEmpty(out path);
|
||||
|
||||
@ -181,7 +181,7 @@ namespace LibHac.FsSrv.Impl
|
||||
|
||||
using (resolver)
|
||||
{
|
||||
return resolver.ResolveHtmlDocumentPath(out path, programId);
|
||||
return resolver.ResolveHtmlDocumentPath(out path, new ProgramId(id));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,17 +1,21 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSrv.Impl;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.Lr;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Spl;
|
||||
using LibHac.Util;
|
||||
using Path = LibHac.Lr.Path;
|
||||
|
||||
namespace LibHac.FsSrv
|
||||
{
|
||||
internal class NcaFileSystemService : IRomFileSystemAccessFailureManager, IDisposable
|
||||
internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
||||
{
|
||||
private const int AocSemaphoreCount = 128;
|
||||
private const int RomSemaphoreCount = 10;
|
||||
@ -44,10 +48,125 @@ namespace LibHac.FsSrv
|
||||
return sharedService;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
AocMountCountSemaphore?.Dispose();
|
||||
RomMountCountSemaphore?.Dispose();
|
||||
}
|
||||
|
||||
public Result OpenFileSystemWithPatch(out ReferenceCountedDisposable<IFileSystemSf> fileSystem,
|
||||
ProgramId programId, FileSystemProxyType fsType)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
fileSystem = default;
|
||||
|
||||
const StorageType storageFlag = StorageType.All;
|
||||
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(storageFlag);
|
||||
|
||||
// Get the program info for the caller and verify permissions
|
||||
Result rc = GetProgramInfo(out ProgramInfo callerProgramInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (fsType != FileSystemProxyType.Manual)
|
||||
{
|
||||
if (fsType == FileSystemProxyType.Logo || fsType == FileSystemProxyType.Control)
|
||||
return ResultFs.NotImplemented.Log();
|
||||
else
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
}
|
||||
|
||||
Accessibility accessibility =
|
||||
callerProgramInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountContentManual);
|
||||
|
||||
if (!accessibility.CanRead)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
// Get the program info for the owner of the file system being opened
|
||||
rc = GetProgramInfoByProgramId(out ProgramInfo ownerProgramInfo, programId.Value);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Try to find the path to the original version of the file system
|
||||
Result originalResult = ServiceImpl.ResolveApplicationHtmlDocumentPath(out Path originalPath,
|
||||
new Ncm.ApplicationId(programId.Value), ownerProgramInfo.StorageId);
|
||||
|
||||
// The file system might have a patch version with no original version, so continue if not found
|
||||
if (originalResult.IsFailure() && !ResultLr.HtmlDocumentNotFound.Includes(originalResult))
|
||||
return originalResult;
|
||||
|
||||
// Use a separate bool because ref structs can't be used as type parameters
|
||||
bool originalPathNormalizerHasValue = false;
|
||||
PathNormalizer originalPathNormalizer = default;
|
||||
|
||||
// Normalize the original version path if found
|
||||
if (originalResult.IsSuccess())
|
||||
{
|
||||
originalPathNormalizer = new PathNormalizer(originalPath, GetPathNormalizerOptions(originalPath));
|
||||
if (originalPathNormalizer.Result.IsFailure()) return originalPathNormalizer.Result;
|
||||
|
||||
originalPathNormalizerHasValue = true;
|
||||
}
|
||||
|
||||
// Try to find the path to the patch file system
|
||||
Result patchResult = ServiceImpl.ResolveRegisteredHtmlDocumentPath(out Path patchPath, programId.Value);
|
||||
|
||||
ReferenceCountedDisposable<IFileSystem> tempFileSystem = null;
|
||||
ReferenceCountedDisposable<IRomFileSystemAccessFailureManager> accessFailureManager = null;
|
||||
try
|
||||
{
|
||||
if (ResultLr.HtmlDocumentNotFound.Includes(patchResult))
|
||||
{
|
||||
// There must either be an original version or patch version of the file system being opened
|
||||
if (originalResult.IsFailure())
|
||||
return originalResult;
|
||||
|
||||
Assert.AssertTrue(originalPathNormalizerHasValue);
|
||||
|
||||
// There is an original version and no patch version. Open the original directly
|
||||
rc = ServiceImpl.OpenFileSystem(out tempFileSystem, originalPathNormalizer.Path, fsType, programId.Value);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the normalized path to the original file system
|
||||
U8Span normalizedOriginalPath;
|
||||
if (originalPathNormalizerHasValue)
|
||||
{
|
||||
normalizedOriginalPath = originalPathNormalizer.Path;
|
||||
}
|
||||
else
|
||||
{
|
||||
normalizedOriginalPath = U8Span.Empty;
|
||||
}
|
||||
|
||||
// Normalize the path to the patch file system
|
||||
var patchPathNormalizer = new PathNormalizer(patchPath, GetPathNormalizerOptions(patchPath));
|
||||
if (patchPathNormalizer.Result.IsFailure()) return patchPathNormalizer.Result;
|
||||
|
||||
if (patchResult.IsFailure())
|
||||
return patchResult;
|
||||
|
||||
U8Span normalizedPatchPath = patchPathNormalizer.Path;
|
||||
|
||||
// Open the file system using both the original and patch versions
|
||||
rc = ServiceImpl.OpenFileSystemWithPatch(out tempFileSystem, normalizedOriginalPath,
|
||||
normalizedPatchPath, fsType, programId.Value);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
// Add all the file system wrappers
|
||||
tempFileSystem = StorageLayoutTypeSetFileSystem.CreateShared(ref tempFileSystem, storageFlag);
|
||||
tempFileSystem = AsynchronousAccessFileSystem.CreateShared(ref tempFileSystem);
|
||||
|
||||
accessFailureManager = SelfReference.AddReference<IRomFileSystemAccessFailureManager>();
|
||||
tempFileSystem = DeepRetryFileSystem.CreateShared(ref tempFileSystem, ref accessFailureManager);
|
||||
|
||||
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref tempFileSystem);
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
tempFileSystem?.Dispose();
|
||||
accessFailureManager?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public Result OpenCodeFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem,
|
||||
@ -62,7 +181,7 @@ namespace LibHac.FsSrv
|
||||
}
|
||||
|
||||
private Result OpenDataStorageCore(out ReferenceCountedDisposable<IStorage> storage, out Hash ncaHeaderDigest,
|
||||
ulong id, StorageId storageId)
|
||||
ulong id, StorageId storageId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
@ -165,7 +284,44 @@ namespace LibHac.FsSrv
|
||||
public Result OpenDataFileSystemWithProgramIndex(out ReferenceCountedDisposable<IFileSystemSf> fileSystem,
|
||||
byte programIndex)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
fileSystem = default;
|
||||
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Get the program ID of the program with the specified index if in a multi-program application
|
||||
rc = ServiceImpl.ResolveRomReferenceProgramId(out ProgramId targetProgramId, programInfo.ProgramId,
|
||||
programIndex);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
ReferenceCountedDisposable<IFileSystem> tempFileSystem = null;
|
||||
try
|
||||
{
|
||||
rc = OpenDataFileSystemCore(out tempFileSystem, out _, targetProgramId.Value,
|
||||
programInfo.StorageId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Verify the caller has access to the file system
|
||||
if (targetProgramId != programInfo.ProgramId &&
|
||||
!programInfo.AccessControl.HasContentOwnerId(targetProgramId.Value))
|
||||
{
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
}
|
||||
|
||||
tempFileSystem = AsynchronousAccessFileSystem.CreateShared(ref tempFileSystem);
|
||||
if (tempFileSystem is null)
|
||||
return ResultFs.AllocationFailureInAllocateShared.Log();
|
||||
|
||||
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref tempFileSystem);
|
||||
if (fileSystem is null)
|
||||
return ResultFs.AllocationFailureInCreateShared.Log();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
tempFileSystem?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public Result OpenDataStorageWithProgramIndex(out ReferenceCountedDisposable<IStorageSf> storage,
|
||||
@ -176,18 +332,90 @@ namespace LibHac.FsSrv
|
||||
|
||||
public Result GetRightsId(out RightsId rightsId, ProgramId programId, StorageId storageId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
Unsafe.SkipInit(out rightsId);
|
||||
|
||||
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageType.All);
|
||||
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (!programInfo.AccessControl.CanCall(OperationType.GetRightsId))
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
rc = ServiceImpl.ResolveProgramPath(out Path programPath, programId, storageId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var normalizer = new PathNormalizer(programPath, GetPathNormalizerOptions(programPath));
|
||||
if (normalizer.Result.IsFailure()) return normalizer.Result;
|
||||
|
||||
rc = ServiceImpl.GetRightsId(out RightsId tempRightsId, out _, normalizer.Path, programId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rightsId = tempRightsId;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result GetRightsIdAndKeyGenerationByPath(out RightsId rightsId, out byte keyGeneration, in FspPath path)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
Unsafe.SkipInit(out rightsId);
|
||||
Unsafe.SkipInit(out keyGeneration);
|
||||
|
||||
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageType.All);
|
||||
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (!programInfo.AccessControl.CanCall(OperationType.GetRightsId))
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
var normalizer = new PathNormalizer(path, GetPathNormalizerOptions(path));
|
||||
if (normalizer.Result.IsFailure()) return normalizer.Result;
|
||||
|
||||
rc = ServiceImpl.GetRightsId(out RightsId tempRightsId, out byte tempKeyGeneration, normalizer.Path,
|
||||
new ProgramId(ulong.MaxValue));
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rightsId = tempRightsId;
|
||||
keyGeneration = tempKeyGeneration;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private Result OpenDataFileSystemCore(out ReferenceCountedDisposable<IFileSystem> fileSystem, out bool isHostFs,
|
||||
ulong id, StorageId storageId)
|
||||
ulong programId, StorageId storageId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
fileSystem = default;
|
||||
Unsafe.SkipInit(out isHostFs);
|
||||
|
||||
if (Unsafe.IsNullRef(ref isHostFs))
|
||||
return ResultFs.NullptrArgument.Log();
|
||||
|
||||
StorageType storageFlag = ServiceImpl.GetStorageFlag(programId);
|
||||
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(storageFlag);
|
||||
|
||||
Result rc = ServiceImpl.ResolveRomPath(out Path romPath, programId, storageId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var normalizer = new PathNormalizer(romPath, GetPathNormalizerOptions(romPath));
|
||||
if (normalizer.Result.IsFailure()) return normalizer.Result;
|
||||
|
||||
isHostFs = IsHostFs(romPath);
|
||||
|
||||
ReferenceCountedDisposable<IFileSystem> tempFileSystem = null;
|
||||
try
|
||||
{
|
||||
rc = ServiceImpl.OpenDataFileSystem(out tempFileSystem, normalizer.Path, FileSystemProxyType.Rom,
|
||||
programId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
tempFileSystem = StorageLayoutTypeSetFileSystem.CreateShared(ref tempFileSystem, storageFlag);
|
||||
|
||||
Shared.Move(out fileSystem, ref tempFileSystem);
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
tempFileSystem?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public Result OpenContentStorageFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem,
|
||||
@ -195,6 +423,9 @@ namespace LibHac.FsSrv
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
StorageType storageFlag = contentStorageId == ContentStorageId.System ? StorageType.Bis : StorageType.All;
|
||||
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(storageFlag);
|
||||
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
@ -204,21 +435,24 @@ namespace LibHac.FsSrv
|
||||
if (!accessibility.CanRead || !accessibility.CanWrite)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
ReferenceCountedDisposable<IFileSystem> fs = null;
|
||||
ReferenceCountedDisposable<IFileSystem> tempFileSystem = null;
|
||||
|
||||
try
|
||||
{
|
||||
rc = ServiceImpl.OpenContentStorageFileSystem(out fs, contentStorageId);
|
||||
rc = ServiceImpl.OpenContentStorageFileSystem(out tempFileSystem, contentStorageId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
tempFileSystem = StorageLayoutTypeSetFileSystem.CreateShared(ref tempFileSystem, storageFlag);
|
||||
tempFileSystem = AsynchronousAccessFileSystem.CreateShared(ref tempFileSystem);
|
||||
|
||||
// Create an SF adapter for the file system
|
||||
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref fs);
|
||||
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref tempFileSystem);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
fs?.Dispose();
|
||||
tempFileSystem?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,12 +491,60 @@ namespace LibHac.FsSrv
|
||||
|
||||
public Result RegisterUpdatePartition()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (!programInfo.AccessControl.CanCall(OperationType.RegisterUpdatePartition))
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
rc = ServiceImpl.ResolveRomPath(out Path romPath, programInfo.ProgramIdValue, programInfo.StorageId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var normalizer = new PathNormalizer(romPath, GetPathNormalizerOptions(romPath));
|
||||
if (normalizer.Result.IsFailure()) return normalizer.Result;
|
||||
|
||||
return ServiceImpl.RegisterUpdatePartition(programInfo.ProgramIdValue, normalizer.Path);
|
||||
}
|
||||
|
||||
public Result OpenRegisteredUpdatePartition(out ReferenceCountedDisposable<IFileSystemSf> fileSystem)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
fileSystem = default;
|
||||
|
||||
var storageFlag = StorageType.All;
|
||||
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(storageFlag);
|
||||
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
Accessibility accessibility =
|
||||
programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountRegisteredUpdatePartition);
|
||||
if (!accessibility.CanRead)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
ReferenceCountedDisposable<IFileSystem> tempFileSystem = null;
|
||||
try
|
||||
{
|
||||
rc = ServiceImpl.OpenRegisteredUpdatePartition(out tempFileSystem);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
tempFileSystem = StorageLayoutTypeSetFileSystem.CreateShared(ref tempFileSystem, storageFlag);
|
||||
if (tempFileSystem is null)
|
||||
return ResultFs.AllocationFailureInAllocateShared.Log();
|
||||
|
||||
tempFileSystem = AsynchronousAccessFileSystem.CreateShared(ref tempFileSystem);
|
||||
if (tempFileSystem is null)
|
||||
return ResultFs.AllocationFailureInAllocateShared.Log();
|
||||
|
||||
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref tempFileSystem);
|
||||
if (fileSystem is null)
|
||||
return ResultFs.AllocationFailureInCreateShared.Log();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
tempFileSystem?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public Result IsArchivedProgram(out bool isArchived, ulong processId)
|
||||
@ -291,30 +573,24 @@ namespace LibHac.FsSrv
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result HandleResolubleAccessFailure(out bool wasDeferred, in Result nonDeferredResult)
|
||||
public Result HandleResolubleAccessFailure(out bool wasDeferred, Result resultForNoFailureDetected)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return ServiceImpl.HandleResolubleAccessFailure(out wasDeferred, resultForNoFailureDetected, ProcessId);
|
||||
}
|
||||
|
||||
public void IncrementRomFsRemountForDataCorruptionCount()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
ServiceImpl.IncrementRomFsRemountForDataCorruptionCount();
|
||||
}
|
||||
|
||||
public void IncrementRomFsUnrecoverableDataCorruptionByRemountCount()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
ServiceImpl.IncrementRomFsUnrecoverableDataCorruptionByRemountCount();
|
||||
}
|
||||
|
||||
public void IncrementRomFsRecoveredByInvalidateCacheCount()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Result IRomFileSystemAccessFailureManager.OpenDataStorageCore(out ReferenceCountedDisposable<IStorage> storage,
|
||||
out Hash ncaHeaderDigest, ulong id, StorageId storageId)
|
||||
{
|
||||
return OpenDataStorageCore(out storage, out ncaHeaderDigest, id, storageId);
|
||||
ServiceImpl.IncrementRomFsRecoveredByInvalidateCacheCount();
|
||||
}
|
||||
|
||||
private Result TryAcquireAddOnContentOpenCountSemaphore(out IUniqueLock semaphoreLock)
|
||||
@ -329,7 +605,17 @@ namespace LibHac.FsSrv
|
||||
|
||||
private Result GetProgramInfo(out ProgramInfo programInfo)
|
||||
{
|
||||
return ServiceImpl.GetProgramInfo(out programInfo, ProcessId);
|
||||
return ServiceImpl.GetProgramInfoByProcessId(out programInfo, ProcessId);
|
||||
}
|
||||
|
||||
private Result GetProgramInfoByProcessId(out ProgramInfo programInfo, ulong processId)
|
||||
{
|
||||
return ServiceImpl.GetProgramInfoByProcessId(out programInfo, processId);
|
||||
}
|
||||
|
||||
private Result GetProgramInfoByProgramId(out ProgramInfo programInfo, ulong programId)
|
||||
{
|
||||
return ServiceImpl.GetProgramInfoByProgramId(out programInfo, programId);
|
||||
}
|
||||
|
||||
private PathNormalizer.Option GetPathNormalizerOptions(U8Span path)
|
||||
@ -347,10 +633,10 @@ namespace LibHac.FsSrv
|
||||
return StringUtils.Compare(path, CommonPaths.HostRootFileSystemMountName, hostMountLength) == 0;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
Result IRomFileSystemAccessFailureManager.OpenDataStorageCore(out ReferenceCountedDisposable<IStorage> storage,
|
||||
out Hash ncaHeaderDigest, ulong id, StorageId storageId)
|
||||
{
|
||||
AocMountCountSemaphore?.Dispose();
|
||||
RomMountCountSemaphore?.Dispose();
|
||||
return OpenDataStorageCore(out storage, out ncaHeaderDigest, id, storageId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ using LibHac.FsSrv.Creators;
|
||||
using LibHac.FsSrv.Impl;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.FsSystem.NcaUtils;
|
||||
using LibHac.Lr;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Spl;
|
||||
using LibHac.Util;
|
||||
@ -56,21 +57,39 @@ namespace LibHac.FsSrv
|
||||
public ProgramRegistryImpl ProgramRegistry;
|
||||
}
|
||||
|
||||
|
||||
private struct MountInfo
|
||||
{
|
||||
public bool IsGameCard;
|
||||
public int GcHandle;
|
||||
public bool IsHostFs;
|
||||
public bool CanMountNca;
|
||||
}
|
||||
|
||||
|
||||
public Result OpenFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, U8Span path,
|
||||
FileSystemProxyType type, ulong id)
|
||||
{
|
||||
return OpenFileSystem(out fileSystem, out Unsafe.NullRef<CodeVerificationData>(), path, type, false, id);
|
||||
}
|
||||
|
||||
public Result OpenFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
out CodeVerificationData verificationData, U8Span path, FileSystemProxyType type,
|
||||
bool canMountSystemDataPrivate, ulong id)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
Unsafe.SkipInit(out verificationData);
|
||||
verificationData.IsValid = false;
|
||||
if (!Unsafe.IsNullRef(ref verificationData))
|
||||
verificationData.IsValid = false;
|
||||
|
||||
// Get a reference to the path that will be advanced as each part of the path is parsed
|
||||
U8Span currentPath = path.Slice(0, StringUtils.GetLength(path));
|
||||
|
||||
// Open the root filesystem based on the path's mount name
|
||||
Result rc = OpenFileSystemFromMountName(ref currentPath,
|
||||
Result rc = ParseMountName(ref currentPath,
|
||||
out ReferenceCountedDisposable<IFileSystem> baseFileSystem, out bool shouldContinue,
|
||||
out MountNameInfo mountNameInfo);
|
||||
out MountInfo mountNameInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Don't continue if the rest of the path is empty
|
||||
@ -90,7 +109,7 @@ namespace LibHac.FsSrv
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = IsContentPathDir(ref currentPath, out bool isDirectory);
|
||||
rc = CheckDirOrNcaOrNsp(ref currentPath, out bool isDirectory);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (isDirectory)
|
||||
@ -104,7 +123,7 @@ namespace LibHac.FsSrv
|
||||
ReferenceCountedDisposable<IFileSystem> readOnlyFileSystem = null;
|
||||
try
|
||||
{
|
||||
rc = TryOpenCaseSensitiveContentDirectory(out manualFileSystem, ref baseFileSystem,
|
||||
rc = ParseDirWithPathCaseNormalizationOnCaseSensitiveHostFs(out manualFileSystem, ref baseFileSystem,
|
||||
currentPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
@ -122,10 +141,10 @@ namespace LibHac.FsSrv
|
||||
}
|
||||
}
|
||||
|
||||
return TryOpenContentDirectory(currentPath, out fileSystem, ref baseFileSystem, type, true);
|
||||
return ParseDir(currentPath, out fileSystem, ref baseFileSystem, type, true);
|
||||
}
|
||||
|
||||
rc = TryOpenNsp(ref currentPath, out ReferenceCountedDisposable<IFileSystem> nspFileSystem, baseFileSystem);
|
||||
rc = ParseNsp(ref currentPath, out ReferenceCountedDisposable<IFileSystem> nspFileSystem, baseFileSystem);
|
||||
|
||||
if (rc.IsSuccess())
|
||||
{
|
||||
@ -151,10 +170,10 @@ namespace LibHac.FsSrv
|
||||
|
||||
ulong openProgramId = mountNameInfo.IsHostFs ? ulong.MaxValue : id;
|
||||
|
||||
rc = TryOpenNca(ref currentPath, out Nca nca, baseFileSystem, openProgramId);
|
||||
rc = ParseNca(ref currentPath, out Nca nca, baseFileSystem, openProgramId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = OpenNcaStorage(out ReferenceCountedDisposable<IStorage> ncaSectionStorage, nca,
|
||||
rc = OpenStorageByContentType(out ReferenceCountedDisposable<IStorage> ncaSectionStorage, nca,
|
||||
out NcaFormatType fsType, type, mountNameInfo.IsGameCard, canMountSystemDataPrivate);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
@ -169,20 +188,164 @@ namespace LibHac.FsSrv
|
||||
}
|
||||
}
|
||||
|
||||
private struct MountNameInfo
|
||||
public Result OpenDataFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, U8Span path,
|
||||
FileSystemProxyType fsType, ulong programId)
|
||||
{
|
||||
public bool IsGameCard;
|
||||
public int GcHandle;
|
||||
public bool IsHostFs;
|
||||
public bool CanMountNca;
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private Result OpenFileSystemFromMountName(ref U8Span path,
|
||||
out ReferenceCountedDisposable<IFileSystem> fileSystem, out bool shouldContinue, out MountNameInfo info)
|
||||
public Result OpenStorageWithPatch(out ReferenceCountedDisposable<IStorage> storage, out Hash ncaHeaderDigest,
|
||||
U8Span originalNcaPath, U8Span currentNcaPath, FileSystemProxyType fsType, ulong id)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenFileSystemWithPatch(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
U8Span originalNcaPath, U8Span currentNcaPath, FileSystemProxyType fsType, ulong id)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
info = new MountNameInfo();
|
||||
ReferenceCountedDisposable<IStorage> romFsStorage = null;
|
||||
try
|
||||
{
|
||||
Result rc = OpenStorageWithPatch(out romFsStorage, out Unsafe.NullRef<Hash>(), originalNcaPath,
|
||||
currentNcaPath, fsType, id);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return _config.RomFsCreator.Create(out fileSystem, romFsStorage);
|
||||
}
|
||||
finally
|
||||
{
|
||||
romFsStorage?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public Result OpenContentStorageFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
ContentStorageId contentStorageId)
|
||||
{
|
||||
const int storagePathMaxLen = 0x40;
|
||||
|
||||
fileSystem = default;
|
||||
|
||||
ReferenceCountedDisposable<IFileSystem> baseFileSystem = null;
|
||||
ReferenceCountedDisposable<IFileSystem> subDirFileSystem = null;
|
||||
ReferenceCountedDisposable<IFileSystem> encryptedFileSystem = null;
|
||||
|
||||
try
|
||||
{
|
||||
Result rc;
|
||||
|
||||
// Open the appropriate base file system for the content storage ID
|
||||
switch (contentStorageId)
|
||||
{
|
||||
case ContentStorageId.System:
|
||||
rc = _config.BaseFsService.OpenBisFileSystem(out baseFileSystem, U8Span.Empty,
|
||||
BisPartitionId.System);
|
||||
break;
|
||||
case ContentStorageId.User:
|
||||
rc = _config.BaseFsService.OpenBisFileSystem(out baseFileSystem, U8Span.Empty,
|
||||
BisPartitionId.User);
|
||||
break;
|
||||
case ContentStorageId.SdCard:
|
||||
rc = _config.BaseFsService.OpenSdCardProxyFileSystem(out baseFileSystem);
|
||||
break;
|
||||
default:
|
||||
rc = ResultFs.InvalidArgument.Log();
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Build the appropriate path for the content storage ID
|
||||
Span<byte> contentStoragePath = stackalloc byte[storagePathMaxLen];
|
||||
|
||||
if (contentStorageId == ContentStorageId.SdCard)
|
||||
{
|
||||
var sb = new U8StringBuilder(contentStoragePath);
|
||||
sb.Append(StringTraits.DirectorySeparator).Append(SdCardNintendoRootDirectoryName);
|
||||
sb.Append(StringTraits.DirectorySeparator).Append(ContentStorageDirectoryName);
|
||||
}
|
||||
else
|
||||
{
|
||||
var sb = new U8StringBuilder(contentStoragePath);
|
||||
sb.Append(StringTraits.DirectorySeparator).Append(ContentStorageDirectoryName);
|
||||
}
|
||||
|
||||
// Make sure the content storage path exists
|
||||
// ReSharper disable once PossibleNullReferenceException
|
||||
rc = Utility.EnsureDirectory(baseFileSystem.Target, new U8Span(contentStoragePath));
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = _config.SubDirectoryFsCreator.Create(out subDirFileSystem, ref baseFileSystem,
|
||||
new U8Span(contentStoragePath));
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Only content on the SD card is encrypted
|
||||
if (contentStorageId != ContentStorageId.SdCard)
|
||||
{
|
||||
// Move the shared reference to the out variable
|
||||
Shared.Move(out fileSystem, ref subDirFileSystem);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
// Create an encrypted file system for SD card content storage
|
||||
rc = _config.EncryptedFsCreator.Create(out encryptedFileSystem, subDirFileSystem,
|
||||
EncryptedFsKeyId.Content, in _encryptionSeed);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
Shared.Move(out fileSystem, ref encryptedFileSystem);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
baseFileSystem?.Dispose();
|
||||
subDirFileSystem?.Dispose();
|
||||
encryptedFileSystem?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public Result GetRightsId(out RightsId rightsId, out byte keyGeneration, U8Span path, ProgramId programId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result RegisterExternalKey(in RightsId rightsId, in AccessKey accessKey)
|
||||
{
|
||||
return _externalKeyManager.Add(rightsId, accessKey);
|
||||
}
|
||||
|
||||
public Result UnregisterExternalKey(in RightsId rightsId)
|
||||
{
|
||||
_externalKeyManager.Remove(rightsId);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result UnregisterAllExternalKey()
|
||||
{
|
||||
_externalKeyManager.Clear();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result RegisterUpdatePartition(ulong programId, U8Span path)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenRegisteredUpdatePartition(out ReferenceCountedDisposable<IFileSystem> fileSystem)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private Result ParseMountName(ref U8Span path,
|
||||
out ReferenceCountedDisposable<IFileSystem> fileSystem, out bool shouldContinue, out MountInfo info)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
info = new MountInfo();
|
||||
shouldContinue = true;
|
||||
|
||||
if (StringUtils.Compare(path, CommonPaths.GameCardFileSystemMountName,
|
||||
@ -342,7 +505,7 @@ namespace LibHac.FsSrv
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private Result IsContentPathDir(ref U8Span path, out bool isDirectory)
|
||||
private Result CheckDirOrNcaOrNsp(ref U8Span path, out bool isDirectory)
|
||||
{
|
||||
isDirectory = default;
|
||||
|
||||
@ -381,7 +544,7 @@ namespace LibHac.FsSrv
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
private Result TryOpenContentDirectory(U8Span path,
|
||||
private Result ParseDir(U8Span path,
|
||||
out ReferenceCountedDisposable<IFileSystem> contentFileSystem,
|
||||
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, FileSystemProxyType fsType, bool preserveUnc)
|
||||
{
|
||||
@ -393,7 +556,7 @@ namespace LibHac.FsSrv
|
||||
Result rc = _config.SubDirectoryFsCreator.Create(out subDirFs, ref baseFileSystem, path, preserveUnc);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return OpenSubDirectoryForFsType(out contentFileSystem, ref subDirFs, fsType);
|
||||
return ParseContentTypeForDirectory(out contentFileSystem, ref subDirFs, fsType);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@ -401,7 +564,7 @@ namespace LibHac.FsSrv
|
||||
}
|
||||
}
|
||||
|
||||
private Result TryOpenCaseSensitiveContentDirectory(
|
||||
private Result ParseDirWithPathCaseNormalizationOnCaseSensitiveHostFs(
|
||||
out ReferenceCountedDisposable<IFileSystem> contentFileSystem,
|
||||
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path)
|
||||
{
|
||||
@ -430,60 +593,7 @@ namespace LibHac.FsSrv
|
||||
return _config.SubDirectoryFsCreator.Create(out contentFileSystem, ref baseFileSystem, fullPath);
|
||||
}
|
||||
|
||||
private Result OpenSubDirectoryForFsType(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, FileSystemProxyType fsType)
|
||||
{
|
||||
fileSystem = default;
|
||||
ReadOnlySpan<byte> dirName;
|
||||
|
||||
// Get the name of the subdirectory for the filesystem type
|
||||
switch (fsType)
|
||||
{
|
||||
case FileSystemProxyType.Package:
|
||||
fileSystem = baseFileSystem;
|
||||
return Result.Success;
|
||||
|
||||
case FileSystemProxyType.Code:
|
||||
dirName = new[] { (byte)'/', (byte)'c', (byte)'o', (byte)'d', (byte)'e', (byte)'/' };
|
||||
break;
|
||||
case FileSystemProxyType.Rom:
|
||||
case FileSystemProxyType.Control:
|
||||
case FileSystemProxyType.Manual:
|
||||
case FileSystemProxyType.Meta:
|
||||
case FileSystemProxyType.RegisteredUpdate:
|
||||
dirName = new[] { (byte)'/', (byte)'d', (byte)'a', (byte)'t', (byte)'a', (byte)'/' };
|
||||
break;
|
||||
case FileSystemProxyType.Logo:
|
||||
dirName = new[] { (byte)'/', (byte)'l', (byte)'o', (byte)'g', (byte)'o', (byte)'/' };
|
||||
break;
|
||||
|
||||
default:
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
}
|
||||
|
||||
ReferenceCountedDisposable<IFileSystem> subDirFs = null;
|
||||
try
|
||||
{
|
||||
// Open the subdirectory filesystem
|
||||
Result rc = _config.SubDirectoryFsCreator.Create(out subDirFs, ref baseFileSystem, new U8Span(dirName));
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (fsType == FileSystemProxyType.Code)
|
||||
{
|
||||
rc = _config.StorageOnNcaCreator.VerifyAcidSignature(subDirFs.Target, null);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
Shared.Move(out fileSystem, ref subDirFs);
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
subDirFs?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private Result TryOpenNsp(ref U8Span path, out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
private Result ParseNsp(ref U8Span path, out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
ReferenceCountedDisposable<IFileSystem> baseFileSystem)
|
||||
{
|
||||
fileSystem = default;
|
||||
@ -542,7 +652,7 @@ namespace LibHac.FsSrv
|
||||
return rc;
|
||||
}
|
||||
|
||||
private Result TryOpenNca(ref U8Span path, out Nca nca, ReferenceCountedDisposable<IFileSystem> baseFileSystem,
|
||||
private Result ParseNca(ref U8Span path, out Nca nca, ReferenceCountedDisposable<IFileSystem> baseFileSystem,
|
||||
ulong ncaId)
|
||||
{
|
||||
nca = default;
|
||||
@ -570,7 +680,77 @@ namespace LibHac.FsSrv
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private Result OpenNcaStorage(out ReferenceCountedDisposable<IStorage> ncaStorage, Nca nca,
|
||||
private Result ParseContentTypeForDirectory(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, FileSystemProxyType fsType)
|
||||
{
|
||||
fileSystem = default;
|
||||
ReadOnlySpan<byte> dirName;
|
||||
|
||||
// Get the name of the subdirectory for the filesystem type
|
||||
switch (fsType)
|
||||
{
|
||||
case FileSystemProxyType.Package:
|
||||
fileSystem = baseFileSystem;
|
||||
return Result.Success;
|
||||
|
||||
case FileSystemProxyType.Code:
|
||||
dirName = new[] { (byte)'/', (byte)'c', (byte)'o', (byte)'d', (byte)'e', (byte)'/' };
|
||||
break;
|
||||
case FileSystemProxyType.Rom:
|
||||
case FileSystemProxyType.Control:
|
||||
case FileSystemProxyType.Manual:
|
||||
case FileSystemProxyType.Meta:
|
||||
case FileSystemProxyType.RegisteredUpdate:
|
||||
dirName = new[] { (byte)'/', (byte)'d', (byte)'a', (byte)'t', (byte)'a', (byte)'/' };
|
||||
break;
|
||||
case FileSystemProxyType.Logo:
|
||||
dirName = new[] { (byte)'/', (byte)'l', (byte)'o', (byte)'g', (byte)'o', (byte)'/' };
|
||||
break;
|
||||
|
||||
default:
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
}
|
||||
|
||||
ReferenceCountedDisposable<IFileSystem> subDirFs = null;
|
||||
try
|
||||
{
|
||||
// Open the subdirectory filesystem
|
||||
Result rc = _config.SubDirectoryFsCreator.Create(out subDirFs, ref baseFileSystem, new U8Span(dirName));
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (fsType == FileSystemProxyType.Code)
|
||||
{
|
||||
rc = _config.StorageOnNcaCreator.VerifyAcidSignature(subDirFs.Target, null);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
Shared.Move(out fileSystem, ref subDirFs);
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
subDirFs?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private Result SetExternalKeyForRightsId(Nca nca)
|
||||
{
|
||||
var rightsId = new RightsId(nca.Header.RightsId);
|
||||
var zero = new RightsId(0, 0);
|
||||
|
||||
if (Crypto.CryptoUtil.IsSameBytes(rightsId.AsBytes(), zero.AsBytes(), Unsafe.SizeOf<RightsId>()))
|
||||
return Result.Success;
|
||||
|
||||
// ReSharper disable once UnusedVariable
|
||||
Result rc = _externalKeyManager.Get(rightsId, out AccessKey accessKey);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// todo: Set key in nca reader
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private Result OpenStorageByContentType(out ReferenceCountedDisposable<IStorage> ncaStorage, Nca nca,
|
||||
out NcaFormatType fsType, FileSystemProxyType fsProxyType, bool isGameCard, bool canMountSystemDataPrivate)
|
||||
{
|
||||
ncaStorage = default;
|
||||
@ -619,10 +799,10 @@ namespace LibHac.FsSrv
|
||||
if (nca.Header.DistributionType == DistributionType.GameCard && !isGameCard)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
Result rc = SetNcaExternalKey(nca);
|
||||
Result rc = SetExternalKeyForRightsId(nca);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = GetNcaSectionIndex(out int sectionIndex, fsProxyType);
|
||||
rc = GetPartitionIndex(out int sectionIndex, fsProxyType);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = _config.StorageOnNcaCreator.Create(out ncaStorage, out NcaFsHeader fsHeader, nca,
|
||||
@ -633,44 +813,108 @@ namespace LibHac.FsSrv
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private Result SetNcaExternalKey(Nca nca)
|
||||
public Result SetSdCardEncryptionSeed(in EncryptionSeed encryptionSeed)
|
||||
{
|
||||
var rightsId = new RightsId(nca.Header.RightsId);
|
||||
var zero = new RightsId(0, 0);
|
||||
|
||||
if (Crypto.CryptoUtil.IsSameBytes(rightsId.AsBytes(), zero.AsBytes(), Unsafe.SizeOf<RightsId>()))
|
||||
return Result.Success;
|
||||
|
||||
// ReSharper disable once UnusedVariable
|
||||
Result rc = _externalKeyManager.Get(rightsId, out AccessKey accessKey);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// todo: Set key in nca reader
|
||||
_encryptionSeed = encryptionSeed;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private Result GetNcaSectionIndex(out int index, FileSystemProxyType fspType)
|
||||
public Result ResolveRomReferenceProgramId(out ProgramId targetProgramId, ProgramId programId,
|
||||
byte programIndex)
|
||||
{
|
||||
switch (fspType)
|
||||
Unsafe.SkipInit(out targetProgramId);
|
||||
|
||||
ProgramId mainProgramId = _config.ProgramRegistryService.GetProgramIdByIndex(programId, programIndex);
|
||||
if (mainProgramId == ProgramId.InvalidId)
|
||||
return ResultFs.TargetProgramIndexNotFound.Log();
|
||||
|
||||
targetProgramId = mainProgramId;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result ResolveProgramPath(out Path path, ProgramId programId, StorageId storageId)
|
||||
{
|
||||
Result rc = _locationResolverSet.ResolveProgramPath(out path, programId.Value, storageId);
|
||||
if (rc.IsSuccess())
|
||||
return Result.Success;
|
||||
|
||||
var dataId = new DataId(programId.Value);
|
||||
|
||||
rc = _locationResolverSet.ResolveDataPath(out path, dataId, storageId);
|
||||
if (rc.IsSuccess())
|
||||
return Result.Success;
|
||||
|
||||
return ResultFs.TargetNotFound.Log();
|
||||
}
|
||||
|
||||
public Result ResolveRomPath(out Path path, ulong id, StorageId storageId)
|
||||
{
|
||||
return _locationResolverSet.ResolveRomPath(out path, id, storageId);
|
||||
}
|
||||
|
||||
public Result ResolveApplicationHtmlDocumentPath(out Path path, Ncm.ApplicationId applicationId,
|
||||
StorageId storageId)
|
||||
{
|
||||
return _locationResolverSet.ResolveApplicationHtmlDocumentPath(out path, applicationId, storageId);
|
||||
}
|
||||
|
||||
public Result ResolveRegisteredHtmlDocumentPath(out Path path, ulong id)
|
||||
{
|
||||
return _locationResolverSet.ResolveRegisteredHtmlDocumentPath(out path, id);
|
||||
}
|
||||
|
||||
internal StorageType GetStorageFlag(ulong programId)
|
||||
{
|
||||
if (programId >= _config.SpeedEmulationRange.ProgramIdMin &&
|
||||
programId <= _config.SpeedEmulationRange.ProgramIdMax)
|
||||
return StorageType.Bis;
|
||||
else
|
||||
return StorageType.All;
|
||||
}
|
||||
|
||||
public Result HandleResolubleAccessFailure(out bool wasDeferred, Result resultForNoFailureDetected,
|
||||
ulong processId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void IncrementRomFsRemountForDataCorruptionCount()
|
||||
{
|
||||
lock (_romfsCountLocker)
|
||||
{
|
||||
case FileSystemProxyType.Code:
|
||||
case FileSystemProxyType.Control:
|
||||
case FileSystemProxyType.Manual:
|
||||
case FileSystemProxyType.Meta:
|
||||
case FileSystemProxyType.Data:
|
||||
index = 0;
|
||||
return Result.Success;
|
||||
case FileSystemProxyType.Rom:
|
||||
case FileSystemProxyType.RegisteredUpdate:
|
||||
index = 1;
|
||||
return Result.Success;
|
||||
case FileSystemProxyType.Logo:
|
||||
index = 2;
|
||||
return Result.Success;
|
||||
default:
|
||||
index = default;
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
_romFsRemountForDataCorruptionCount++;
|
||||
}
|
||||
}
|
||||
|
||||
public void IncrementRomFsUnrecoverableDataCorruptionByRemountCount()
|
||||
{
|
||||
lock (_romfsCountLocker)
|
||||
{
|
||||
_romfsUnrecoverableDataCorruptionByRemountCount++;
|
||||
}
|
||||
}
|
||||
|
||||
public void IncrementRomFsRecoveredByInvalidateCacheCount()
|
||||
{
|
||||
lock (_romfsCountLocker)
|
||||
{
|
||||
_romFsRecoveredByInvalidateCacheCount++;
|
||||
}
|
||||
}
|
||||
|
||||
public void GetAndClearRomFsErrorInfo(out int recoveredByRemountCount, out int unrecoverableCount,
|
||||
out int recoveredByCacheInvalidationCount)
|
||||
{
|
||||
lock (_romfsCountLocker)
|
||||
{
|
||||
recoveredByRemountCount = _romFsRemountForDataCorruptionCount;
|
||||
unrecoverableCount = _romfsUnrecoverableDataCorruptionByRemountCount;
|
||||
recoveredByCacheInvalidationCount = _romFsRecoveredByInvalidateCacheCount;
|
||||
|
||||
_romFsRemountForDataCorruptionCount = 0;
|
||||
_romfsUnrecoverableDataCorruptionByRemountCount = 0;
|
||||
_romFsRecoveredByInvalidateCacheCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -718,129 +962,40 @@ namespace LibHac.FsSrv
|
||||
}
|
||||
}
|
||||
|
||||
public Result OpenFileSystemWithPatch(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
ProgramId programId, FileSystemProxyType fsType)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenContentStorageFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
ContentStorageId contentStorageId)
|
||||
{
|
||||
const int storagePathMaxLen = 0x40;
|
||||
|
||||
fileSystem = default;
|
||||
|
||||
ReferenceCountedDisposable<IFileSystem> baseFileSystem = null;
|
||||
ReferenceCountedDisposable<IFileSystem> subDirFileSystem = null;
|
||||
ReferenceCountedDisposable<IFileSystem> encryptedFileSystem = null;
|
||||
|
||||
try
|
||||
{
|
||||
Result rc;
|
||||
|
||||
// Open the appropriate base file system for the content storage ID
|
||||
switch (contentStorageId)
|
||||
{
|
||||
case ContentStorageId.System:
|
||||
rc = _config.BaseFsService.OpenBisFileSystem(out baseFileSystem, U8Span.Empty,
|
||||
BisPartitionId.System);
|
||||
break;
|
||||
case ContentStorageId.User:
|
||||
rc = _config.BaseFsService.OpenBisFileSystem(out baseFileSystem, U8Span.Empty,
|
||||
BisPartitionId.User);
|
||||
break;
|
||||
case ContentStorageId.SdCard:
|
||||
rc = _config.BaseFsService.OpenSdCardProxyFileSystem(out baseFileSystem);
|
||||
break;
|
||||
default:
|
||||
rc = ResultFs.InvalidArgument.Log();
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Build the appropriate path for the content storage ID
|
||||
Span<byte> contentStoragePath = stackalloc byte[storagePathMaxLen];
|
||||
|
||||
if (contentStorageId == ContentStorageId.SdCard)
|
||||
{
|
||||
var sb = new U8StringBuilder(contentStoragePath);
|
||||
sb.Append(StringTraits.DirectorySeparator).Append(SdCardNintendoRootDirectoryName);
|
||||
sb.Append(StringTraits.DirectorySeparator).Append(ContentStorageDirectoryName);
|
||||
}
|
||||
else
|
||||
{
|
||||
var sb = new U8StringBuilder(contentStoragePath);
|
||||
sb.Append(StringTraits.DirectorySeparator).Append(ContentStorageDirectoryName);
|
||||
}
|
||||
|
||||
// Make sure the content storage path exists
|
||||
// ReSharper disable once PossibleNullReferenceException
|
||||
rc = Utility.EnsureDirectory(baseFileSystem.Target, new U8Span(contentStoragePath));
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = _config.SubDirectoryFsCreator.Create(out subDirFileSystem, ref baseFileSystem,
|
||||
new U8Span(contentStoragePath));
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Only content on the SD card is encrypted
|
||||
if (contentStorageId != ContentStorageId.SdCard)
|
||||
{
|
||||
// Move the shared reference to the out variable
|
||||
Shared.Move(out fileSystem, ref subDirFileSystem);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
// Create an encrypted file system for SD card content storage
|
||||
rc = _config.EncryptedFsCreator.Create(out encryptedFileSystem, subDirFileSystem,
|
||||
EncryptedFsKeyId.Content, in _encryptionSeed);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
Shared.Move(out fileSystem, ref encryptedFileSystem);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
baseFileSystem?.Dispose();
|
||||
subDirFileSystem?.Dispose();
|
||||
encryptedFileSystem?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public Result RegisterExternalKey(in RightsId rightsId, in AccessKey accessKey)
|
||||
{
|
||||
return _externalKeyManager.Add(rightsId, accessKey);
|
||||
}
|
||||
|
||||
public Result UnregisterExternalKey(in RightsId rightsId)
|
||||
{
|
||||
_externalKeyManager.Remove(rightsId);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result UnregisterAllExternalKey()
|
||||
{
|
||||
_externalKeyManager.Clear();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result SetSdCardEncryptionSeed(in EncryptionSeed encryptionSeed)
|
||||
{
|
||||
_encryptionSeed = encryptionSeed;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
internal Result GetProgramInfo(out ProgramInfo programInfo, ulong processId)
|
||||
internal Result GetProgramInfoByProcessId(out ProgramInfo programInfo, ulong processId)
|
||||
{
|
||||
return _config.ProgramRegistry.GetProgramInfo(out programInfo, processId);
|
||||
}
|
||||
|
||||
internal Result GetProgramInfoByProgramId(out ProgramInfo programInfo, ulong programId)
|
||||
{
|
||||
return _config.ProgramRegistry.GetProgramInfoByProgramId(out programInfo, programId);
|
||||
}
|
||||
|
||||
private Result GetPartitionIndex(out int index, FileSystemProxyType fspType)
|
||||
{
|
||||
switch (fspType)
|
||||
{
|
||||
case FileSystemProxyType.Code:
|
||||
case FileSystemProxyType.Control:
|
||||
case FileSystemProxyType.Manual:
|
||||
case FileSystemProxyType.Meta:
|
||||
case FileSystemProxyType.Data:
|
||||
index = 0;
|
||||
return Result.Success;
|
||||
case FileSystemProxyType.Rom:
|
||||
case FileSystemProxyType.RegisteredUpdate:
|
||||
index = 1;
|
||||
return Result.Success;
|
||||
case FileSystemProxyType.Logo:
|
||||
index = 2;
|
||||
return Result.Success;
|
||||
default:
|
||||
index = default;
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
}
|
||||
}
|
||||
|
||||
private static ReadOnlySpan<byte> SdCardNintendoRootDirectoryName => // Nintendo
|
||||
new[]
|
||||
{
|
||||
|
@ -165,6 +165,7 @@ namespace LibHac.FsSrv
|
||||
{
|
||||
saveDirectoryFs?.Dispose();
|
||||
// ReSharper disable once ExpressionIsAlwaysNull
|
||||
// ReSharper disable once ConstantConditionalAccessQualifier
|
||||
cachedSaveDataFs?.Dispose();
|
||||
saveDataFs?.Dispose();
|
||||
}
|
||||
|
@ -45,7 +45,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="5.0.0-preview.7.20364.11" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Sf;
|
||||
|
||||
namespace LibHac.Lr
|
||||
{
|
||||
@ -21,7 +22,7 @@ namespace LibHac.Lr
|
||||
public Result UnregisterAllAddOnContentPath() =>
|
||||
_interface.Target.UnregisterAllAddOnContentPath();
|
||||
|
||||
public Result RefreshApplicationAddOnContent(ReadOnlySpan<Ncm.ApplicationId> ids) =>
|
||||
public Result RefreshApplicationAddOnContent(InArray<Ncm.ApplicationId> ids) =>
|
||||
_interface.Target.RefreshApplicationAddOnContent(ids);
|
||||
|
||||
public Result UnregisterApplicationAddOnContent(Ncm.ApplicationId id) =>
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Sf;
|
||||
|
||||
namespace LibHac.Lr
|
||||
{
|
||||
@ -8,7 +9,7 @@ namespace LibHac.Lr
|
||||
Result ResolveAddOnContentPath(out Path path, DataId id);
|
||||
Result RegisterAddOnContentStorage(DataId id, Ncm.ApplicationId applicationId, StorageId storageId);
|
||||
Result UnregisterAllAddOnContentPath();
|
||||
Result RefreshApplicationAddOnContent(ReadOnlySpan<Ncm.ApplicationId> ids);
|
||||
Result RefreshApplicationAddOnContent(InArray<Ncm.ApplicationId> ids);
|
||||
Result UnregisterApplicationAddOnContent(Ncm.ApplicationId id);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Sf;
|
||||
|
||||
namespace LibHac.Lr
|
||||
{
|
||||
@ -16,14 +17,14 @@ namespace LibHac.Lr
|
||||
Result RedirectApplicationLegalInformationPath(in Path path, ProgramId id, ProgramId ownerId);
|
||||
Result Refresh();
|
||||
Result RedirectApplicationProgramPath(in Path path, ProgramId id, ProgramId ownerId);
|
||||
Result ClearApplicationRedirection(ReadOnlySpan<ProgramId> excludingIds);
|
||||
Result ClearApplicationRedirection(InArray<ProgramId> excludingIds);
|
||||
Result EraseProgramRedirection(ProgramId id);
|
||||
Result EraseApplicationControlRedirection(ProgramId id);
|
||||
Result EraseApplicationHtmlDocumentRedirection(ProgramId id);
|
||||
Result EraseApplicationLegalInformationRedirection(ProgramId id);
|
||||
Result ResolveProgramPathForDebug(out Path path, ProgramId id);
|
||||
Result RedirectProgramPathForDebug(in Path path, ProgramId id);
|
||||
Result RedirectApplicationProgramPathForDebug(in Path path, ProgramId id);
|
||||
Result RedirectApplicationProgramPathForDebug(in Path path, ProgramId id, ProgramId ownerId);
|
||||
Result EraseProgramRedirectionForDebug(ProgramId id);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Sf;
|
||||
|
||||
namespace LibHac.Lr
|
||||
{
|
||||
@ -45,7 +46,7 @@ namespace LibHac.Lr
|
||||
public Result RedirectApplicationProgramPath(in Path path, ProgramId id, ProgramId ownerId) =>
|
||||
_interface.Target.RedirectApplicationProgramPath(in path, id, ownerId);
|
||||
|
||||
public Result ClearApplicationRedirection(ReadOnlySpan<ProgramId> excludingIds) =>
|
||||
public Result ClearApplicationRedirection(InArray<ProgramId> excludingIds) =>
|
||||
_interface.Target.ClearApplicationRedirection(excludingIds);
|
||||
|
||||
public Result EraseProgramRedirection(ProgramId id) =>
|
||||
@ -66,8 +67,8 @@ namespace LibHac.Lr
|
||||
public Result RedirectProgramPathForDebug(in Path path, ProgramId id) =>
|
||||
_interface.Target.RedirectProgramPathForDebug(in path, id);
|
||||
|
||||
public Result RedirectApplicationProgramPathForDebug(in Path path, ProgramId id) =>
|
||||
_interface.Target.RedirectApplicationProgramPathForDebug(in path, id);
|
||||
public Result RedirectApplicationProgramPathForDebug(in Path path, ProgramId id, ProgramId ownerId) =>
|
||||
_interface.Target.RedirectApplicationProgramPathForDebug(in path, id, ownerId);
|
||||
|
||||
public Result EraseProgramRedirectionForDebug(ProgramId id) =>
|
||||
_interface.Target.EraseProgramRedirectionForDebug(id);
|
||||
|
@ -442,6 +442,24 @@ namespace LibHac
|
||||
return TryAddReferenceImpl(target, referenceCount);
|
||||
}
|
||||
|
||||
public ReferenceCountedDisposable<TTo>? TryAddReference<TTo>()
|
||||
where TTo : class, IDisposable
|
||||
{
|
||||
WeakReference<T>? weakInstance = _weakInstance;
|
||||
if (weakInstance == null || !weakInstance.TryGetTarget(out var target))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
StrongBox<int>? referenceCount = _boxedReferenceCount;
|
||||
if (referenceCount == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return TryAddReferenceImpl<T, TTo>(target, referenceCount, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increments the reference count for the disposable object, and returns a new disposable reference to
|
||||
/// it.
|
||||
@ -459,6 +477,36 @@ namespace LibHac
|
||||
/// underlying object has already been disposed.</returns>
|
||||
public ReferenceCountedDisposable<T> AddReference() =>
|
||||
TryAddReference() ?? throw new ObjectDisposedException(nameof(WeakReference));
|
||||
|
||||
public ReferenceCountedDisposable<TTo> AddReference<TTo>() where TTo : class, IDisposable
|
||||
{
|
||||
WeakReference<T>? weakInstance = _weakInstance;
|
||||
if (weakInstance == null || !weakInstance.TryGetTarget(out var target))
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(WeakReference));
|
||||
}
|
||||
|
||||
StrongBox<int>? referenceCount = _boxedReferenceCount;
|
||||
if (referenceCount == null)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(WeakReference));
|
||||
}
|
||||
|
||||
ReferenceCountedDisposable<TTo>? newReference =
|
||||
TryAddReferenceImpl<T, TTo>(target, referenceCount, out CreateResult result);
|
||||
|
||||
if (newReference != null)
|
||||
{
|
||||
return newReference;
|
||||
}
|
||||
|
||||
throw result switch
|
||||
{
|
||||
CreateResult.Disposed => new ObjectDisposedException(nameof(ReferenceCountedDisposable<T>)),
|
||||
CreateResult.NotCastable => new InvalidCastException(),
|
||||
_ => new NotSupportedException("This exception should never be thrown.")
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -41,4 +41,34 @@ namespace LibHac.Sf
|
||||
return new OutBuffer(SpanHelpers.AsByteSpan(ref value));
|
||||
}
|
||||
}
|
||||
|
||||
public readonly ref struct InArray<T> where T : unmanaged
|
||||
{
|
||||
private readonly ReadOnlySpan<T> _array;
|
||||
|
||||
public int Size => _array.Length;
|
||||
public ReadOnlySpan<T> Array => _array;
|
||||
|
||||
public InArray(ReadOnlySpan<T> array)
|
||||
{
|
||||
_array = array;
|
||||
}
|
||||
|
||||
public ref readonly T this[int i] => ref _array[i];
|
||||
}
|
||||
|
||||
public readonly ref struct OutArray<T> where T : unmanaged
|
||||
{
|
||||
private readonly Span<T> _array;
|
||||
|
||||
public int Size => _array.Length;
|
||||
public Span<T> Array => _array;
|
||||
|
||||
public OutArray(Span<T> array)
|
||||
{
|
||||
_array = array;
|
||||
}
|
||||
|
||||
public ref T this[int i] => ref _array[i];
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user