Add more NcaFileSystemService functions

This commit is contained in:
Alex Barney 2020-11-10 15:07:28 -07:00
parent 6b6a31dcea
commit 0efaafd101
14 changed files with 862 additions and 289 deletions

View File

@ -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)
{

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

View File

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

View File

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

View File

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

View File

@ -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[]
{

View File

@ -165,6 +165,7 @@ namespace LibHac.FsSrv
{
saveDirectoryFs?.Dispose();
// ReSharper disable once ExpressionIsAlwaysNull
// ReSharper disable once ConstantConditionalAccessQualifier
cachedSaveDataFs?.Dispose();
saveDataFs?.Dispose();
}

View File

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

View File

@ -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) =>

View File

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

View File

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

View File

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

View File

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

View File

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