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