From 4a7b67bf5a80d87164a02f4d95703000831fece0 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Sun, 28 Apr 2024 15:51:09 -0700 Subject: [PATCH] Update NcaFileSystemService --- src/LibHac/FsSrv/FileSystemProxyImpl.cs | 4 +- src/LibHac/FsSrv/Impl/DeepRetryStorage.cs | 2 +- .../IRomFileSystemAccessFailureManager.cs | 2 +- .../Impl/SystemDataUpdateEventNotifier.cs | 3 +- src/LibHac/FsSrv/NcaFileSystemService.cs | 581 ++++++++++++++++-- src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs | 20 +- src/LibHac/FsSrv/Sf/IFileSystemProxy.cs | 2 +- src/LibHac/FsSystem/SemaphoreAdapter.cs | 26 + src/LibHac/FsSystem/Utility.cs | 84 +++ 9 files changed, 638 insertions(+), 86 deletions(-) diff --git a/src/LibHac/FsSrv/FileSystemProxyImpl.cs b/src/LibHac/FsSrv/FileSystemProxyImpl.cs index 8f1f3758..6738035d 100644 --- a/src/LibHac/FsSrv/FileSystemProxyImpl.cs +++ b/src/LibHac/FsSrv/FileSystemProxyImpl.cs @@ -263,13 +263,13 @@ public class FileSystemProxyImpl : IFileSystemProxy, IFileSystemProxyForLoader return ncaFsService.OpenDataFileSystemByDataId(ref outFileSystem, dataId, storageId).Ret(); } - public Result OpenDataStorageByPath(ref SharedRef outFileSystem, ref readonly FspPath path, + public Result OpenDataStorageByPath(ref SharedRef outStorage, ref readonly FspPath path, ContentAttributes attributes, FileSystemProxyType fsType) { Result res = GetNcaFileSystemService(out NcaFileSystemService ncaFsService); if (res.IsFailure()) return res.Miss(); - return ncaFsService.OpenDataStorageByPath(ref outFileSystem, in path, attributes, fsType).Ret(); + return ncaFsService.OpenDataStorageByPath(ref outStorage, in path, attributes, fsType).Ret(); } public Result OpenPatchDataStorageByCurrentProcess(ref SharedRef outStorage) diff --git a/src/LibHac/FsSrv/Impl/DeepRetryStorage.cs b/src/LibHac/FsSrv/Impl/DeepRetryStorage.cs index 2b29eebd..e6a803e9 100644 --- a/src/LibHac/FsSrv/Impl/DeepRetryStorage.cs +++ b/src/LibHac/FsSrv/Impl/DeepRetryStorage.cs @@ -152,7 +152,7 @@ public class DeepRetryStorage : IStorage for (int i = 0; i < maxRetryCount; i++) { retryResult = _parent.Get.OpenDataStorageCore(ref remountStorage.Ref, ref remountStorageAccessSplitter.Ref, - out digest, _dataStorageContext.GetProgramIdValue(), _dataStorageContext.GetStorageId()); + ref digest, _dataStorageContext.GetProgramIdValue(), _dataStorageContext.GetStorageId()); if (!ResultFs.DataCorrupted.Includes(retryResult)) break; diff --git a/src/LibHac/FsSrv/Impl/IRomFileSystemAccessFailureManager.cs b/src/LibHac/FsSrv/Impl/IRomFileSystemAccessFailureManager.cs index 459b986c..efe76eed 100644 --- a/src/LibHac/FsSrv/Impl/IRomFileSystemAccessFailureManager.cs +++ b/src/LibHac/FsSrv/Impl/IRomFileSystemAccessFailureManager.cs @@ -8,7 +8,7 @@ namespace LibHac.FsSrv.Impl; public interface IRomFileSystemAccessFailureManager : IDisposable { - Result OpenDataStorageCore(ref SharedRef outStorage, ref SharedRef outStorageAccessSplitter, out Hash ncaHeaderDigest, ulong id, StorageId storageId); + Result OpenDataStorageCore(ref SharedRef outStorage, ref SharedRef outStorageAccessSplitter, ref Hash outNcaDigest, ulong id, StorageId storageId); Result HandleResolubleAccessFailure(out bool wasDeferred, Result nonDeferredResult); void IncrementRomFsDeepRetryStartCount(); void IncrementRomFsRemountForDataCorruptionCount(); diff --git a/src/LibHac/FsSrv/Impl/SystemDataUpdateEventNotifier.cs b/src/LibHac/FsSrv/Impl/SystemDataUpdateEventNotifier.cs index a7fc3855..d3aa3719 100644 --- a/src/LibHac/FsSrv/Impl/SystemDataUpdateEventNotifier.cs +++ b/src/LibHac/FsSrv/Impl/SystemDataUpdateEventNotifier.cs @@ -1,9 +1,10 @@ using System; +using LibHac.FsSrv.Sf; using LibHac.Sf; namespace LibHac.FsSrv.Impl; -public class SystemDataUpdateEventNotifier : IDisposable +public class SystemDataUpdateEventNotifier : IEventNotifier { public SystemDataUpdateEventNotifier() { diff --git a/src/LibHac/FsSrv/NcaFileSystemService.cs b/src/LibHac/FsSrv/NcaFileSystemService.cs index 88f5a64a..668126f0 100644 --- a/src/LibHac/FsSrv/NcaFileSystemService.cs +++ b/src/LibHac/FsSrv/NcaFileSystemService.cs @@ -1,7 +1,7 @@ -// ReSharper disable UnusedMember.Local UnusedType.Local -#pragma warning disable CS0169 // Field is never used -using System; +using System; +using System.Runtime.CompilerServices; using LibHac.Common; +using LibHac.Diag; using LibHac.Fs; using LibHac.FsSrv.Impl; using LibHac.FsSrv.Sf; @@ -10,12 +10,12 @@ using LibHac.Lr; using LibHac.Ncm; using LibHac.Sf; using LibHac.Spl; +using LibHac.Util; using IFileSystem = LibHac.Fs.Fsa.IFileSystem; -using IStorage = LibHac.Fs.IStorage; using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem; +using IStorage = LibHac.Fs.IStorage; using IStorageSf = LibHac.FsSrv.Sf.IStorage; using Path = LibHac.Fs.Path; -using Utility = LibHac.FsSrv.Impl.Utility; namespace LibHac.FsSrv; @@ -32,7 +32,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager private const int RomDivisionSizeUnitCountSemaphoreCount = 128; private WeakRef _selfReference; - private NcaFileSystemServiceImpl _serviceImpl; + private readonly NcaFileSystemServiceImpl _serviceImpl; private ulong _processId; private SemaphoreAdapter _aocMountCountSemaphore; private SemaphoreAdapter _romMountCountSemaphore; @@ -47,6 +47,12 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager _romDivisionSizeUnitCountSemaphore = new SemaphoreAdapter(RomDivisionSizeUnitCountSemaphoreCount, RomDivisionSizeUnitCountSemaphoreCount); } + private SharedRef GetSharedFromThis() => + SharedRef.Create(in _selfReference); + + private SharedRef GetSharedAccessFailureManagerFromThis() => + SharedRef.Create(in _selfReference); + public static SharedRef CreateShared(NcaFileSystemServiceImpl serviceImpl, ulong processId) { @@ -87,7 +93,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager } public Result OpenFileSystemWithPatch(ref SharedRef outFileSystem, ProgramId programId, - FileSystemProxyType fsType) + FileSystemProxyType type) { const StorageLayoutType storageFlag = StorageLayoutType.All; using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag); @@ -96,7 +102,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager Result res = GetProgramInfo(out ProgramInfo callerProgramInfo); if (res.IsFailure()) return res.Miss(); - switch (fsType) + switch (type) { case FileSystemProxyType.Manual: Accessibility accessibility = @@ -144,7 +150,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager return originalResult.Miss(); // There is an original version and no patch version. Open the original directly - res = _serviceImpl.OpenFileSystem(ref fileSystem.Ref, in originalPath, contentAttributes, fsType, + res = _serviceImpl.OpenFileSystem(ref fileSystem.Ref, in originalPath, contentAttributes, type, programId.Value, isDirectory); if (res.IsFailure()) return res.Miss(); } @@ -159,25 +165,16 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager // Open the file system using both the original and patch versions res = _serviceImpl.OpenFileSystemWithPatch(ref fileSystem.Ref, in originalNcaPath, contentAttributes, - in patchPath, patchContentAttributes, fsType, originalProgramId, programId.Value); + in patchPath, patchContentAttributes, type, originalProgramId, programId.Value); if (res.IsFailure()) return res.Miss(); } // Add all the file system wrappers - using var typeSetFileSystem = - new SharedRef(new StorageLayoutTypeSetFileSystem(ref fileSystem.Ref, storageFlag)); - - using var asyncFileSystem = - new SharedRef(new AsynchronousAccessFileSystem(ref typeSetFileSystem.Ref)); - - using SharedRef accessFailureManager = - SharedRef.Create(in _selfReference); - - using SharedRef retryFileSystem = - DeepRetryFileSystem.CreateShared(ref asyncFileSystem.Ref, ref accessFailureManager.Ref); - - using SharedRef fileSystemAdapter = - FileSystemInterfaceAdapter.CreateShared(ref retryFileSystem.Ref, false); + using var typeSetFileSystem = new SharedRef(new StorageLayoutTypeSetFileSystem(ref fileSystem.Ref, storageFlag)); + using var asyncFileSystem = new SharedRef(new AsynchronousAccessFileSystem(ref typeSetFileSystem.Ref)); + using SharedRef accessFailureManager = SharedRef.Create(in _selfReference); + using SharedRef retryFileSystem = DeepRetryFileSystem.CreateShared(ref asyncFileSystem.Ref, ref accessFailureManager.Ref); + using SharedRef fileSystemAdapter = FileSystemInterfaceAdapter.CreateShared(ref retryFileSystem.Ref, false); outFileSystem.SetByMove(ref fileSystemAdapter.Ref); @@ -187,34 +184,128 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager public Result OpenCodeFileSystem(ref SharedRef outFileSystem, OutBuffer outVerificationData, ref readonly FspPath path, ContentAttributes attributes, ProgramId programId) { - throw new NotImplementedException(); + StorageLayoutType storageFlag = _serviceImpl.GetStorageFlag(programId.Value); + using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag); + + if (!_serviceImpl.FsServer.IsInitialProgram(_processId)) + return ResultFs.PermissionDenied.Log(); + + bool isDirectory = PathUtility.IsDirectoryPath(in path); + using var pathNormalized = new Path(); + Result res = pathNormalized.InitializeWithReplaceUnc(path.Str); + if (res.IsFailure()) return res.Miss(); + + var flags = new PathFlags(); + flags.AllowMountName(); + flags.AllowWindowsPath(); + res = pathNormalized.Normalize(flags); + if (res.IsFailure()) return res.Miss(); + + if (outVerificationData.Size != Unsafe.SizeOf()) + return ResultFs.InvalidArgument.Log(); + + using var fileSystem = new SharedRef(); + res = _serviceImpl.OpenFileSystem(ref fileSystem.Ref, ref outVerificationData.As(), + in pathNormalized, attributes, FileSystemProxyType.Code, canMountSystemDataPrivate: false, programId.Value, + isDirectory); + if (res.IsFailure()) return res.Miss(); + + // Add all the file system wrappers + using var typeSetFileSystem = new SharedRef(new StorageLayoutTypeSetFileSystem(ref fileSystem.Ref, storageFlag)); + using var asyncFileSystem = new SharedRef(new AsynchronousAccessFileSystem(ref typeSetFileSystem.Ref)); + using SharedRef fileSystemAdapter = FileSystemInterfaceAdapter.CreateShared(ref asyncFileSystem.Ref, allowAllOperations: false); + + outFileSystem.SetByMove(ref fileSystemAdapter.Ref); + + return Result.Success; } public Result OpenDataFileSystemByCurrentProcess(ref SharedRef outFileSystem) { - throw new NotImplementedException(); - } + Result res = GetProgramInfo(out ProgramInfo programInfo); + if (res.IsFailure()) return res.Miss(); - public Result OpenDataStorageByPath(ref SharedRef outFileSystem, ref readonly FspPath path, - ContentAttributes attributes, FileSystemProxyType fsType) - { - throw new NotImplementedException(); + using var fileSystem = new SharedRef(); + res = OpenDataFileSystemCore(ref fileSystem.Ref, out bool isHostFs, programInfo.ProgramId.Value, programInfo.StorageId); + if (res.IsFailure()) return res.Miss(); + + using var asyncFileSystem = new SharedRef(); + + if (isHostFs) + { + asyncFileSystem.SetByMove(ref fileSystem.Ref); + } + else + { + asyncFileSystem.Reset(new AsynchronousAccessFileSystem(ref fileSystem.Ref)); + } + + using SharedRef fileSystemAdapter = + FileSystemInterfaceAdapter.CreateShared(ref asyncFileSystem.Ref, allowAllOperations: false); + + outFileSystem.SetByMove(ref fileSystemAdapter.Ref); + + return Result.Success; } private Result TryAcquireAddOnContentDivisionSizeUnitCountSemaphore(ref UniqueRef outSemaphore, IStorage storage) { - throw new NotImplementedException(); + Result res = storage.GetSize(out long storageSize); + if (res.IsFailure()) return res.Miss(); + + int divisionCount = (int)BitUtil.DivideUp(storageSize, _serviceImpl.GetAddOnContentDivisionSize()); + + using SharedRef ncaService = GetSharedFromThis(); + res = FsSystem.Utility.MakeUniqueLockWithPin(ref outSemaphore, _aocMountCountSemaphore, divisionCount, in ncaService); + + if (!res.IsSuccess()) + { + if (ResultFs.OpenCountLimit.Includes(res)) + return ResultFs.AocMountDivisionSizeUnitCountLimit.LogConverted(res); + + return res.Miss(); + } + + return Result.Success; } private Result TryAcquireRomMountCountSemaphore(ref UniqueRef outSemaphore) { - throw new NotImplementedException(); + using SharedRef ncaService = GetSharedFromThis(); + Result res = FsSystem.Utility.MakeUniqueLockWithPin(ref outSemaphore, _romMountCountSemaphore, ref ncaService.Ref); + + if (!res.IsSuccess()) + { + if (ResultFs.OpenCountLimit.Includes(res)) + return ResultFs.RomMountCountLimit.LogConverted(res); + + return res.Miss(); + } + + return Result.Success; } private Result TryAcquireRomDivisionSizeUnitCountSemaphore(ref UniqueRef outSemaphore, ref UniqueRef mountCountSemaphore, IStorage storage) { - throw new NotImplementedException(); + Result res = storage.GetSize(out long storageSize); + if (res.IsFailure()) return res.Miss(); + + int divisionCount = (int)BitUtil.DivideUp(storageSize, _serviceImpl.GetRomDivisionSize()); + + using SharedRef ncaService = GetSharedFromThis(); + res = FsSystem.Utility.MakeUniqueLockWithPin(ref outSemaphore, ref mountCountSemaphore, + _romDivisionSizeUnitCountSemaphore, divisionCount, in ncaService); + + if (!res.IsSuccess()) + { + if (ResultFs.OpenCountLimit.Includes(res)) + return ResultFs.RomMountDivisionSizeUnitCountLimit.LogConverted(res); + + return res.Miss(); + } + + return Result.Success; } public void IncrementRomFsDeepRetryStartCount() @@ -243,30 +334,193 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager } private Result OpenDataStorageCore(ref SharedRef outStorage, - ref SharedRef outStorageAccessSplitter, out Hash ncaHeaderDigest, ulong id, + ref SharedRef outStorageAccessSplitter, ref Hash outNcaDigest, ulong id, StorageId storageId) { - throw new NotImplementedException(); + using var programPath = new Path(); + Result originalResult = _serviceImpl.ResolveRomPath(out bool isDirectory, ref programPath.Ref(), + out ContentAttributes contentAttributes, out ulong originalProgramId, id, storageId); + + using var patchPath = new Path(); + Result patchResult = _serviceImpl.ResolveRegisteredProgramPath(ref patchPath.Ref(), + out ContentAttributes patchContentAttributes, id); + + using var storage = new SharedRef(); + using var storageAccessSplitter = new SharedRef(); + + if (ResultLr.ProgramNotFound.Includes(patchResult)) + { + // If a patch NCA wasn't found, operate on just the original NCA. + // We can't open a storage if the content is from a directory + if (isDirectory) + return ResultFs.TargetNotFound.Log(); + + // Since we couldn't find a patch NCA, make sure we successfully found an original NCA + if (originalResult.IsFailure()) + return originalResult.Miss(); + + Result res = _serviceImpl.OpenDataStorage(ref storage.Ref, ref storageAccessSplitter.Ref, + ref outNcaDigest, in programPath, contentAttributes, FileSystemProxyType.Rom, id); + if (res.IsFailure()) return res.Miss(); + } + else + { + if (patchResult.IsFailure()) + return patchResult.Miss(); + + ref readonly Path originalNcaPath = ref originalResult.IsSuccess() + ? ref programPath + : ref PathExtensions.GetNullRef(); + + Result res = _serviceImpl.OpenStorageWithPatch(ref storage.Ref, ref storageAccessSplitter.Ref, + ref outNcaDigest, in originalNcaPath, contentAttributes, in patchPath, patchContentAttributes, + FileSystemProxyType.Rom, originalProgramId, id); + if (res.IsFailure()) return res.Miss(); + } + + outStorage.SetByMove(ref storage.Ref); + outStorageAccessSplitter.SetByMove(ref storageAccessSplitter.Ref); + + return Result.Success; } public Result OpenDataStorageByCurrentProcess(ref SharedRef outStorage) { - throw new NotImplementedException(); + Result res = GetProgramInfo(out ProgramInfo programInfo); + if (res.IsFailure()) return res.Miss(); + + StorageLayoutType storageFlag = _serviceImpl.GetStorageFlag(programInfo.ProgramIdValue); + using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag); + + Hash digest = default; + + using var romMountCountSemaphore = new UniqueRef(); + res = TryAcquireRomMountCountSemaphore(ref romMountCountSemaphore.Ref); + if (res.IsFailure()) return res.Miss(); + + StorageId storageId = programInfo.StorageId; + using var storage = new SharedRef(); + using var storageAccessSplitter = new SharedRef(); + res = OpenDataStorageCore(ref storage.Ref, ref storageAccessSplitter.Ref, ref digest, programInfo.ProgramIdValue, storageId); + if (res.IsFailure()) return res.Miss(); + + using var romDivisionSizeUnitCountSemaphore = new UniqueRef(); + res = TryAcquireRomDivisionSizeUnitCountSemaphore(ref romDivisionSizeUnitCountSemaphore.Ref, + ref romMountCountSemaphore.Ref, storage.Get); + if (res.IsFailure()) return res.Miss(); + + using SharedRef accessFailureManager = GetSharedAccessFailureManagerFromThis(); + + using var retryStorage = new SharedRef(new DeepRetryStorage(in storage, in storageAccessSplitter, + in accessFailureManager, ref romDivisionSizeUnitCountSemaphore.Ref, in digest, programInfo.ProgramIdValue, + storageId, _serviceImpl.FsServer)); + + using var typeSetStorage = new SharedRef(new StorageLayoutTypeSetStorage(ref retryStorage.Ref, storageFlag)); + using var storageAdapter = new SharedRef(new StorageInterfaceAdapter(ref typeSetStorage.Ref)); + + outStorage.SetByMove(ref storageAdapter.Ref); + + return Result.Success; } public Result OpenDataStorageByPath(ref SharedRef outStorage, in FspPath path, - ContentAttributes attributes, FileSystemProxyType fspType) + ContentAttributes attributes, FileSystemProxyType type) { - throw new NotImplementedException(); + Result res = GetProgramInfo(out ProgramInfo programInfo); + if (res.IsFailure()) return res.Miss(); + + StorageLayoutType storageFlag = _serviceImpl.GetStorageFlag(programInfo.ProgramIdValue); + using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag); + + if (!programInfo.AccessControl.CanCall(OperationType.OpenDataStorageByPath)) + return ResultFs.PermissionDenied.Log(); + + using var storage = new SharedRef(); + Hash digest = default; + + using var romMountCountSemaphore = new UniqueRef(); + res = TryAcquireRomMountCountSemaphore(ref romMountCountSemaphore.Ref); + if (res.IsFailure()) return res.Miss(); + + using var ncaPath = new Path(); + res = ncaPath.Initialize(path.Str); + if (res.IsFailure()) return res.Miss(); + + var flags = new PathFlags(); + flags.AllowMountName(); + res = ncaPath.Normalize(flags); + if (res.IsFailure()) return res.Miss(); + + using var storageAccessSplitter = new SharedRef(); + res = _serviceImpl.OpenDataStorage(ref storage.Ref, ref storageAccessSplitter.Ref, ref digest, in ncaPath, + attributes, type, ulong.MaxValue); + if (res.IsFailure()) return res.Miss(); + + using var romDivisionSizeUnitCountSemaphore = new UniqueRef(); + res = TryAcquireRomDivisionSizeUnitCountSemaphore(ref romDivisionSizeUnitCountSemaphore.Ref, + ref romMountCountSemaphore.Ref, storage.Get); + if (res.IsFailure()) return res.Miss(); + + using SharedRef accessFailureManager = GetSharedAccessFailureManagerFromThis(); + + using var retryStorage = new SharedRef(new DeepRetryStorage(in storage, in storageAccessSplitter, + in accessFailureManager, ref romDivisionSizeUnitCountSemaphore.Ref, in digest, ProgramId.InvalidId.Value, + StorageId.None, _serviceImpl.FsServer)); + + using var typeSetStorage = new SharedRef(new StorageLayoutTypeSetStorage(ref retryStorage.Ref, storageFlag)); + using var storageAdapter = new SharedRef(new StorageInterfaceAdapter(ref typeSetStorage.Ref)); + + outStorage.SetByMove(ref storageAdapter.Ref); + + return Result.Success; } public Result OpenDataStorageByProgramId(ref SharedRef outStorage, ProgramId programId) { - throw new NotImplementedException(); + StorageLayoutType storageFlag = _serviceImpl.GetStorageFlag(programId.Value); + using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag); + + Result res = GetProgramInfoByProgramId(out ProgramInfo programInfo, programId.Value); + if (res.IsFailure()) return res.Miss(); + + Hash digest = default; + + using var romMountCountSemaphore = new UniqueRef(); + res = TryAcquireRomMountCountSemaphore(ref romMountCountSemaphore.Ref); + if (res.IsFailure()) return res.Miss(); + + using var storage = new SharedRef(); + using var storageAccessSplitter = new SharedRef(); + res = OpenDataStorageCore(ref storage.Ref, ref storageAccessSplitter.Ref, ref digest, programId.Value, programInfo.StorageId); + if (res.IsFailure()) return res.Miss(); + + res = GetProgramInfo(out ProgramInfo properProgramInfo); + if (res.IsFailure()) return res.Miss(); + + if (!properProgramInfo.AccessControl.HasContentOwnerId(programId.Value)) + return ResultFs.PermissionDenied.Log(); + + using var romDivisionSizeUnitCountSemaphore = new UniqueRef(); + res = TryAcquireRomDivisionSizeUnitCountSemaphore(ref romDivisionSizeUnitCountSemaphore.Ref, + ref romMountCountSemaphore.Ref, storage.Get); + if (res.IsFailure()) return res.Miss(); + + using SharedRef accessFailureManager = GetSharedAccessFailureManagerFromThis(); + + using var retryStorage = new SharedRef(new DeepRetryStorage(in storage, in storageAccessSplitter, + in accessFailureManager, ref romDivisionSizeUnitCountSemaphore.Ref, in digest, programId.Value, + programInfo.StorageId, _serviceImpl.FsServer)); + + using var typeSetStorage = new SharedRef(new StorageLayoutTypeSetStorage(ref retryStorage.Ref, storageFlag)); + using var storageAdapter = new SharedRef(new StorageInterfaceAdapter(ref typeSetStorage.Ref)); + + outStorage.SetByMove(ref storageAdapter.Ref); + + return Result.Success; } public Result OpenFileSystemWithId(ref SharedRef outFileSystem, in FspPath path, - ContentAttributes attributes, ulong id, FileSystemProxyType fsType) + ContentAttributes attributes, ulong id, FileSystemProxyType type) { const StorageLayoutType storageFlag = StorageLayoutType.All; using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag); @@ -276,7 +530,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager AccessControl ac = programInfo.AccessControl; - switch (fsType) + switch (type) { case FileSystemProxyType.Logo: if (!ac.GetAccessibilityFor(AccessibilityType.MountLogo).CanRead) @@ -306,7 +560,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager return ResultFs.InvalidArgument.Log(); } - if (fsType == FileSystemProxyType.Meta) + if (type == FileSystemProxyType.Meta) { id = ulong.MaxValue; } @@ -330,7 +584,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager bool isDirectory = PathUtility.IsDirectoryPath(in path); using var fileSystem = new SharedRef(); - res = _serviceImpl.OpenFileSystem(ref fileSystem.Ref, in pathNormalized, attributes, fsType, + res = _serviceImpl.OpenFileSystem(ref fileSystem.Ref, in pathNormalized, attributes, type, canMountSystemDataPrivate, id, isDirectory); if (res.IsFailure()) return res.Miss(); @@ -342,7 +596,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager new SharedRef(new AsynchronousAccessFileSystem(ref fileSystem.Ref)); using SharedRef fileSystemAdapter = - FileSystemInterfaceAdapter.CreateShared(ref asyncFileSystem.Ref, false); + FileSystemInterfaceAdapter.CreateShared(ref asyncFileSystem.Ref, allowAllOperations: false); outFileSystem.SetByMove(ref fileSystemAdapter.Ref); @@ -351,22 +605,154 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager public Result OpenDataFileSystemByProgramId(ref SharedRef outFileSystem, ProgramId programId) { - throw new NotImplementedException(); + Result res = GetProgramInfoByProgramId(out ProgramInfo programInfo, programId.Value); + if (res.IsFailure()) return res.Miss(); + + using var fileSystem = new SharedRef(); + res = OpenDataFileSystemCore(ref fileSystem.Ref, out _, programInfo.ProgramId.Value, programInfo.StorageId); + if (res.IsFailure()) return res.Miss(); + + res = GetProgramInfo(out ProgramInfo properProgramInfo); + if (res.IsFailure()) return res.Miss(); + + if (!properProgramInfo.AccessControl.HasContentOwnerId(programId.Value)) + return ResultFs.PermissionDenied.Log(); + + using var asyncFileSystem = new SharedRef(new AsynchronousAccessFileSystem(ref fileSystem.Ref)); + + using SharedRef fileSystemAdapter = + FileSystemInterfaceAdapter.CreateShared(ref asyncFileSystem.Ref, allowAllOperations: false); + + outFileSystem.SetByMove(ref fileSystemAdapter.Ref); + + return Result.Success; } public Result OpenDataStorageByDataId(ref SharedRef outStorage, DataId dataId, StorageId storageId) { - throw new NotImplementedException(); + Result res; + + StorageLayoutType storageFlag = _serviceImpl.GetStorageFlag(dataId.Value); + using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag); + + bool isAoc = storageId == StorageId.None; + + using var systemDataPath = new Path(); + using var systemDataPatchPath = new Path(); + + ContentAttributes contentAttributes; + ContentAttributes patchContentAttributes = ContentAttributes.None; + + if (isAoc) + { + res = _serviceImpl.ResolveAddOnContentPath(ref systemDataPath.Ref(), out contentAttributes, + ref systemDataPatchPath.Ref(), out patchContentAttributes, dataId); + if (res.IsFailure()) return res.Miss(); + } + else + { + res = _serviceImpl.ResolveDataPath(ref systemDataPath.Ref(), out contentAttributes, dataId, storageId); + if (res.IsFailure()) return res.Miss(); + + Assert.SdkAssert(systemDataPatchPath.IsEmpty()); + } + + res = GetProgramInfo(out ProgramInfo programInfo); + if (res.IsFailure()) return res.Miss(); + + var accessibility = programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountSystemDataPrivate); + bool canMountSystemDataPrivate = accessibility.CanRead; + + using var storage = new SharedRef(); + using var storageAccessSplitter = new SharedRef(); + + if (systemDataPatchPath.IsEmpty()) + { + res = _serviceImpl.OpenDataStorage(ref storage.Ref, ref storageAccessSplitter.Ref, + ref Unsafe.NullRef(), in systemDataPath, contentAttributes, FileSystemProxyType.Data, + dataId.Value, canMountSystemDataPrivate); + if (res.IsFailure()) return res.Miss(); + } + else + { + res = _serviceImpl.OpenStorageWithPatch(ref storage.Ref, ref storageAccessSplitter.Ref, + ref Unsafe.NullRef(), in systemDataPath, contentAttributes, in systemDataPatchPath, + patchContentAttributes, FileSystemProxyType.Data, dataId.Value, dataId.Value, canMountSystemDataPrivate); + if (res.IsFailure()) return res.Miss(); + } + + using var mountCountSemaphore = new UniqueRef(); + + if (isAoc) + { + res = TryAcquireAddOnContentDivisionSizeUnitCountSemaphore(ref mountCountSemaphore.Ref, storage.Get); + if (res.IsFailure()) return res.Miss(); + } + + using SharedRef accessFailureManager = GetSharedAccessFailureManagerFromThis(); + + using var retryStorage = new SharedRef(new DeepRetryStorage(in storage, in storageAccessSplitter, + in accessFailureManager, ref mountCountSemaphore.Ref, deepRetryEnabled: isAoc, _serviceImpl.FsServer)); + + using var typeSetStorage = new SharedRef(new StorageLayoutTypeSetStorage(ref retryStorage.Ref, storageFlag)); + using var storageAdapter = new SharedRef(new StorageInterfaceAdapter(ref typeSetStorage.Ref)); + + outStorage.SetByMove(ref storageAdapter.Ref); + + return Result.Success; } public Result OpenDataFileSystemByDataId(ref SharedRef outFileSystem, DataId dataId, StorageId storageId) { - throw new NotImplementedException(); + Result res; + + StorageLayoutType storageFlag = _serviceImpl.GetStorageFlag(dataId.Value); + using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag); + + bool isAoc = storageId == StorageId.None; + + using var dataPath = new Path(); + using var dataPatchPathUnused = new Path(); + + ContentAttributes contentAttributes; + + if (isAoc) + { + res = _serviceImpl.ResolveAddOnContentPath(ref dataPath.Ref(), out contentAttributes, + ref dataPatchPathUnused.Ref(), out _, dataId); + if (res.IsFailure()) return res.Miss(); + } + else + { + res = _serviceImpl.ResolveDataPath(ref dataPath.Ref(), out contentAttributes, dataId, storageId); + if (res.IsFailure()) return res.Miss(); + + Assert.SdkAssert(dataPatchPathUnused.IsEmpty()); + } + + using var fileSystem = new SharedRef(); + res = _serviceImpl.OpenDataFileSystem(ref fileSystem.Ref, in dataPath, contentAttributes, + FileSystemProxyType.Data, dataId.Value, isDirectory: true); + + if (!res.IsSuccess()) + { + if (ResultFs.PathNotFound.Includes(res)) + return ResultFs.TargetNotFound.LogConverted(res); + + return res.Miss(); + } + + using var typeSetFileSystem = new SharedRef(new StorageLayoutTypeSetFileSystem(ref fileSystem.Ref, storageFlag)); + using SharedRef fileSystemAdapter = FileSystemInterfaceAdapter.CreateShared(ref typeSetFileSystem.Ref, allowAllOperations: false); + + outFileSystem.SetByMove(ref fileSystemAdapter.Ref); + + return Result.Success; } public Result OpenPatchDataStorageByCurrentProcess(ref SharedRef outStorage) { - throw new NotImplementedException(); + return ResultFs.TargetNotFound.Log(); } public Result OpenDataFileSystemWithProgramIndex(ref SharedRef outFileSystem, byte programIndex) @@ -391,11 +777,8 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager } // Add all the file system wrappers - using var asyncFileSystem = - new SharedRef(new AsynchronousAccessFileSystem(ref fileSystem.Ref)); - - using SharedRef fileSystemAdapter = - FileSystemInterfaceAdapter.CreateShared(ref asyncFileSystem.Ref, false); + using var asyncFileSystem = new SharedRef(new AsynchronousAccessFileSystem(ref fileSystem.Ref)); + using SharedRef fileSystemAdapter = FileSystemInterfaceAdapter.CreateShared(ref asyncFileSystem.Ref, allowAllOperations: false); outFileSystem.SetByMove(ref fileSystemAdapter.Ref); @@ -414,13 +797,15 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager StorageLayoutType storageFlag = _serviceImpl.GetStorageFlag(targetProgramId.Value); using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag); + Hash digest = default; + using var romMountCountSemaphore = new UniqueRef(); res = TryAcquireRomMountCountSemaphore(ref romMountCountSemaphore.Ref); if (res.IsFailure()) return res.Miss(); using var storage = new SharedRef(); using var storageAccessSplitter = new SharedRef(); - res = OpenDataStorageCore(ref storage.Ref, ref storageAccessSplitter.Ref, out Hash digest, targetProgramId.Value, programInfo.StorageId); + res = OpenDataStorageCore(ref storage.Ref, ref storageAccessSplitter.Ref, ref digest, targetProgramId.Value, programInfo.StorageId); if (res.IsFailure()) return res.Miss(); if (programInfo.ProgramId != targetProgramId && !programInfo.AccessControl.HasContentOwnerId(targetProgramId.Value)) @@ -431,7 +816,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager ref romMountCountSemaphore.Ref, storage.Get); if (res.IsFailure()) return res.Miss(); - using SharedRef accessFailureManager = SharedRef.Create(in _selfReference); + using SharedRef accessFailureManager = GetSharedAccessFailureManagerFromThis(); using var retryStorage = new SharedRef(new DeepRetryStorage(in storage, in storageAccessSplitter, in accessFailureManager, ref romDivisionSizeUnitCountSemaphore.Ref, in digest, targetProgramId.Value, @@ -543,7 +928,6 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager return Result.Success; } - // ReSharper disable once OutParameterValueIsAlwaysDiscarded.Local private Result OpenDataFileSystemCore(ref SharedRef outFileSystem, out bool isHostFs, ulong programId, StorageId storageId) { @@ -557,7 +941,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager out ContentAttributes contentAttributes, out _, programId, storageId); if (res.IsFailure()) return res.Miss(); - isHostFs = Utility.IsHostFsMountName(programPath.GetString()); + isHostFs = Impl.Utility.IsHostFsMountName(programPath.GetString()); using var fileSystem = new SharedRef(); res = _serviceImpl.OpenDataFileSystem(ref fileSystem.Ref, in programPath, contentAttributes, @@ -684,9 +1068,33 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager return Result.Success; } - public Result IsArchivedProgram(out bool isArchived, ulong processId) + public Result IsArchivedProgram(out bool outIsArchived, ulong processId) { - throw new NotImplementedException(); + UnsafeHelpers.SkipParamInit(out outIsArchived); + + Result res = GetProgramInfoByProcessId(out ProgramInfo programInfo, processId); + if (res.IsFailure()) return res.Miss(); + + using var programPath = new Path(); + res = _serviceImpl.ResolveProgramPath(out _, ref programPath.Ref(), out _, programInfo.ProgramId, + programInfo.StorageId); + if (res.IsFailure()) return res.Miss(); + + ReadOnlySpan ncaExtension = ".nca"u8; + int ncaExtensionLength = ncaExtension.Length; + + ReadOnlySpan path = programPath.GetString(); + int pathLength = StringUtils.GetLength(path, PathTool.EntryNameLengthMax); + + if (pathLength > ncaExtensionLength && + StringUtils.CompareCaseInsensitive(path.Slice(pathLength - ncaExtensionLength), ".nca"u8) == 0) + { + outIsArchived = true; + return Result.Success; + } + + outIsArchived = false; + return Result.Success; } public Result SetSdCardEncryptionSeed(in EncryptionSeed encryptionSeed) @@ -702,17 +1110,60 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager public Result OpenHostFileSystem(ref SharedRef outFileSystem, ref readonly FspPath path) { - throw new NotImplementedException(); + Result res = GetProgramInfo(out ProgramInfo programInfo); + if (res.IsFailure()) return res.Miss(); + + Accessibility accessibility = programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountHost); + + if (!accessibility.CanRead || !accessibility.CanWrite) + return ResultFs.PermissionDenied.Log(); + + using var pathNormalized = new Path(); + res = pathNormalized.Initialize(path.Str); + if (res.IsFailure()) return res.Miss(); + + using var fileSystem = new SharedRef(); + res = _serviceImpl.OpenHostFileSystem(ref fileSystem.Ref, in pathNormalized, openCaseSensitive: false); + if (res.IsFailure()) return res.Miss(); + + PathFlags pathFlags = FileSystemInterfaceAdapter.GetDefaultPathFlags(); + if (path.Str.At(0) == 0) + pathFlags.AllowWindowsPath(); + + using SharedRef fileSystemAdapter = + FileSystemInterfaceAdapter.CreateShared(ref fileSystem.Ref, pathFlags, allowAllOperations: false); + + outFileSystem.SetByMove(ref fileSystemAdapter.Ref); + + return Result.Success; } public Result OpenSystemDataUpdateEventNotifier(ref SharedRef outEventNotifier) { - throw new NotImplementedException(); + Result res = GetProgramInfo(out ProgramInfo programInfo); + if (res.IsFailure()) return res.Miss(); + + if (!programInfo.AccessControl.CanCall(OperationType.OpenSystemDataUpdateEventNotifier)) + return ResultFs.PermissionDenied.Log(); + + using var systemDataUpdateEventNotifier = new UniqueRef(); + res = _serviceImpl.CreateNotifier(ref systemDataUpdateEventNotifier.Ref); + if (res.IsFailure()) return res.Miss(); + + outEventNotifier.Set(ref systemDataUpdateEventNotifier.Ref); + + return Result.Success; } public Result NotifySystemDataUpdateEvent() { - throw new NotImplementedException(); + Result res = GetProgramInfo(out ProgramInfo programInfo); + if (res.IsFailure()) return res.Miss(); + + if (!programInfo.AccessControl.CanCall(OperationType.NotifySystemDataUpdateEvent)) + return ResultFs.PermissionDenied.Log(); + + return _serviceImpl.NotifySystemDataUpdateEvent().Ret(); } public Result HandleResolubleAccessFailure(out bool wasDeferred, Result nonDeferredResult) @@ -721,9 +1172,9 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager } Result IRomFileSystemAccessFailureManager.OpenDataStorageCore(ref SharedRef outStorage, - ref SharedRef outStorageAccessSplitter, out Hash ncaHeaderDigest, ulong id, + ref SharedRef outStorageAccessSplitter, ref Hash outNcaDigest, ulong id, StorageId storageId) { - return OpenDataStorageCore(ref outStorage, ref outStorageAccessSplitter, out ncaHeaderDigest, id, storageId).Ret(); + return OpenDataStorageCore(ref outStorage, ref outStorageAccessSplitter, ref outNcaDigest, id, storageId).Ret(); } } \ No newline at end of file diff --git a/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs b/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs index 6645429b..275386a5 100644 --- a/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs +++ b/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs @@ -169,10 +169,10 @@ file static class Anonymous /// Based on nnSdk 17.5.0 (FS 17.0.0) public class NcaFileSystemServiceImpl : IDisposable { - private Configuration _config; - private UpdatePartitionPath _updatePartitionPath; - private ExternalKeyManager _externalKeyManager; - private SystemDataUpdateEventManager _systemDataUpdateEventManager; + private readonly Configuration _config; + private readonly UpdatePartitionPath _updatePartitionPath; + private readonly ExternalKeyManager _externalKeyManager; + private readonly SystemDataUpdateEventManager _systemDataUpdateEventManager; private EncryptionSeed _encryptionSeed; private uint _romFsDeepRetryStartCount; private uint _romFsRemountForDataCorruptionCount; @@ -915,16 +915,6 @@ public class NcaFileSystemServiceImpl : IDisposable return ResultFs.PathNotFound.Log(); } - private Result ParseDir(ref readonly Path path, ref SharedRef outContentFileSystem, - ref SharedRef baseFileSystem, FileSystemProxyType type, bool preserveUnc) - { - using var fileSystem = new SharedRef(); - Result res = _config.SubDirectoryFsCreator.Create(ref fileSystem.Ref, baseFileSystem, path); - if (res.IsFailure()) return res.Miss(); - - return ParseContentTypeForDirectory(ref outContentFileSystem, ref fileSystem.Ref, type); - } - private Result ParseDirWithPathCaseNormalizationOnCaseSensitiveHostOrLocalFs( ref SharedRef outFileSystem, ref readonly Path path, MountInfo.FileSystemType fsType) { @@ -1139,7 +1129,7 @@ public class NcaFileSystemServiceImpl : IDisposable zeroRightsId.Clear(); ncaReader.GetRightsId(rightsId); - bool hasRightsId = !Crypto.CryptoUtil.IsSameBytes(rightsId, zeroRightsId, Unsafe.SizeOf()); + bool hasRightsId = !CryptoUtil.IsSameBytes(rightsId, zeroRightsId, Unsafe.SizeOf()); if (hasRightsId) { diff --git a/src/LibHac/FsSrv/Sf/IFileSystemProxy.cs b/src/LibHac/FsSrv/Sf/IFileSystemProxy.cs index 3295fdff..f1ac1180 100644 --- a/src/LibHac/FsSrv/Sf/IFileSystemProxy.cs +++ b/src/LibHac/FsSrv/Sf/IFileSystemProxy.cs @@ -80,7 +80,7 @@ public interface IFileSystemProxy : IDisposable Result OpenPatchDataStorageByCurrentProcess(ref SharedRef outStorage); Result OpenDataFileSystemWithProgramIndex(ref SharedRef outFileSystem, byte programIndex); Result OpenDataStorageWithProgramIndex(ref SharedRef outStorage, byte programIndex); - Result OpenDataStorageByPath(ref SharedRef outFileSystem, ref readonly FspPath path, ContentAttributes attributes, FileSystemProxyType fsType); + Result OpenDataStorageByPath(ref SharedRef outStorage, ref readonly FspPath path, ContentAttributes attributes, FileSystemProxyType fsType); Result OpenDataFileSystemByDataId(ref SharedRef outFileSystem, DataId dataId, StorageId storageId); Result OpenDeviceOperator(ref SharedRef outDeviceOperator); Result OpenSdCardDetectionEventNotifier(ref SharedRef outEventNotifier); diff --git a/src/LibHac/FsSystem/SemaphoreAdapter.cs b/src/LibHac/FsSystem/SemaphoreAdapter.cs index 5d28549b..37cd5dd5 100644 --- a/src/LibHac/FsSystem/SemaphoreAdapter.cs +++ b/src/LibHac/FsSystem/SemaphoreAdapter.cs @@ -1,5 +1,6 @@ using System; using System.Threading; +using LibHac.Diag; using LibHac.Os; namespace LibHac.FsSystem; @@ -18,6 +19,23 @@ public class SemaphoreAdapter : IDisposable, ILockable return _semaphore.Wait(System.TimeSpan.Zero); } + public bool TryLock(out int outAcquiredCount, int count) + { + Assert.SdkRequiresLess(0, count); + + for (int i = 0; i < count; i++) + { + if (!_semaphore.Wait(System.TimeSpan.Zero)) + { + outAcquiredCount = i; + return false; + } + } + + outAcquiredCount = count; + return true; + } + public void Lock() { _semaphore.Wait(); @@ -28,6 +46,14 @@ public class SemaphoreAdapter : IDisposable, ILockable _semaphore.Release(); } + public void Unlock(int count) + { + if (count > 0) + { + _semaphore.Release(count); + } + } + public void Dispose() { _semaphore?.Dispose(); diff --git a/src/LibHac/FsSystem/Utility.cs b/src/LibHac/FsSystem/Utility.cs index 9e972a60..9dbccf5a 100644 --- a/src/LibHac/FsSystem/Utility.cs +++ b/src/LibHac/FsSystem/Utility.cs @@ -24,6 +24,65 @@ public class DummyEventNotifier : IEventNotifier } } +public class StageLockWithPin : IUniqueLock where T : class, IDisposable +{ + private UniqueRef _lock; + private MultilockWithPin _multilock; + + public StageLockWithPin(ref UniqueRef inLock, ref readonly SharedRef objectToPin, SemaphoreAdapter semaphore) + { + _lock = new UniqueRef(ref inLock); + _multilock = new MultilockWithPin(in objectToPin, semaphore); + } + + public void Dispose() + { + _multilock.Dispose(); + _lock.Destroy(); + } + + public Result Lock(int count) + { + return _multilock.Lock(count).Ret(); + } +} + +public class MultilockWithPin : IUniqueLock where T : class, IDisposable +{ + private SharedRef _pinnedObject; + private SemaphoreAdapter _semaphore; + private int _lockCount; + + public MultilockWithPin(ref readonly SharedRef objectToPin, SemaphoreAdapter semaphore) + { + _pinnedObject = SharedRef.CreateCopy(in objectToPin); + _semaphore = semaphore; + _lockCount = 0; + } + + public void Dispose() + { + if (_lockCount > 0) + { + _semaphore.Unlock(_lockCount); + } + + _pinnedObject.Destroy(); + } + + public Result Lock(int count) + { + Assert.SdkRequiresEqual(_lockCount, 0); + + if (!_semaphore.TryLock(out _lockCount, count)) + { + return ResultFs.OpenCountLimit.Log(); + } + + return Result.Success; + } +} + /// /// Various utility functions used by the namespace. /// @@ -442,6 +501,31 @@ internal static class Utility return Result.Success; } + public static Result MakeUniqueLockWithPin(ref UniqueRef outUniqueLock, + SemaphoreAdapter semaphore, int count, ref readonly SharedRef objectToPin) where T : class, IDisposable + { + using var multilock = new UniqueRef>(new(in objectToPin, semaphore)); + + Result res = multilock.Get.Lock(count); + if (res.IsFailure()) return res.Miss(); + + outUniqueLock.Set(ref multilock.Ref); + return Result.Success; + } + + public static Result MakeUniqueLockWithPin(ref UniqueRef outUniqueLock, + ref UniqueRef inLock, SemaphoreAdapter semaphore, int count, ref readonly SharedRef objectToPin) + where T : class, IDisposable + { + using var stageLock = new UniqueRef>(new(ref inLock, in objectToPin, semaphore)); + + Result res = stageLock.Get.Lock(count); + if (res.IsFailure()) return res.Miss(); + + outUniqueLock.Set(ref stageLock.Ref); + return Result.Success; + } + private static void SubtractIfHasValue(ref int inOutValue, int count, bool hasValue) { if (hasValue)