mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2025-02-09 13:14:46 +01:00
Add more NcaFileSystemService functions
This commit is contained in:
parent
6b6a31dcea
commit
0efaafd101
@ -7,20 +7,10 @@ namespace LibHac.FsSrv.Impl
|
|||||||
{
|
{
|
||||||
public class AsynchronousAccessFileSystem : ForwardingFileSystem
|
public 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)
|
||||||
{
|
{
|
||||||
|
58
src/LibHac/FsSrv/Impl/DeepRetryFileSystem.cs
Normal file
58
src/LibHac/FsSrv/Impl/DeepRetryFileSystem.cs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.Fs.Fsa;
|
||||||
|
using LibHac.FsSystem;
|
||||||
|
|
||||||
|
namespace LibHac.FsSrv.Impl
|
||||||
|
{
|
||||||
|
public class DeepRetryFileSystem : ForwardingFileSystem
|
||||||
|
{
|
||||||
|
private ReferenceCountedDisposable<DeepRetryFileSystem>.WeakReference SelfReference { get; set; }
|
||||||
|
private ReferenceCountedDisposable<IRomFileSystemAccessFailureManager> AccessFailureManager { get; set; }
|
||||||
|
|
||||||
|
protected DeepRetryFileSystem(ref ReferenceCountedDisposable<IFileSystem> baseFileSystem,
|
||||||
|
ref ReferenceCountedDisposable<IRomFileSystemAccessFailureManager> accessFailureManager) : base(
|
||||||
|
ref baseFileSystem)
|
||||||
|
{
|
||||||
|
AccessFailureManager = Shared.Move(ref accessFailureManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ReferenceCountedDisposable<IFileSystem> CreateShared(
|
||||||
|
ref ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||||
|
ref ReferenceCountedDisposable<IRomFileSystemAccessFailureManager> accessFailureManager)
|
||||||
|
{
|
||||||
|
ReferenceCountedDisposable<DeepRetryFileSystem> sharedRetryFileSystem = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var retryFileSystem = new DeepRetryFileSystem(ref fileSystem, ref accessFailureManager);
|
||||||
|
sharedRetryFileSystem = new ReferenceCountedDisposable<DeepRetryFileSystem>(retryFileSystem);
|
||||||
|
|
||||||
|
retryFileSystem.SelfReference =
|
||||||
|
new ReferenceCountedDisposable<DeepRetryFileSystem>.WeakReference(sharedRetryFileSystem);
|
||||||
|
|
||||||
|
return sharedRetryFileSystem.AddReference<IFileSystem>();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
sharedRetryFileSystem?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
AccessFailureManager?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReSharper disable once RedundantOverriddenMember
|
||||||
|
protected override Result DoOpenFile(out IFile file, U8Span path, OpenMode mode)
|
||||||
|
{
|
||||||
|
// Todo: Implement
|
||||||
|
return base.DoOpenFile(out file, path, mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,17 +1,18 @@
|
|||||||
using LibHac.Fs;
|
using System;
|
||||||
|
using LibHac.Fs;
|
||||||
using LibHac.FsSystem;
|
using LibHac.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();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
@ -62,7 +181,7 @@ namespace LibHac.FsSrv
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Result OpenDataStorageCore(out ReferenceCountedDisposable<IStorage> storage, out Hash ncaHeaderDigest,
|
private Result OpenDataStorageCore(out ReferenceCountedDisposable<IStorage> storage, out Hash ncaHeaderDigest,
|
||||||
ulong id, StorageId storageId)
|
ulong id, StorageId storageId)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
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
|
// 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)
|
||||||
|
{
|
||||||
|
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:
|
_romFsRemountForDataCorruptionCount++;
|
||||||
case FileSystemProxyType.Control:
|
}
|
||||||
case FileSystemProxyType.Manual:
|
}
|
||||||
case FileSystemProxyType.Meta:
|
|
||||||
case FileSystemProxyType.Data:
|
public void IncrementRomFsUnrecoverableDataCorruptionByRemountCount()
|
||||||
index = 0;
|
{
|
||||||
return Result.Success;
|
lock (_romfsCountLocker)
|
||||||
case FileSystemProxyType.Rom:
|
{
|
||||||
case FileSystemProxyType.RegisteredUpdate:
|
_romfsUnrecoverableDataCorruptionByRemountCount++;
|
||||||
index = 1;
|
}
|
||||||
return Result.Success;
|
}
|
||||||
case FileSystemProxyType.Logo:
|
|
||||||
index = 2;
|
public void IncrementRomFsRecoveredByInvalidateCacheCount()
|
||||||
return Result.Success;
|
{
|
||||||
default:
|
lock (_romfsCountLocker)
|
||||||
index = default;
|
{
|
||||||
return ResultFs.InvalidArgument.Log();
|
_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[]
|
||||||
{
|
{
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
@ -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) =>
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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.")
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user