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 class AsynchronousAccessFileSystem : ForwardingFileSystem
{ {
public AsynchronousAccessFileSystem(ReferenceCountedDisposable<IFileSystem> baseFileSystem) : base(
baseFileSystem)
{ }
protected AsynchronousAccessFileSystem(ref ReferenceCountedDisposable<IFileSystem> baseFileSystem) : base( protected AsynchronousAccessFileSystem(ref ReferenceCountedDisposable<IFileSystem> baseFileSystem) : base(
ref baseFileSystem) ref baseFileSystem)
{ } { }
public static ReferenceCountedDisposable<IFileSystem> CreateShared(
ReferenceCountedDisposable<IFileSystem> baseFileSystem)
{
return new ReferenceCountedDisposable<IFileSystem>(new AsynchronousAccessFileSystem(baseFileSystem));
}
public static ReferenceCountedDisposable<IFileSystem> CreateShared( public static ReferenceCountedDisposable<IFileSystem> CreateShared(
ref ReferenceCountedDisposable<IFileSystem> fileSystem) 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,15 +1,16 @@
using LibHac.Fs; using System;
using LibHac.Fs;
using LibHac.FsSystem; using LibHac.FsSystem;
using LibHac.Ncm; 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, Result OpenDataStorageCore(out ReferenceCountedDisposable<IStorage> storage, out Hash ncaHeaderDigest, ulong id,
StorageId storageId); StorageId storageId);
Result HandleResolubleAccessFailure(out bool wasDeferred, in Result nonDeferredResult); Result HandleResolubleAccessFailure(out bool wasDeferred, Result resultForNoFailureDetected);
void IncrementRomFsRemountForDataCorruptionCount(); void IncrementRomFsRemountForDataCorruptionCount();
void IncrementRomFsUnrecoverableDataCorruptionByRemountCount(); void IncrementRomFsUnrecoverableDataCorruptionByRemountCount();
void IncrementRomFsRecoveredByInvalidateCacheCount(); void IncrementRomFsRecoveredByInvalidateCacheCount();

View File

@ -108,28 +108,28 @@ namespace LibHac.FsSrv.Impl
return Result.Success; 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); Path.InitEmpty(out path);
Result rc = GetLocationResolver(out LocationResolver resolver, storageId); Result rc = GetLocationResolver(out LocationResolver resolver, storageId);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = resolver.ResolveProgramPath(out path, programId); rc = resolver.ResolveProgramPath(out path, new ProgramId(id));
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
PathUtility.Replace(path.StrMutable, (byte)'\\', (byte)'/'); PathUtility.Replace(path.StrMutable, (byte)'\\', (byte)'/');
return Result.Success; 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); Path.InitEmpty(out path);
Result rc = GetLocationResolver(out LocationResolver resolver, storageId); Result rc = GetLocationResolver(out LocationResolver resolver, storageId);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = resolver.ResolveProgramPath(out path, programId); rc = resolver.ResolveProgramPath(out path, new ProgramId(id));
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
PathUtility.Replace(path.StrMutable, (byte)'\\', (byte)'/'); PathUtility.Replace(path.StrMutable, (byte)'\\', (byte)'/');
@ -159,7 +159,7 @@ namespace LibHac.FsSrv.Impl
return resolver.ResolveDataPath(out path, dataId); 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); Path.InitEmpty(out path);
@ -168,11 +168,11 @@ namespace LibHac.FsSrv.Impl
using (resolver) 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); Path.InitEmpty(out path);
@ -181,7 +181,7 @@ namespace LibHac.FsSrv.Impl
using (resolver) 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;
using System.Runtime.CompilerServices;
using LibHac.Common; using LibHac.Common;
using LibHac.Diag;
using LibHac.Fs; using LibHac.Fs;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
using LibHac.FsSrv.Impl; using LibHac.FsSrv.Impl;
using LibHac.FsSrv.Sf; using LibHac.FsSrv.Sf;
using LibHac.FsSystem; using LibHac.FsSystem;
using LibHac.Lr;
using LibHac.Ncm; using LibHac.Ncm;
using LibHac.Spl; using LibHac.Spl;
using LibHac.Util; using LibHac.Util;
using Path = LibHac.Lr.Path;
namespace LibHac.FsSrv namespace LibHac.FsSrv
{ {
internal class NcaFileSystemService : IRomFileSystemAccessFailureManager, IDisposable internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
{ {
private const int AocSemaphoreCount = 128; private const int AocSemaphoreCount = 128;
private const int RomSemaphoreCount = 10; private const int RomSemaphoreCount = 10;
@ -44,10 +48,125 @@ namespace LibHac.FsSrv
return sharedService; return sharedService;
} }
public void Dispose()
{
AocMountCountSemaphore?.Dispose();
RomMountCountSemaphore?.Dispose();
}
public Result OpenFileSystemWithPatch(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, public Result OpenFileSystemWithPatch(out ReferenceCountedDisposable<IFileSystemSf> fileSystem,
ProgramId programId, FileSystemProxyType fsType) 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, public Result OpenCodeFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem,
@ -165,7 +284,44 @@ namespace LibHac.FsSrv
public Result OpenDataFileSystemWithProgramIndex(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, public Result OpenDataFileSystemWithProgramIndex(out ReferenceCountedDisposable<IFileSystemSf> fileSystem,
byte programIndex) 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, public Result OpenDataStorageWithProgramIndex(out ReferenceCountedDisposable<IStorageSf> storage,
@ -176,18 +332,90 @@ namespace LibHac.FsSrv
public Result GetRightsId(out RightsId rightsId, ProgramId programId, StorageId storageId) 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) 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, 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, public Result OpenContentStorageFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem,
@ -195,6 +423,9 @@ namespace LibHac.FsSrv
{ {
fileSystem = default; fileSystem = default;
StorageType storageFlag = contentStorageId == ContentStorageId.System ? StorageType.Bis : StorageType.All;
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(storageFlag);
Result rc = GetProgramInfo(out ProgramInfo programInfo); Result rc = GetProgramInfo(out ProgramInfo programInfo);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
@ -204,21 +435,24 @@ namespace LibHac.FsSrv
if (!accessibility.CanRead || !accessibility.CanWrite) if (!accessibility.CanRead || !accessibility.CanWrite)
return ResultFs.PermissionDenied.Log(); return ResultFs.PermissionDenied.Log();
ReferenceCountedDisposable<IFileSystem> fs = null; ReferenceCountedDisposable<IFileSystem> tempFileSystem = null;
try try
{ {
rc = ServiceImpl.OpenContentStorageFileSystem(out fs, contentStorageId); rc = ServiceImpl.OpenContentStorageFileSystem(out tempFileSystem, contentStorageId);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
tempFileSystem = StorageLayoutTypeSetFileSystem.CreateShared(ref tempFileSystem, storageFlag);
tempFileSystem = AsynchronousAccessFileSystem.CreateShared(ref tempFileSystem);
// Create an SF adapter for the file system // Create an SF adapter for the file system
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref fs); fileSystem = FileSystemInterfaceAdapter.CreateShared(ref tempFileSystem);
return Result.Success; return Result.Success;
} }
finally finally
{ {
fs?.Dispose(); tempFileSystem?.Dispose();
} }
} }
@ -257,12 +491,60 @@ namespace LibHac.FsSrv
public Result RegisterUpdatePartition() 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) 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) public Result IsArchivedProgram(out bool isArchived, ulong processId)
@ -291,30 +573,24 @@ namespace LibHac.FsSrv
throw new NotImplementedException(); 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() public void IncrementRomFsRemountForDataCorruptionCount()
{ {
throw new NotImplementedException(); ServiceImpl.IncrementRomFsRemountForDataCorruptionCount();
} }
public void IncrementRomFsUnrecoverableDataCorruptionByRemountCount() public void IncrementRomFsUnrecoverableDataCorruptionByRemountCount()
{ {
throw new NotImplementedException(); ServiceImpl.IncrementRomFsUnrecoverableDataCorruptionByRemountCount();
} }
public void IncrementRomFsRecoveredByInvalidateCacheCount() public void IncrementRomFsRecoveredByInvalidateCacheCount()
{ {
throw new NotImplementedException(); ServiceImpl.IncrementRomFsRecoveredByInvalidateCacheCount();
}
Result IRomFileSystemAccessFailureManager.OpenDataStorageCore(out ReferenceCountedDisposable<IStorage> storage,
out Hash ncaHeaderDigest, ulong id, StorageId storageId)
{
return OpenDataStorageCore(out storage, out ncaHeaderDigest, id, storageId);
} }
private Result TryAcquireAddOnContentOpenCountSemaphore(out IUniqueLock semaphoreLock) private Result TryAcquireAddOnContentOpenCountSemaphore(out IUniqueLock semaphoreLock)
@ -329,7 +605,17 @@ namespace LibHac.FsSrv
private Result GetProgramInfo(out ProgramInfo programInfo) 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) private PathNormalizer.Option GetPathNormalizerOptions(U8Span path)
@ -347,10 +633,10 @@ namespace LibHac.FsSrv
return StringUtils.Compare(path, CommonPaths.HostRootFileSystemMountName, hostMountLength) == 0; 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(); return OpenDataStorageCore(out storage, out ncaHeaderDigest, id, storageId);
RomMountCountSemaphore?.Dispose();
} }
} }
} }

View File

@ -8,6 +8,7 @@ using LibHac.FsSrv.Creators;
using LibHac.FsSrv.Impl; using LibHac.FsSrv.Impl;
using LibHac.FsSystem; using LibHac.FsSystem;
using LibHac.FsSystem.NcaUtils; using LibHac.FsSystem.NcaUtils;
using LibHac.Lr;
using LibHac.Ncm; using LibHac.Ncm;
using LibHac.Spl; using LibHac.Spl;
using LibHac.Util; using LibHac.Util;
@ -56,21 +57,39 @@ namespace LibHac.FsSrv
public ProgramRegistryImpl ProgramRegistry; 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, public Result OpenFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
out CodeVerificationData verificationData, U8Span path, FileSystemProxyType type, out CodeVerificationData verificationData, U8Span path, FileSystemProxyType type,
bool canMountSystemDataPrivate, ulong id) bool canMountSystemDataPrivate, ulong id)
{ {
fileSystem = default; fileSystem = default;
Unsafe.SkipInit(out verificationData); Unsafe.SkipInit(out verificationData);
if (!Unsafe.IsNullRef(ref verificationData))
verificationData.IsValid = false; verificationData.IsValid = false;
// Get a reference to the path that will be advanced as each part of the path is parsed // 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)); U8Span currentPath = path.Slice(0, StringUtils.GetLength(path));
// Open the root filesystem based on the path's mount name // 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 ReferenceCountedDisposable<IFileSystem> baseFileSystem, out bool shouldContinue,
out MountNameInfo mountNameInfo); out MountInfo mountNameInfo);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
// Don't continue if the rest of the path is empty // Don't continue if the rest of the path is empty
@ -90,7 +109,7 @@ namespace LibHac.FsSrv
return rc; return rc;
} }
rc = IsContentPathDir(ref currentPath, out bool isDirectory); rc = CheckDirOrNcaOrNsp(ref currentPath, out bool isDirectory);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
if (isDirectory) if (isDirectory)
@ -104,7 +123,7 @@ namespace LibHac.FsSrv
ReferenceCountedDisposable<IFileSystem> readOnlyFileSystem = null; ReferenceCountedDisposable<IFileSystem> readOnlyFileSystem = null;
try try
{ {
rc = TryOpenCaseSensitiveContentDirectory(out manualFileSystem, ref baseFileSystem, rc = ParseDirWithPathCaseNormalizationOnCaseSensitiveHostFs(out manualFileSystem, ref baseFileSystem,
currentPath); currentPath);
if (rc.IsFailure()) return rc; 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()) if (rc.IsSuccess())
{ {
@ -151,10 +170,10 @@ namespace LibHac.FsSrv
ulong openProgramId = mountNameInfo.IsHostFs ? ulong.MaxValue : id; 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; 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); out NcaFormatType fsType, type, mountNameInfo.IsGameCard, canMountSystemDataPrivate);
if (rc.IsFailure()) return rc; 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; throw new NotImplementedException();
public int GcHandle;
public bool IsHostFs;
public bool CanMountNca;
} }
private Result OpenFileSystemFromMountName(ref U8Span path, public Result OpenStorageWithPatch(out ReferenceCountedDisposable<IStorage> storage, out Hash ncaHeaderDigest,
out ReferenceCountedDisposable<IFileSystem> fileSystem, out bool shouldContinue, out MountNameInfo info) 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; 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; shouldContinue = true;
if (StringUtils.Compare(path, CommonPaths.GameCardFileSystemMountName, if (StringUtils.Compare(path, CommonPaths.GameCardFileSystemMountName,
@ -342,7 +505,7 @@ namespace LibHac.FsSrv
return Result.Success; return Result.Success;
} }
private Result IsContentPathDir(ref U8Span path, out bool isDirectory) private Result CheckDirOrNcaOrNsp(ref U8Span path, out bool isDirectory)
{ {
isDirectory = default; isDirectory = default;
@ -381,7 +544,7 @@ namespace LibHac.FsSrv
return ResultFs.PathNotFound.Log(); return ResultFs.PathNotFound.Log();
} }
private Result TryOpenContentDirectory(U8Span path, private Result ParseDir(U8Span path,
out ReferenceCountedDisposable<IFileSystem> contentFileSystem, out ReferenceCountedDisposable<IFileSystem> contentFileSystem,
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, FileSystemProxyType fsType, bool preserveUnc) 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); Result rc = _config.SubDirectoryFsCreator.Create(out subDirFs, ref baseFileSystem, path, preserveUnc);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
return OpenSubDirectoryForFsType(out contentFileSystem, ref subDirFs, fsType); return ParseContentTypeForDirectory(out contentFileSystem, ref subDirFs, fsType);
} }
finally finally
{ {
@ -401,7 +564,7 @@ namespace LibHac.FsSrv
} }
} }
private Result TryOpenCaseSensitiveContentDirectory( private Result ParseDirWithPathCaseNormalizationOnCaseSensitiveHostFs(
out ReferenceCountedDisposable<IFileSystem> contentFileSystem, out ReferenceCountedDisposable<IFileSystem> contentFileSystem,
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path) ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path)
{ {
@ -430,60 +593,7 @@ namespace LibHac.FsSrv
return _config.SubDirectoryFsCreator.Create(out contentFileSystem, ref baseFileSystem, fullPath); return _config.SubDirectoryFsCreator.Create(out contentFileSystem, ref baseFileSystem, fullPath);
} }
private Result OpenSubDirectoryForFsType(out ReferenceCountedDisposable<IFileSystem> fileSystem, private Result ParseNsp(ref U8Span path, 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,
ReferenceCountedDisposable<IFileSystem> baseFileSystem) ReferenceCountedDisposable<IFileSystem> baseFileSystem)
{ {
fileSystem = default; fileSystem = default;
@ -542,7 +652,7 @@ namespace LibHac.FsSrv
return rc; 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) ulong ncaId)
{ {
nca = default; nca = default;
@ -570,7 +680,77 @@ namespace LibHac.FsSrv
return Result.Success; 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) out NcaFormatType fsType, FileSystemProxyType fsProxyType, bool isGameCard, bool canMountSystemDataPrivate)
{ {
ncaStorage = default; ncaStorage = default;
@ -619,10 +799,10 @@ namespace LibHac.FsSrv
if (nca.Header.DistributionType == DistributionType.GameCard && !isGameCard) if (nca.Header.DistributionType == DistributionType.GameCard && !isGameCard)
return ResultFs.PermissionDenied.Log(); return ResultFs.PermissionDenied.Log();
Result rc = SetNcaExternalKey(nca); Result rc = SetExternalKeyForRightsId(nca);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = GetNcaSectionIndex(out int sectionIndex, fsProxyType); rc = GetPartitionIndex(out int sectionIndex, fsProxyType);
if (rc.IsFailure()) return rc; if (rc.IsFailure()) return rc;
rc = _config.StorageOnNcaCreator.Create(out ncaStorage, out NcaFsHeader fsHeader, nca, rc = _config.StorageOnNcaCreator.Create(out ncaStorage, out NcaFsHeader fsHeader, nca,
@ -633,44 +813,108 @@ namespace LibHac.FsSrv
return Result.Success; return Result.Success;
} }
private Result SetNcaExternalKey(Nca nca) public Result SetSdCardEncryptionSeed(in EncryptionSeed encryptionSeed)
{ {
var rightsId = new RightsId(nca.Header.RightsId); _encryptionSeed = encryptionSeed;
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; 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)
{ {
case FileSystemProxyType.Code: Result rc = _locationResolverSet.ResolveProgramPath(out path, programId.Value, storageId);
case FileSystemProxyType.Control: if (rc.IsSuccess())
case FileSystemProxyType.Manual:
case FileSystemProxyType.Meta:
case FileSystemProxyType.Data:
index = 0;
return Result.Success; return Result.Success;
case FileSystemProxyType.Rom:
case FileSystemProxyType.RegisteredUpdate: var dataId = new DataId(programId.Value);
index = 1;
rc = _locationResolverSet.ResolveDataPath(out path, dataId, storageId);
if (rc.IsSuccess())
return Result.Success; return Result.Success;
case FileSystemProxyType.Logo:
index = 2; return ResultFs.TargetNotFound.Log();
return Result.Success; }
default:
index = default; public Result ResolveRomPath(out Path path, ulong id, StorageId storageId)
return ResultFs.InvalidArgument.Log(); {
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)
{
_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, internal Result GetProgramInfoByProcessId(out ProgramInfo programInfo, ulong processId)
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)
{ {
return _config.ProgramRegistry.GetProgramInfo(out programInfo, 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 private static ReadOnlySpan<byte> SdCardNintendoRootDirectoryName => // Nintendo
new[] new[]
{ {

View File

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

View File

@ -45,7 +45,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" /> <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> </ItemGroup>
</Project> </Project>

View File

@ -1,5 +1,6 @@
using System; using System;
using LibHac.Ncm; using LibHac.Ncm;
using LibHac.Sf;
namespace LibHac.Lr namespace LibHac.Lr
{ {
@ -21,7 +22,7 @@ namespace LibHac.Lr
public Result UnregisterAllAddOnContentPath() => public Result UnregisterAllAddOnContentPath() =>
_interface.Target.UnregisterAllAddOnContentPath(); _interface.Target.UnregisterAllAddOnContentPath();
public Result RefreshApplicationAddOnContent(ReadOnlySpan<Ncm.ApplicationId> ids) => public Result RefreshApplicationAddOnContent(InArray<Ncm.ApplicationId> ids) =>
_interface.Target.RefreshApplicationAddOnContent(ids); _interface.Target.RefreshApplicationAddOnContent(ids);
public Result UnregisterApplicationAddOnContent(Ncm.ApplicationId id) => public Result UnregisterApplicationAddOnContent(Ncm.ApplicationId id) =>

View File

@ -1,5 +1,6 @@
using System; using System;
using LibHac.Ncm; using LibHac.Ncm;
using LibHac.Sf;
namespace LibHac.Lr namespace LibHac.Lr
{ {
@ -8,7 +9,7 @@ namespace LibHac.Lr
Result ResolveAddOnContentPath(out Path path, DataId id); Result ResolveAddOnContentPath(out Path path, DataId id);
Result RegisterAddOnContentStorage(DataId id, Ncm.ApplicationId applicationId, StorageId storageId); Result RegisterAddOnContentStorage(DataId id, Ncm.ApplicationId applicationId, StorageId storageId);
Result UnregisterAllAddOnContentPath(); Result UnregisterAllAddOnContentPath();
Result RefreshApplicationAddOnContent(ReadOnlySpan<Ncm.ApplicationId> ids); Result RefreshApplicationAddOnContent(InArray<Ncm.ApplicationId> ids);
Result UnregisterApplicationAddOnContent(Ncm.ApplicationId id); Result UnregisterApplicationAddOnContent(Ncm.ApplicationId id);
} }
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using LibHac.Ncm; using LibHac.Ncm;
using LibHac.Sf;
namespace LibHac.Lr namespace LibHac.Lr
{ {
@ -16,14 +17,14 @@ namespace LibHac.Lr
Result RedirectApplicationLegalInformationPath(in Path path, ProgramId id, ProgramId ownerId); Result RedirectApplicationLegalInformationPath(in Path path, ProgramId id, ProgramId ownerId);
Result Refresh(); Result Refresh();
Result RedirectApplicationProgramPath(in Path path, ProgramId id, ProgramId ownerId); Result RedirectApplicationProgramPath(in Path path, ProgramId id, ProgramId ownerId);
Result ClearApplicationRedirection(ReadOnlySpan<ProgramId> excludingIds); Result ClearApplicationRedirection(InArray<ProgramId> excludingIds);
Result EraseProgramRedirection(ProgramId id); Result EraseProgramRedirection(ProgramId id);
Result EraseApplicationControlRedirection(ProgramId id); Result EraseApplicationControlRedirection(ProgramId id);
Result EraseApplicationHtmlDocumentRedirection(ProgramId id); Result EraseApplicationHtmlDocumentRedirection(ProgramId id);
Result EraseApplicationLegalInformationRedirection(ProgramId id); Result EraseApplicationLegalInformationRedirection(ProgramId id);
Result ResolveProgramPathForDebug(out Path path, ProgramId id); Result ResolveProgramPathForDebug(out Path path, ProgramId id);
Result RedirectProgramPathForDebug(in 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); Result EraseProgramRedirectionForDebug(ProgramId id);
} }
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using LibHac.Ncm; using LibHac.Ncm;
using LibHac.Sf;
namespace LibHac.Lr namespace LibHac.Lr
{ {
@ -45,7 +46,7 @@ namespace LibHac.Lr
public Result RedirectApplicationProgramPath(in Path path, ProgramId id, ProgramId ownerId) => public Result RedirectApplicationProgramPath(in Path path, ProgramId id, ProgramId ownerId) =>
_interface.Target.RedirectApplicationProgramPath(in path, id, ownerId); _interface.Target.RedirectApplicationProgramPath(in path, id, ownerId);
public Result ClearApplicationRedirection(ReadOnlySpan<ProgramId> excludingIds) => public Result ClearApplicationRedirection(InArray<ProgramId> excludingIds) =>
_interface.Target.ClearApplicationRedirection(excludingIds); _interface.Target.ClearApplicationRedirection(excludingIds);
public Result EraseProgramRedirection(ProgramId id) => public Result EraseProgramRedirection(ProgramId id) =>
@ -66,8 +67,8 @@ namespace LibHac.Lr
public Result RedirectProgramPathForDebug(in Path path, ProgramId id) => public Result RedirectProgramPathForDebug(in Path path, ProgramId id) =>
_interface.Target.RedirectProgramPathForDebug(in path, id); _interface.Target.RedirectProgramPathForDebug(in path, id);
public Result RedirectApplicationProgramPathForDebug(in Path path, ProgramId id) => public Result RedirectApplicationProgramPathForDebug(in Path path, ProgramId id, ProgramId ownerId) =>
_interface.Target.RedirectApplicationProgramPathForDebug(in path, id); _interface.Target.RedirectApplicationProgramPathForDebug(in path, id, ownerId);
public Result EraseProgramRedirectionForDebug(ProgramId id) => public Result EraseProgramRedirectionForDebug(ProgramId id) =>
_interface.Target.EraseProgramRedirectionForDebug(id); _interface.Target.EraseProgramRedirectionForDebug(id);

View File

@ -442,6 +442,24 @@ namespace LibHac
return TryAddReferenceImpl(target, referenceCount); 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> /// <summary>
/// Increments the reference count for the disposable object, and returns a new disposable reference to /// Increments the reference count for the disposable object, and returns a new disposable reference to
/// it. /// it.
@ -459,6 +477,36 @@ namespace LibHac
/// underlying object has already been disposed.</returns> /// underlying object has already been disposed.</returns>
public ReferenceCountedDisposable<T> AddReference() => public ReferenceCountedDisposable<T> AddReference() =>
TryAddReference() ?? throw new ObjectDisposedException(nameof(WeakReference)); 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)); 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];
}
} }