mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Update the NCA filesystem service for FS 18
This commit is contained in:
parent
78d9847b6a
commit
b5158764b9
24 changed files with 323 additions and 271 deletions
|
@ -124,6 +124,7 @@ public enum OperationId
|
|||
QueryUnpreparedRange = 4,
|
||||
QueryLazyLoadCompletionRate = 5,
|
||||
SetLazyLoadPriority = 6,
|
||||
GetAsynchronousAccessSplitter = 7,
|
||||
ReadyLazyLoadFile = 10001
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.FsSrv.FsCreator;
|
||||
|
||||
namespace LibHac.FsSrv;
|
||||
|
@ -7,6 +8,8 @@ namespace LibHac.FsSrv;
|
|||
public delegate Result GenerateSeedUniqueMac(Span<byte> outMacBuffer, ReadOnlySpan<byte> data, ReadOnlySpan<byte> seed);
|
||||
public delegate Result GenerateDeviceUniqueMac(Span<byte> outMacBuffer, ReadOnlySpan<byte> data, DeviceUniqueMacType macType);
|
||||
|
||||
public delegate void GenerateSdEncryptionKey(Span<byte> outKey, IEncryptedFileSystemCreator.KeyId keyId, in EncryptionSeed seed);
|
||||
|
||||
public delegate Result SaveTransferAesKeyGenerator(Span<byte> outKeyBuffer,
|
||||
SaveDataTransferCryptoConfiguration.KeyIndex index, ReadOnlySpan<byte> keySource, int keyGeneration);
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ public class FileSystemProxyCoreImpl
|
|||
|
||||
tempFs.SetByMove(ref fileSystem.Ref);
|
||||
res = _fsCreators.EncryptedFileSystemCreator.Create(ref fileSystem.Ref, in tempFs,
|
||||
IEncryptedFileSystemCreator.KeyId.CustomStorage, in _sdEncryptionSeed);
|
||||
IEncryptedFileSystemCreator.KeyId.CustomStorage);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
}
|
||||
else
|
||||
|
@ -114,7 +114,6 @@ public class FileSystemProxyCoreImpl
|
|||
|
||||
public Result SetSdCardEncryptionSeed(in EncryptionSeed seed)
|
||||
{
|
||||
_sdEncryptionSeed = seed;
|
||||
return Result.Success;
|
||||
return _fsCreators.EncryptedFileSystemCreator.SetEncryptionSeed(IEncryptedFileSystemCreator.KeyId.Save, in seed).Ret();
|
||||
}
|
||||
}
|
|
@ -957,13 +957,7 @@ public class FileSystemProxyImpl : IFileSystemProxy, IFileSystemProxyForLoader
|
|||
res = GetSaveDataFileSystemService(out SaveDataFileSystemService saveFsService);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
res = saveFsService.SetSdCardEncryptionSeed(in seed);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
res = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
return ncaFsService.SetSdCardEncryptionSeed(in seed).Ret();
|
||||
return saveFsService.SetSdCardEncryptionSeed(in seed).Ret();
|
||||
}
|
||||
|
||||
public Result GetAndClearErrorInfo(out FileSystemProxyErrorInfo errorInfo)
|
||||
|
|
|
@ -47,6 +47,7 @@ internal struct FileSystemServerGlobals : IDisposable
|
|||
public GameCardServiceGlobals GameCardService;
|
||||
public HierarchicalIntegrityVerificationStorageGlobals HierarchicalIntegrityVerificationStorage;
|
||||
public SpeedEmulationConfigurationGlobals SpeedEmulationConfiguration;
|
||||
public StorageAccessSpeedControlGlobals StorageAccessSpeedControl;
|
||||
|
||||
public void Initialize(HorizonClient horizonClient, FileSystemServer fsServer)
|
||||
{
|
||||
|
|
|
@ -15,6 +15,9 @@ public static class FileSystemServerInitializer
|
|||
{
|
||||
private const ulong SpeedEmulationProgramIdWithoutPlatformIdMinimum = 0;
|
||||
private const ulong SpeedEmulationProgramIdWithoutPlatformIdMaximum = 0x1FFF;
|
||||
|
||||
private const ulong StorageAccessSpeedControlProgramIdWithoutPlatformIdMinimum = 0x3000;
|
||||
private const ulong StorageAccessSpeedControlProgramIdWithoutPlatformIdMaximum = 0x_00FF_FFFF_FFFF_FFFF;
|
||||
|
||||
private const uint ContentDivisionSize = ConcatenationFileSystem.DefaultInternalFileSize;
|
||||
|
||||
|
@ -109,9 +112,11 @@ public static class FileSystemServerInitializer
|
|||
var accessFailureManagementService =
|
||||
new AccessFailureManagementServiceImpl(in accessFailureManagementServiceConfig);
|
||||
|
||||
var speedEmulationRange =
|
||||
new InternalProgramIdRangeForSpeedEmulation(SpeedEmulationProgramIdWithoutPlatformIdMinimum,
|
||||
SpeedEmulationProgramIdWithoutPlatformIdMaximum);
|
||||
var speedEmulationRange = new InternalProgramIdRangeForSpeedEmulation(
|
||||
SpeedEmulationProgramIdWithoutPlatformIdMinimum, SpeedEmulationProgramIdWithoutPlatformIdMaximum);
|
||||
|
||||
var storageAccessSpeedControlRange = new InternalProgramIdRangeForStorageAccessSpeedControl(
|
||||
StorageAccessSpeedControlProgramIdWithoutPlatformIdMinimum, StorageAccessSpeedControlProgramIdWithoutPlatformIdMaximum);
|
||||
|
||||
var ncaFsServiceConfig = new NcaFileSystemServiceImpl.Configuration();
|
||||
ncaFsServiceConfig.BaseFsService = baseFsService;
|
||||
|
@ -127,6 +132,7 @@ public static class FileSystemServerInitializer
|
|||
ncaFsServiceConfig.SpeedEmulationRange = speedEmulationRange;
|
||||
ncaFsServiceConfig.AddOnContentDivisionSize = ContentDivisionSize;
|
||||
ncaFsServiceConfig.RomDivisionSize = ContentDivisionSize;
|
||||
ncaFsServiceConfig.StorageAccessSpeedControlRange = storageAccessSpeedControlRange;
|
||||
ncaFsServiceConfig.FsServer = server;
|
||||
|
||||
var ncaFsService = new NcaFileSystemServiceImpl(in ncaFsServiceConfig);
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
using LibHac.Common;
|
||||
#pragma warning disable CS0169 // Field is never used
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Common.Keys;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using static LibHac.FsSrv.FsCreator.IEncryptedFileSystemCreator;
|
||||
|
||||
|
@ -9,28 +13,67 @@ namespace LibHac.FsSrv.FsCreator;
|
|||
|
||||
public class EncryptedFileSystemCreator : IEncryptedFileSystemCreator
|
||||
{
|
||||
private KeySet KeySet { get; }
|
||||
private readonly KeySet _keySet;
|
||||
private Configuration _configuration;
|
||||
private EncryptionSeed _seed;
|
||||
|
||||
public struct Configuration
|
||||
{
|
||||
public RandomDataGenerator GenerateRandomData;
|
||||
public GenerateSdEncryptionKey GenerateSdEncryptionKey;
|
||||
}
|
||||
|
||||
public EncryptedFileSystemCreator(KeySet keySet)
|
||||
{
|
||||
KeySet = keySet;
|
||||
_keySet = keySet;
|
||||
}
|
||||
|
||||
public Result Create(ref SharedRef<IFileSystem> outEncryptedFileSystem, ref readonly SharedRef<IFileSystem> baseFileSystem, KeyId idIndex, in EncryptionSeed encryptionSeed)
|
||||
private ref readonly EncryptionSeed GetSeed(KeyId keyId)
|
||||
{
|
||||
if (idIndex < KeyId.Save || idIndex > KeyId.CustomStorage)
|
||||
switch (keyId)
|
||||
{
|
||||
case KeyId.Save:
|
||||
case KeyId.Content:
|
||||
case KeyId.CustomStorage:
|
||||
return ref _seed;
|
||||
default:
|
||||
Abort.UnexpectedDefault();
|
||||
return ref Unsafe.NullRef<EncryptionSeed>();
|
||||
}
|
||||
}
|
||||
|
||||
public Result Create(ref SharedRef<IFileSystem> outEncryptedFileSystem, ref readonly SharedRef<IFileSystem> baseFileSystem, KeyId keyId)
|
||||
{
|
||||
if (keyId < KeyId.Save || keyId > KeyId.CustomStorage)
|
||||
{
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
}
|
||||
|
||||
// todo: "proper" key generation instead of a lazy hack
|
||||
KeySet.SetSdSeed(encryptionSeed.Value);
|
||||
_keySet.SetSdSeed(GetSeed(keyId).Value);
|
||||
|
||||
using var encryptedFileSystem = new SharedRef<AesXtsFileSystem>(new AesXtsFileSystem(in baseFileSystem,
|
||||
KeySet.SdCardEncryptionKeys[(int)idIndex].DataRo.ToArray(), 0x4000));
|
||||
_keySet.SdCardEncryptionKeys[(int)keyId].DataRo.ToArray(), 0x4000));
|
||||
|
||||
outEncryptedFileSystem.SetByMove(ref encryptedFileSystem.Ref);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result SetEncryptionSeed(KeyId keyId, in EncryptionSeed encryptionSeed)
|
||||
{
|
||||
switch (keyId)
|
||||
{
|
||||
case KeyId.Save:
|
||||
case KeyId.Content:
|
||||
case KeyId.CustomStorage:
|
||||
_seed = encryptionSeed;
|
||||
break;
|
||||
default:
|
||||
Abort.UnexpectedDefault();
|
||||
break;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
|
@ -14,5 +14,7 @@ public interface IEncryptedFileSystemCreator
|
|||
}
|
||||
|
||||
Result Create(ref SharedRef<IFileSystem> outEncryptedFileSystem, ref readonly SharedRef<IFileSystem> baseFileSystem,
|
||||
KeyId idIndex, in EncryptionSeed encryptionSeed);
|
||||
KeyId keyId);
|
||||
|
||||
Result SetEncryptionSeed(KeyId keyId, in EncryptionSeed encryptionSeed);
|
||||
}
|
|
@ -6,12 +6,10 @@ namespace LibHac.FsSrv.FsCreator;
|
|||
|
||||
public interface IStorageOnNcaCreator
|
||||
{
|
||||
Result Create(ref SharedRef<IStorage> outStorage,
|
||||
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, ref NcaFsHeaderReader outHeaderReader,
|
||||
Result Create(ref SharedRef<IStorage> outStorage, ref NcaFsHeaderReader outHeaderReader,
|
||||
ref readonly SharedRef<NcaReader> ncaReader, int fsIndex);
|
||||
|
||||
Result CreateWithPatch(ref SharedRef<IStorage> outStorage,
|
||||
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, ref NcaFsHeaderReader outHeaderReader,
|
||||
Result CreateWithPatch(ref SharedRef<IStorage> outStorage, ref NcaFsHeaderReader outHeaderReader,
|
||||
ref readonly SharedRef<NcaReader> originalNcaReader, ref readonly SharedRef<NcaReader> currentNcaReader,
|
||||
int fsIndex);
|
||||
|
||||
|
|
|
@ -27,28 +27,22 @@ public class StorageOnNcaCreator : IStorageOnNcaCreator
|
|||
_hashGeneratorFactorySelector = hashGeneratorFactorySelector;
|
||||
}
|
||||
|
||||
public Result Create(ref SharedRef<IStorage> outStorage,
|
||||
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, ref NcaFsHeaderReader outHeaderReader,
|
||||
ref readonly SharedRef<NcaReader> ncaReader, int fsIndex)
|
||||
public Result Create(ref SharedRef<IStorage> outStorage, ref NcaFsHeaderReader outHeaderReader, ref readonly SharedRef<NcaReader> ncaReader, int fsIndex)
|
||||
{
|
||||
var ncaFsDriver = new NcaFileSystemDriver(in ncaReader, _memoryResource, _bufferManager, _hashGeneratorFactorySelector);
|
||||
|
||||
using var storage = new SharedRef<IStorage>();
|
||||
using var storageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>();
|
||||
Result res = RomResultConverter.ConvertRomResult(ncaFsDriver.OpenStorage(ref storage.Ref,
|
||||
ref storageAccessSplitter.Ref, ref outHeaderReader, fsIndex));
|
||||
Result res = RomResultConverter.ConvertRomResult(ncaFsDriver.OpenStorage(ref storage.Ref, ref outHeaderReader, fsIndex));
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
using var resultConvertStorage = new SharedRef<RomResultConvertStorage>(new RomResultConvertStorage(in storage));
|
||||
|
||||
outStorage.SetByMove(ref resultConvertStorage.Ref);
|
||||
outStorageAccessSplitter.SetByMove(ref storageAccessSplitter.Ref);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result CreateWithPatch(ref SharedRef<IStorage> outStorage,
|
||||
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, ref NcaFsHeaderReader outHeaderReader,
|
||||
ref NcaFsHeaderReader outHeaderReader,
|
||||
ref readonly SharedRef<NcaReader> originalNcaReader, ref readonly SharedRef<NcaReader> currentNcaReader,
|
||||
int fsIndex)
|
||||
{
|
||||
|
@ -56,15 +50,11 @@ public class StorageOnNcaCreator : IStorageOnNcaCreator
|
|||
_bufferManager, _hashGeneratorFactorySelector);
|
||||
|
||||
using var storage = new SharedRef<IStorage>();
|
||||
using var storageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>();
|
||||
Result res = RomResultConverter.ConvertRomResult(ncaFsDriver.OpenStorage(ref storage.Ref,
|
||||
ref storageAccessSplitter.Ref, ref outHeaderReader, fsIndex));
|
||||
Result res = RomResultConverter.ConvertRomResult(ncaFsDriver.OpenStorage(ref storage.Ref, ref outHeaderReader, fsIndex));
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
using var resultConvertStorage = new SharedRef<RomResultConvertStorage>(new RomResultConvertStorage(in storage));
|
||||
|
||||
outStorage.SetByMove(ref resultConvertStorage.Ref);
|
||||
outStorageAccessSplitter.SetByMove(ref storageAccessSplitter.Ref);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ namespace LibHac.FsSrv.Impl;
|
|||
public class DeepRetryStorage : IStorage
|
||||
{
|
||||
private AsynchronousAccessStorage _asyncStorage;
|
||||
private SharedRef<IAsynchronousAccessSplitter> _accessSplitter;
|
||||
private SharedRef<IRomFileSystemAccessFailureManager> _parent;
|
||||
private UniqueRef<IUniqueLock> _mountCountLock;
|
||||
private DataStorageContext _dataStorageContext;
|
||||
|
@ -52,14 +51,12 @@ public class DeepRetryStorage : IStorage
|
|||
}
|
||||
|
||||
public DeepRetryStorage(ref readonly SharedRef<IStorage> baseStorage,
|
||||
ref readonly SharedRef<IAsynchronousAccessSplitter> accessSplitter,
|
||||
ref readonly SharedRef<IRomFileSystemAccessFailureManager> parent,
|
||||
ref UniqueRef<IUniqueLock> mountCountSemaphore,
|
||||
bool deepRetryEnabled, FileSystemServer fsServer)
|
||||
{
|
||||
// Missing: Getting the thread pool via GetRegisteredThreadPool()
|
||||
_asyncStorage = new AsynchronousAccessStorage(in baseStorage, accessSplitter.Get);
|
||||
_accessSplitter = SharedRef<IAsynchronousAccessSplitter>.CreateCopy(in accessSplitter);
|
||||
_asyncStorage = new AsynchronousAccessStorage(in baseStorage);
|
||||
_parent = SharedRef<IRomFileSystemAccessFailureManager>.CreateCopy(in parent);
|
||||
_mountCountLock = UniqueRef<IUniqueLock>.Create(ref mountCountSemaphore);
|
||||
_dataStorageContext = new DataStorageContext();
|
||||
|
@ -70,14 +67,12 @@ public class DeepRetryStorage : IStorage
|
|||
}
|
||||
|
||||
public DeepRetryStorage(ref readonly SharedRef<IStorage> baseStorage,
|
||||
ref readonly SharedRef<IAsynchronousAccessSplitter> accessSplitter,
|
||||
ref readonly SharedRef<IRomFileSystemAccessFailureManager> parent,
|
||||
ref UniqueRef<IUniqueLock> mountCountSemaphore,
|
||||
in Hash hash, ulong programId, StorageId storageId, FileSystemServer fsServer)
|
||||
{
|
||||
// Missing: Getting the thread pool via GetRegisteredThreadPool()
|
||||
_asyncStorage = new AsynchronousAccessStorage(in baseStorage, accessSplitter.Get);
|
||||
_accessSplitter = SharedRef<IAsynchronousAccessSplitter>.CreateCopy(in accessSplitter);
|
||||
_asyncStorage = new AsynchronousAccessStorage(in baseStorage);
|
||||
_parent = SharedRef<IRomFileSystemAccessFailureManager>.CreateCopy(in parent);
|
||||
_mountCountLock = UniqueRef<IUniqueLock>.Create(ref mountCountSemaphore);
|
||||
_dataStorageContext = new DataStorageContext(in hash, programId, storageId);
|
||||
|
@ -92,7 +87,6 @@ public class DeepRetryStorage : IStorage
|
|||
_readWriteLock.Dispose();
|
||||
_mountCountLock.Destroy();
|
||||
_parent.Destroy();
|
||||
_accessSplitter.Destroy();
|
||||
_asyncStorage.Dispose();
|
||||
|
||||
base.Dispose();
|
||||
|
@ -143,7 +137,6 @@ public class DeepRetryStorage : IStorage
|
|||
Assert.SdkNotNull(_parent);
|
||||
|
||||
using var remountStorage = new SharedRef<IStorage>();
|
||||
using var remountStorageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>();
|
||||
|
||||
const int maxRetryCount = 2;
|
||||
|
||||
|
@ -151,8 +144,8 @@ public class DeepRetryStorage : IStorage
|
|||
Hash digest = default;
|
||||
for (int i = 0; i < maxRetryCount; i++)
|
||||
{
|
||||
retryResult = _parent.Get.OpenDataStorageCore(ref remountStorage.Ref, ref remountStorageAccessSplitter.Ref,
|
||||
ref digest, _dataStorageContext.GetProgramIdValue(), _dataStorageContext.GetStorageId());
|
||||
retryResult = _parent.Get.OpenDataStorageCore(ref remountStorage.Ref, ref digest,
|
||||
_dataStorageContext.GetProgramIdValue(), _dataStorageContext.GetStorageId());
|
||||
|
||||
if (!ResultFs.DataCorrupted.Includes(retryResult))
|
||||
break;
|
||||
|
@ -170,8 +163,7 @@ public class DeepRetryStorage : IStorage
|
|||
return ResultFs.NcaDigestInconsistent.Log();
|
||||
}
|
||||
|
||||
_accessSplitter.SetByMove(ref remountStorageAccessSplitter.Ref);
|
||||
_asyncStorage.SetBaseStorage(in remountStorage, _accessSplitter.Get);
|
||||
_asyncStorage.SetBaseStorage(in remountStorage);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace LibHac.FsSrv.Impl;
|
|||
|
||||
public interface IRomFileSystemAccessFailureManager : IDisposable
|
||||
{
|
||||
Result OpenDataStorageCore(ref SharedRef<IStorage> outStorage, ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, ref Hash outNcaDigest, ulong id, StorageId storageId);
|
||||
Result OpenDataStorageCore(ref SharedRef<IStorage> outStorage, ref Hash outNcaDigest, ulong id, StorageId storageId);
|
||||
Result HandleResolubleAccessFailure(out bool wasDeferred, Result nonDeferredResult);
|
||||
void IncrementRomFsDeepRetryStartCount();
|
||||
void IncrementRomFsRemountForDataCorruptionCount();
|
||||
|
|
44
src/LibHac/FsSrv/Impl/StorageAccessSpeedControl.cs
Normal file
44
src/LibHac/FsSrv/Impl/StorageAccessSpeedControl.cs
Normal file
|
@ -0,0 +1,44 @@
|
|||
namespace LibHac.FsSrv
|
||||
{
|
||||
public readonly struct InternalProgramIdRangeForStorageAccessSpeedControl
|
||||
{
|
||||
public readonly ulong ProgramIdWithoutPlatformIdMin;
|
||||
public readonly ulong ProgramIdWithoutPlatformIdMax;
|
||||
|
||||
public InternalProgramIdRangeForStorageAccessSpeedControl(ulong min, ulong max)
|
||||
{
|
||||
ProgramIdWithoutPlatformIdMin = min;
|
||||
ProgramIdWithoutPlatformIdMax = max;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace LibHac.FsSrv.Impl
|
||||
{
|
||||
internal struct StorageAccessSpeedControlGlobals
|
||||
{
|
||||
public InternalProgramIdRangeForStorageAccessSpeedControl TargetProgramIdRange;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles getting and setting the configuration for storage access speed control.
|
||||
/// </summary>
|
||||
/// <remarks>Based on nnSdk 18.3.0 (FS 18.0.0)</remarks>
|
||||
public static class StorageAccessSpeedControl
|
||||
{
|
||||
public static void SetTargetProgramIdRange(FileSystemServer fsServer,
|
||||
InternalProgramIdRangeForStorageAccessSpeedControl range)
|
||||
{
|
||||
fsServer.Globals.StorageAccessSpeedControl.TargetProgramIdRange = range;
|
||||
}
|
||||
|
||||
public static bool IsTargetProgramId(FileSystemServer fsServer, ulong programId)
|
||||
{
|
||||
var range = fsServer.Globals.StorageAccessSpeedControl.TargetProgramIdRange;
|
||||
ulong programIdWithoutPlatformId = Utility.ClearPlatformIdInProgramId(programId);
|
||||
|
||||
return programIdWithoutPlatformId >= range.ProgramIdWithoutPlatformIdMin &&
|
||||
programIdWithoutPlatformId <= range.ProgramIdWithoutPlatformIdMax;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,7 +24,7 @@ namespace LibHac.FsSrv;
|
|||
/// </summary>
|
||||
/// <remarks>FS will have one instance of this class for every connected process.
|
||||
/// The FS permissions of the calling process are checked on every function call.
|
||||
/// <br/>Based on nnSdk 17.5.0 (FS 17.0.0)</remarks>
|
||||
/// <br/>Based on nnSdk 18.3.0 (FS 18.0.0)</remarks>
|
||||
internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
||||
{
|
||||
private const int AocSemaphoreCount = 128;
|
||||
|
@ -95,18 +95,18 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
|||
public Result OpenFileSystemWithPatch(ref SharedRef<IFileSystemSf> outFileSystem, ProgramId programId,
|
||||
FileSystemProxyType type)
|
||||
{
|
||||
const StorageLayoutType storageFlag = StorageLayoutType.All;
|
||||
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
|
||||
|
||||
// Get the program info for the caller and verify permissions
|
||||
Result res = GetProgramInfo(out ProgramInfo callerProgramInfo);
|
||||
Result res = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
StorageLayoutType storageFlag = _serviceImpl.GetStorageFlag(programInfo.ProgramIdValue);
|
||||
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case FileSystemProxyType.Manual:
|
||||
Accessibility accessibility =
|
||||
callerProgramInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountContentManual);
|
||||
programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountContentManual);
|
||||
|
||||
if (!accessibility.CanRead)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
@ -123,14 +123,14 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
|||
}
|
||||
|
||||
// Get the program info for the owner of the file system being opened
|
||||
res = GetProgramInfoByProgramId(out ProgramInfo ownerProgramInfo, programId.Value);
|
||||
res = GetProgramInfoByProgramId(out ProgramInfo applicationProgramInfo, programId.Value);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
// Try to find the path to the original version of the file system
|
||||
using var originalPath = new Path();
|
||||
Result originalResult = _serviceImpl.ResolveApplicationHtmlDocumentPath(out bool isDirectory,
|
||||
ref originalPath.Ref(), out ContentAttributes contentAttributes, out ulong originalProgramId,
|
||||
programId.Value, ownerProgramInfo.StorageId);
|
||||
programId.Value, applicationProgramInfo.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))
|
||||
|
@ -334,8 +334,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
|||
_serviceImpl.IncrementRomFsUnrecoverableByGameCardAccessFailedCount();
|
||||
}
|
||||
|
||||
private Result OpenDataStorageCore(ref SharedRef<IStorage> outStorage,
|
||||
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, ref Hash outNcaDigest, ulong id,
|
||||
private Result OpenDataStorageCore(ref SharedRef<IStorage> outStorage, ref Hash outNcaDigest, ulong id,
|
||||
StorageId storageId)
|
||||
{
|
||||
using var programPath = new Path();
|
||||
|
@ -347,7 +346,6 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
|||
out ContentAttributes patchContentAttributes, id);
|
||||
|
||||
using var storage = new SharedRef<IStorage>();
|
||||
using var storageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>();
|
||||
|
||||
if (ResultLr.ProgramNotFound.Includes(patchResult))
|
||||
{
|
||||
|
@ -360,8 +358,8 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
|||
if (originalResult.IsFailure())
|
||||
return originalResult.Miss();
|
||||
|
||||
Result res = _serviceImpl.OpenDataStorage(ref storage.Ref, ref storageAccessSplitter.Ref,
|
||||
ref outNcaDigest, in programPath, contentAttributes, FileSystemProxyType.Rom, id);
|
||||
Result res = _serviceImpl.OpenDataStorage(ref storage.Ref, ref outNcaDigest, in programPath,
|
||||
contentAttributes, FileSystemProxyType.Rom, id);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
}
|
||||
else
|
||||
|
@ -373,14 +371,12 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
|||
? 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);
|
||||
Result res = _serviceImpl.OpenStorageWithPatch(ref storage.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;
|
||||
}
|
||||
|
@ -401,8 +397,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
|||
|
||||
StorageId storageId = programInfo.StorageId;
|
||||
using var storage = new SharedRef<IStorage>();
|
||||
using var storageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>();
|
||||
res = OpenDataStorageCore(ref storage.Ref, ref storageAccessSplitter.Ref, ref digest, programInfo.ProgramIdValue, storageId);
|
||||
res = OpenDataStorageCore(ref storage.Ref, ref digest, programInfo.ProgramIdValue, storageId);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
using var romDivisionSizeUnitCountSemaphore = new UniqueRef<IUniqueLock>();
|
||||
|
@ -412,9 +407,9 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
|||
|
||||
using SharedRef<IRomFileSystemAccessFailureManager> accessFailureManager = GetSharedAccessFailureManagerFromThis();
|
||||
|
||||
using var retryStorage = new SharedRef<IStorage>(new DeepRetryStorage(in storage, in storageAccessSplitter,
|
||||
in accessFailureManager, ref romDivisionSizeUnitCountSemaphore.Ref, in digest, programInfo.ProgramIdValue,
|
||||
storageId, _serviceImpl.FsServer));
|
||||
using var retryStorage = new SharedRef<IStorage>(new DeepRetryStorage(in storage, in accessFailureManager,
|
||||
ref romDivisionSizeUnitCountSemaphore.Ref, in digest, programInfo.ProgramIdValue, storageId,
|
||||
_serviceImpl.FsServer));
|
||||
|
||||
using var typeSetStorage = new SharedRef<IStorage>(new StorageLayoutTypeSetStorage(in retryStorage, storageFlag));
|
||||
using var storageAdapter = new SharedRef<IStorageSf>(new StorageInterfaceAdapter(in typeSetStorage));
|
||||
|
@ -452,9 +447,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
|||
res = ncaPath.Normalize(flags);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
using var storageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>();
|
||||
res = _serviceImpl.OpenDataStorage(ref storage.Ref, ref storageAccessSplitter.Ref, ref digest, in ncaPath,
|
||||
attributes, type, ulong.MaxValue);
|
||||
res = _serviceImpl.OpenDataStorage(ref storage.Ref, ref digest, in ncaPath, attributes, type, ulong.MaxValue);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
using var romDivisionSizeUnitCountSemaphore = new UniqueRef<IUniqueLock>();
|
||||
|
@ -464,9 +457,9 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
|||
|
||||
using SharedRef<IRomFileSystemAccessFailureManager> accessFailureManager = GetSharedAccessFailureManagerFromThis();
|
||||
|
||||
using var retryStorage = new SharedRef<IStorage>(new DeepRetryStorage(in storage, in storageAccessSplitter,
|
||||
in accessFailureManager, ref romDivisionSizeUnitCountSemaphore.Ref, in digest, ProgramId.InvalidId.Value,
|
||||
StorageId.None, _serviceImpl.FsServer));
|
||||
using var retryStorage = new SharedRef<IStorage>(new DeepRetryStorage(in storage, in accessFailureManager,
|
||||
ref romDivisionSizeUnitCountSemaphore.Ref, in digest, ProgramId.InvalidId.Value, StorageId.None,
|
||||
_serviceImpl.FsServer));
|
||||
|
||||
using var typeSetStorage = new SharedRef<IStorage>(new StorageLayoutTypeSetStorage(in retryStorage, storageFlag));
|
||||
using var storageAdapter = new SharedRef<IStorageSf>(new StorageInterfaceAdapter(in typeSetStorage));
|
||||
|
@ -478,12 +471,12 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
|||
|
||||
public Result OpenDataStorageByProgramId(ref SharedRef<IStorageSf> outStorage, ProgramId programId)
|
||||
{
|
||||
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();
|
||||
|
||||
StorageLayoutType storageFlag = _serviceImpl.GetStorageFlag(programId.Value, programInfo.ProgramIdValue);
|
||||
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
|
||||
|
||||
Hash digest = default;
|
||||
|
||||
using var romMountCountSemaphore = new UniqueRef<IUniqueLock>();
|
||||
|
@ -491,8 +484,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
|||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
using var storage = new SharedRef<IStorage>();
|
||||
using var storageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>();
|
||||
res = OpenDataStorageCore(ref storage.Ref, ref storageAccessSplitter.Ref, ref digest, programId.Value, programInfo.StorageId);
|
||||
res = OpenDataStorageCore(ref storage.Ref, ref digest, programId.Value, programInfo.StorageId);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
res = GetProgramInfo(out ProgramInfo properProgramInfo);
|
||||
|
@ -508,9 +500,9 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
|||
|
||||
using SharedRef<IRomFileSystemAccessFailureManager> accessFailureManager = GetSharedAccessFailureManagerFromThis();
|
||||
|
||||
using var retryStorage = new SharedRef<IStorage>(new DeepRetryStorage(in storage, in storageAccessSplitter,
|
||||
in accessFailureManager, ref romDivisionSizeUnitCountSemaphore.Ref, in digest, programId.Value,
|
||||
programInfo.StorageId, _serviceImpl.FsServer));
|
||||
using var retryStorage = new SharedRef<IStorage>(new DeepRetryStorage(in storage, in accessFailureManager,
|
||||
ref romDivisionSizeUnitCountSemaphore.Ref, in digest, programId.Value, programInfo.StorageId,
|
||||
_serviceImpl.FsServer));
|
||||
|
||||
using var typeSetStorage = new SharedRef<IStorage>(new StorageLayoutTypeSetStorage(in retryStorage, storageFlag));
|
||||
using var storageAdapter = new SharedRef<IStorageSf>(new StorageInterfaceAdapter(in typeSetStorage));
|
||||
|
@ -523,12 +515,12 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
|||
public Result OpenFileSystemWithId(ref SharedRef<IFileSystemSf> outFileSystem, in FspPath path,
|
||||
ContentAttributes attributes, ulong id, FileSystemProxyType type)
|
||||
{
|
||||
const StorageLayoutType storageFlag = StorageLayoutType.All;
|
||||
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
|
||||
|
||||
Result res = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
StorageLayoutType storageFlag = _serviceImpl.GetStorageFlag(id, programInfo.ProgramIdValue);
|
||||
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
|
||||
|
||||
AccessControl ac = programInfo.AccessControl;
|
||||
|
||||
switch (type)
|
||||
|
@ -626,12 +618,14 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
|||
|
||||
public Result OpenDataStorageByDataId(ref SharedRef<IStorageSf> outStorage, DataId dataId, StorageId storageId)
|
||||
{
|
||||
Result res;
|
||||
|
||||
StorageLayoutType storageFlag = _serviceImpl.GetStorageFlag(dataId.Value);
|
||||
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
|
||||
Result res = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
bool isAoc = storageId == StorageId.None;
|
||||
DataId targetDataId = _serviceImpl.RedirectDataId(dataId, storageId);
|
||||
|
||||
StorageLayoutType storageFlag = _serviceImpl.GetStorageFlag(targetDataId.Value, programInfo.ProgramIdValue);
|
||||
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
|
||||
|
||||
using var systemDataPath = new Path();
|
||||
using var systemDataPatchPath = new Path();
|
||||
|
@ -642,38 +636,33 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
|||
if (isAoc)
|
||||
{
|
||||
res = _serviceImpl.ResolveAddOnContentPath(ref systemDataPath.Ref(), out contentAttributes,
|
||||
ref systemDataPatchPath.Ref(), out patchContentAttributes, dataId);
|
||||
ref systemDataPatchPath.Ref(), out patchContentAttributes, targetDataId);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
}
|
||||
else
|
||||
{
|
||||
res = _serviceImpl.ResolveDataPath(ref systemDataPath.Ref(), out contentAttributes, dataId, storageId);
|
||||
res = _serviceImpl.ResolveDataPath(ref systemDataPath.Ref(), out contentAttributes, targetDataId, 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<IStorage>();
|
||||
using var storageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>();
|
||||
|
||||
if (systemDataPatchPath.IsEmpty())
|
||||
{
|
||||
res = _serviceImpl.OpenDataStorage(ref storage.Ref, ref storageAccessSplitter.Ref,
|
||||
ref Unsafe.NullRef<Hash>(), in systemDataPath, contentAttributes, FileSystemProxyType.Data,
|
||||
dataId.Value, canMountSystemDataPrivate);
|
||||
res = _serviceImpl.OpenDataStorage(ref storage.Ref, ref Unsafe.NullRef<Hash>(), in systemDataPath,
|
||||
contentAttributes, FileSystemProxyType.Data, targetDataId.Value, canMountSystemDataPrivate);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
}
|
||||
else
|
||||
{
|
||||
res = _serviceImpl.OpenStorageWithPatch(ref storage.Ref, ref storageAccessSplitter.Ref,
|
||||
ref Unsafe.NullRef<Hash>(), in systemDataPath, contentAttributes, in systemDataPatchPath,
|
||||
patchContentAttributes, FileSystemProxyType.Data, dataId.Value, dataId.Value, canMountSystemDataPrivate);
|
||||
res = _serviceImpl.OpenStorageWithPatch(ref storage.Ref, ref Unsafe.NullRef<Hash>(), in systemDataPath,
|
||||
contentAttributes, in systemDataPatchPath, patchContentAttributes, FileSystemProxyType.Data,
|
||||
targetDataId.Value, targetDataId.Value, canMountSystemDataPrivate);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
}
|
||||
|
||||
|
@ -687,8 +676,8 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
|||
|
||||
using SharedRef<IRomFileSystemAccessFailureManager> accessFailureManager = GetSharedAccessFailureManagerFromThis();
|
||||
|
||||
using var retryStorage = new SharedRef<IStorage>(new DeepRetryStorage(in storage, in storageAccessSplitter,
|
||||
in accessFailureManager, ref mountCountSemaphore.Ref, deepRetryEnabled: isAoc, _serviceImpl.FsServer));
|
||||
using var retryStorage = new SharedRef<IStorage>(new DeepRetryStorage(in storage, in accessFailureManager,
|
||||
ref mountCountSemaphore.Ref, deepRetryEnabled: isAoc, _serviceImpl.FsServer));
|
||||
|
||||
using var typeSetStorage = new SharedRef<IStorage>(new StorageLayoutTypeSetStorage(in retryStorage, storageFlag));
|
||||
using var storageAdapter = new SharedRef<IStorageSf>(new StorageInterfaceAdapter(in typeSetStorage));
|
||||
|
@ -701,12 +690,13 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
|||
public Result OpenDataFileSystemByDataId(ref SharedRef<IFileSystemSf> outFileSystem, DataId dataId, StorageId storageId)
|
||||
{
|
||||
Result res;
|
||||
|
||||
bool isAoc = storageId == StorageId.None;
|
||||
DataId targetDataId = _serviceImpl.RedirectDataId(dataId, storageId);
|
||||
|
||||
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();
|
||||
|
||||
|
@ -715,12 +705,12 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
|||
if (isAoc)
|
||||
{
|
||||
res = _serviceImpl.ResolveAddOnContentPath(ref dataPath.Ref(), out contentAttributes,
|
||||
ref dataPatchPathUnused.Ref(), out _, dataId);
|
||||
ref dataPatchPathUnused.Ref(), out _, targetDataId);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
}
|
||||
else
|
||||
{
|
||||
res = _serviceImpl.ResolveDataPath(ref dataPath.Ref(), out contentAttributes, dataId, storageId);
|
||||
res = _serviceImpl.ResolveDataPath(ref dataPath.Ref(), out contentAttributes, targetDataId, storageId);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
Assert.SdkAssert(dataPatchPathUnused.IsEmpty());
|
||||
|
@ -728,7 +718,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
|||
|
||||
using var fileSystem = new SharedRef<IFileSystem>();
|
||||
res = _serviceImpl.OpenDataFileSystem(ref fileSystem.Ref, in dataPath, contentAttributes,
|
||||
FileSystemProxyType.Data, dataId.Value, isDirectory: true);
|
||||
FileSystemProxyType.Data, targetDataId.Value, isDirectory: true);
|
||||
|
||||
if (!res.IsSuccess())
|
||||
{
|
||||
|
@ -766,7 +756,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
|||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
// Verify the caller has access to the file system
|
||||
if (targetProgramId != programInfo.ProgramId &&
|
||||
if (programInfo.ProgramId != targetProgramId &&
|
||||
!programInfo.AccessControl.HasContentOwnerId(targetProgramId.Value))
|
||||
{
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
@ -790,7 +780,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
|||
res = _serviceImpl.ResolveRomReferenceProgramId(out ProgramId targetProgramId, programInfo.ProgramId, programIndex);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
StorageLayoutType storageFlag = _serviceImpl.GetStorageFlag(targetProgramId.Value);
|
||||
StorageLayoutType storageFlag = _serviceImpl.GetStorageFlag(targetProgramId.Value, programInfo.ProgramIdValue);
|
||||
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
|
||||
|
||||
Hash digest = default;
|
||||
|
@ -800,8 +790,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
|||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
using var storage = new SharedRef<IStorage>();
|
||||
using var storageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>();
|
||||
res = OpenDataStorageCore(ref storage.Ref, ref storageAccessSplitter.Ref, ref digest, targetProgramId.Value, programInfo.StorageId);
|
||||
res = OpenDataStorageCore(ref storage.Ref, ref digest, targetProgramId.Value, programInfo.StorageId);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
if (programInfo.ProgramId != targetProgramId && !programInfo.AccessControl.HasContentOwnerId(targetProgramId.Value))
|
||||
|
@ -814,9 +803,9 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
|||
|
||||
using SharedRef<IRomFileSystemAccessFailureManager> accessFailureManager = GetSharedAccessFailureManagerFromThis();
|
||||
|
||||
using var retryStorage = new SharedRef<IStorage>(new DeepRetryStorage(in storage, in storageAccessSplitter,
|
||||
in accessFailureManager, ref romDivisionSizeUnitCountSemaphore.Ref, in digest, targetProgramId.Value,
|
||||
programInfo.StorageId, _serviceImpl.FsServer));
|
||||
using var retryStorage = new SharedRef<IStorage>(new DeepRetryStorage(in storage, in accessFailureManager,
|
||||
ref romDivisionSizeUnitCountSemaphore.Ref, in digest, targetProgramId.Value, programInfo.StorageId,
|
||||
_serviceImpl.FsServer));
|
||||
|
||||
using var typeSetStorage = new SharedRef<IStorage>(new StorageLayoutTypeSetStorage(in retryStorage, storageFlag));
|
||||
using var storageAdapter = new SharedRef<IStorageSf>(new StorageInterfaceAdapter(in typeSetStorage));
|
||||
|
@ -955,12 +944,12 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
|||
public Result OpenContentStorageFileSystem(ref SharedRef<IFileSystemSf> outFileSystem,
|
||||
ContentStorageId contentStorageId)
|
||||
{
|
||||
StorageLayoutType storageFlag = contentStorageId == ContentStorageId.System ? StorageLayoutType.Bis : StorageLayoutType.All;
|
||||
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
|
||||
|
||||
Result res = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
StorageLayoutType storageFlag = _serviceImpl.GetStorageFlagByContentStorageId(contentStorageId, programInfo.ProgramIdValue);
|
||||
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
|
||||
|
||||
Accessibility accessibility = programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountContentStorage);
|
||||
|
||||
if (!accessibility.CanRead || !accessibility.CanWrite)
|
||||
|
@ -1088,17 +1077,6 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
|||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result SetSdCardEncryptionSeed(in EncryptionSeed encryptionSeed)
|
||||
{
|
||||
Result res = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
if (!programInfo.AccessControl.CanCall(OperationType.SetEncryptionSeed))
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
return _serviceImpl.SetSdCardEncryptionSeed(in encryptionSeed).Ret();
|
||||
}
|
||||
|
||||
public Result OpenHostFileSystem(ref SharedRef<IFileSystemSf> outFileSystem, ref readonly FspPath path)
|
||||
{
|
||||
Result res = GetProgramInfo(out ProgramInfo programInfo);
|
||||
|
@ -1163,9 +1141,8 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
|||
}
|
||||
|
||||
Result IRomFileSystemAccessFailureManager.OpenDataStorageCore(ref SharedRef<IStorage> outStorage,
|
||||
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, ref Hash outNcaDigest, ulong id,
|
||||
StorageId storageId)
|
||||
ref Hash outNcaDigest, ulong id, StorageId storageId)
|
||||
{
|
||||
return OpenDataStorageCore(ref outStorage, ref outStorageAccessSplitter, ref outNcaDigest, id, storageId).Ret();
|
||||
return OpenDataStorageCore(ref outStorage, ref outNcaDigest, id, storageId).Ret();
|
||||
}
|
||||
}
|
|
@ -166,14 +166,13 @@ file static class Anonymous
|
|||
/// <summary>
|
||||
/// Handles locating and opening NCA content files.
|
||||
/// </summary>
|
||||
/// <remarks>Based on nnSdk 17.5.0 (FS 17.0.0)</remarks>
|
||||
/// <remarks>Based on nnSdk 18.3.0 (FS 18.0.0)</remarks>
|
||||
public class NcaFileSystemServiceImpl : IDisposable
|
||||
{
|
||||
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;
|
||||
private uint _romfsUnrecoverableDataCorruptionByRemountCount;
|
||||
|
@ -193,11 +192,13 @@ public class NcaFileSystemServiceImpl : IDisposable
|
|||
public IEncryptedFileSystemCreator EncryptedFsCreator;
|
||||
public INspRootFileSystemCreator NspRootFileSystemCreator;
|
||||
public LocationResolverSet LocationResolverSet;
|
||||
public Func<DataId, StorageId, DataId> RedirectDataId;
|
||||
public ProgramRegistryServiceImpl ProgramRegistryService;
|
||||
public AccessFailureManagementServiceImpl AccessFailureManagementService;
|
||||
public InternalProgramIdRangeForSpeedEmulation SpeedEmulationRange;
|
||||
public long AddOnContentDivisionSize;
|
||||
public long RomDivisionSize;
|
||||
public InternalProgramIdRangeForStorageAccessSpeedControl StorageAccessSpeedControlRange;
|
||||
|
||||
// LibHac additions
|
||||
public FileSystemServer FsServer;
|
||||
|
@ -244,6 +245,8 @@ public class NcaFileSystemServiceImpl : IDisposable
|
|||
_romFsUnrecoverableByGameCardAccessFailedCount = 0;
|
||||
|
||||
_romfsCountMutex = new SdkMutexType();
|
||||
|
||||
StorageAccessSpeedControl.SetTargetProgramIdRange(_config.FsServer, _config.StorageAccessSpeedControlRange);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
@ -386,9 +389,8 @@ public class NcaFileSystemServiceImpl : IDisposable
|
|||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
using var storage = new SharedRef<IStorage>();
|
||||
using var storageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>();
|
||||
res = OpenStorageByContentType(ref storage.Ref, ref storageAccessSplitter.Ref, in ncaReader,
|
||||
out NcaFsHeader.FsType fsType, type, mountInfo.IsGameCard(), canMountSystemDataPrivate);
|
||||
res = OpenStorageByContentType(ref storage.Ref, in ncaReader, out NcaFsHeader.FsType fsType, type,
|
||||
mountInfo.IsGameCard(), canMountSystemDataPrivate);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
switch (fsType)
|
||||
|
@ -475,9 +477,8 @@ public class NcaFileSystemServiceImpl : IDisposable
|
|||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
using var storage = new SharedRef<IStorage>();
|
||||
using var storageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>();
|
||||
res = OpenStorageByContentType(ref storage.Ref, ref storageAccessSplitter.Ref, in ncaReader,
|
||||
out NcaFsHeader.FsType fsType, type, mountInfo.IsGameCard(), canMountSystemDataPrivate: false);
|
||||
res = OpenStorageByContentType(ref storage.Ref, in ncaReader, out NcaFsHeader.FsType fsType, type,
|
||||
mountInfo.IsGameCard(), canMountSystemDataPrivate: false);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
if (fsType != NcaFsHeader.FsType.RomFs)
|
||||
|
@ -486,18 +487,15 @@ public class NcaFileSystemServiceImpl : IDisposable
|
|||
return _config.RomFsCreator.Create(ref outFileSystem, in storage).Ret();
|
||||
}
|
||||
|
||||
public Result OpenDataStorage(ref SharedRef<IStorage> outStorage,
|
||||
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, ref Hash outNcaDigest,
|
||||
ref readonly Path path, ContentAttributes attributes, FileSystemProxyType type, ulong id)
|
||||
public Result OpenDataStorage(ref SharedRef<IStorage> outStorage, ref Hash outNcaDigest, ref readonly Path path,
|
||||
ContentAttributes attributes, FileSystemProxyType type, ulong id)
|
||||
{
|
||||
return OpenDataStorage(ref outStorage, ref outStorageAccessSplitter, ref outNcaDigest, in path, attributes,
|
||||
type, id, canMountSystemDataPrivate: false).Ret();
|
||||
return OpenDataStorage(ref outStorage, ref outNcaDigest, in path, attributes, type, id,
|
||||
canMountSystemDataPrivate: false).Ret();
|
||||
}
|
||||
|
||||
public Result OpenDataStorage(ref SharedRef<IStorage> outStorage,
|
||||
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, ref Hash outNcaDigest,
|
||||
ref readonly Path path, ContentAttributes attributes, FileSystemProxyType type, ulong id,
|
||||
bool canMountSystemDataPrivate)
|
||||
public Result OpenDataStorage(ref SharedRef<IStorage> outStorage, ref Hash outNcaDigest, ref readonly Path path,
|
||||
ContentAttributes attributes, FileSystemProxyType type, ulong id, bool canMountSystemDataPrivate)
|
||||
{
|
||||
using var ncaReader = new SharedRef<NcaReader>();
|
||||
Result res = ParseNca(ref ncaReader.Ref, out bool isGameCard, path.GetString(), attributes, id);
|
||||
|
@ -508,8 +506,8 @@ public class NcaFileSystemServiceImpl : IDisposable
|
|||
GenerateNcaDigest(out outNcaDigest, ncaReader.Get, null);
|
||||
}
|
||||
|
||||
res = OpenStorageByContentType(ref outStorage, ref outStorageAccessSplitter, in ncaReader,
|
||||
out NcaFsHeader.FsType fsType, type, isGameCard, canMountSystemDataPrivate);
|
||||
res = OpenStorageByContentType(ref outStorage, in ncaReader, out NcaFsHeader.FsType fsType, type, isGameCard,
|
||||
canMountSystemDataPrivate);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
if (fsType != NcaFsHeader.FsType.RomFs)
|
||||
|
@ -518,18 +516,15 @@ public class NcaFileSystemServiceImpl : IDisposable
|
|||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result OpenStorageWithPatch(ref SharedRef<IStorage> outStorage,
|
||||
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, ref Hash outNcaDigest,
|
||||
public Result OpenStorageWithPatch(ref SharedRef<IStorage> outStorage, ref Hash outNcaDigest,
|
||||
ref readonly Path originalNcaPath, ContentAttributes originalAttributes, ref readonly Path currentNcaPath,
|
||||
ContentAttributes currentAttributes, FileSystemProxyType type, ulong originalId, ulong currentId)
|
||||
{
|
||||
return OpenStorageWithPatch(ref outStorage, ref outStorageAccessSplitter, ref outNcaDigest,
|
||||
in originalNcaPath, originalAttributes, in currentNcaPath, currentAttributes, type, originalId, currentId,
|
||||
false).Ret();
|
||||
return OpenStorageWithPatch(ref outStorage, ref outNcaDigest, in originalNcaPath, originalAttributes,
|
||||
in currentNcaPath, currentAttributes, type, originalId, currentId, false).Ret();
|
||||
}
|
||||
|
||||
public Result OpenStorageWithPatch(ref SharedRef<IStorage> outStorage,
|
||||
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, ref Hash outNcaDigest,
|
||||
public Result OpenStorageWithPatch(ref SharedRef<IStorage> outStorage, ref Hash outNcaDigest,
|
||||
ref readonly Path originalNcaPath, ContentAttributes originalAttributes, ref readonly Path currentNcaPath,
|
||||
ContentAttributes currentAttributes, FileSystemProxyType type, ulong originalId, ulong currentId,
|
||||
bool canMountSystemDataPrivate)
|
||||
|
@ -567,8 +562,8 @@ public class NcaFileSystemServiceImpl : IDisposable
|
|||
GenerateNcaDigest(out outNcaDigest, originalNcaReader.Get, currentNcaReader.Get);
|
||||
}
|
||||
|
||||
res = OpenStorageWithPatchByContentType(ref outStorage, ref outStorageAccessSplitter, in originalNcaReader,
|
||||
in currentNcaReader, out NcaFsHeader.FsType fsType, type, canMountSystemDataPrivate);
|
||||
res = OpenStorageWithPatchByContentType(ref outStorage, in originalNcaReader, in currentNcaReader,
|
||||
out NcaFsHeader.FsType fsType, type, canMountSystemDataPrivate);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
if (fsType != NcaFsHeader.FsType.RomFs)
|
||||
|
@ -582,11 +577,10 @@ public class NcaFileSystemServiceImpl : IDisposable
|
|||
FileSystemProxyType type, ulong originalId, ulong currentId)
|
||||
{
|
||||
using var storage = new SharedRef<IStorage>();
|
||||
using var storageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>();
|
||||
using var fileSystem = new SharedRef<IFileSystem>();
|
||||
|
||||
Result res = OpenStorageWithPatch(ref storage.Ref, ref storageAccessSplitter.Ref, ref Unsafe.NullRef<Hash>(),
|
||||
in originalNcaPath, originalAttributes, in currentNcaPath, currentAttributes, type, originalId, currentId,
|
||||
Result res = OpenStorageWithPatch(ref storage.Ref, ref Unsafe.NullRef<Hash>(), in originalNcaPath,
|
||||
originalAttributes, in currentNcaPath, currentAttributes, type, originalId, currentId,
|
||||
canMountSystemDataPrivate: false);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
|
@ -658,7 +652,7 @@ public class NcaFileSystemServiceImpl : IDisposable
|
|||
{
|
||||
using SharedRef<IFileSystem> tempFileSystem = SharedRef<IFileSystem>.CreateMove(ref subDirFs.Ref);
|
||||
res = _config.EncryptedFsCreator.Create(ref subDirFs.Ref, in tempFileSystem,
|
||||
IEncryptedFileSystemCreator.KeyId.Content, in _encryptionSeed);
|
||||
IEncryptedFileSystemCreator.KeyId.Content);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
}
|
||||
|
||||
|
@ -933,7 +927,7 @@ public class NcaFileSystemServiceImpl : IDisposable
|
|||
res = OpenHostFileSystem(ref outFileSystem, in pathRoot, openCaseSensitive: true);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
break;
|
||||
|
||||
|
||||
case MountInfo.FileSystemType.LocalFs:
|
||||
res = _config.LocalFsCreator.Create(ref outFileSystem, in pathRoot, openCaseSensitive: true);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
@ -1148,7 +1142,6 @@ public class NcaFileSystemServiceImpl : IDisposable
|
|||
}
|
||||
|
||||
public Result OpenStorageByContentType(ref SharedRef<IStorage> outNcaStorage,
|
||||
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter,
|
||||
ref readonly SharedRef<NcaReader> ncaReader, out NcaFsHeader.FsType outFsType, FileSystemProxyType fsProxyType,
|
||||
bool isGameCard, bool canMountSystemDataPrivate)
|
||||
{
|
||||
|
@ -1221,8 +1214,7 @@ public class NcaFileSystemServiceImpl : IDisposable
|
|||
|
||||
var ncaFsHeaderReader = new NcaFsHeaderReader();
|
||||
|
||||
res = _config.StorageOnNcaCreator.Create(ref outNcaStorage, ref outStorageAccessSplitter, ref ncaFsHeaderReader,
|
||||
in ncaReader, partitionIndex);
|
||||
res = _config.StorageOnNcaCreator.Create(ref outNcaStorage, ref ncaFsHeaderReader, in ncaReader, partitionIndex);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
outFsType = ncaFsHeaderReader.GetFsType();
|
||||
|
@ -1230,7 +1222,6 @@ public class NcaFileSystemServiceImpl : IDisposable
|
|||
}
|
||||
|
||||
public Result OpenStorageWithPatchByContentType(ref SharedRef<IStorage> outNcaStorage,
|
||||
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter,
|
||||
ref readonly SharedRef<NcaReader> originalNcaReader, ref readonly SharedRef<NcaReader> currentNcaReader,
|
||||
out NcaFsHeader.FsType outFsType, FileSystemProxyType fsProxyType, bool canMountSystemDataPrivate)
|
||||
{
|
||||
|
@ -1279,21 +1270,14 @@ public class NcaFileSystemServiceImpl : IDisposable
|
|||
|
||||
var ncaFsHeaderReader = new NcaFsHeaderReader();
|
||||
|
||||
res = _config.StorageOnNcaCreator.CreateWithPatch(ref outNcaStorage, ref outStorageAccessSplitter,
|
||||
ref ncaFsHeaderReader, in originalNcaReader, in currentNcaReader, partitionIndex);
|
||||
res = _config.StorageOnNcaCreator.CreateWithPatch(ref outNcaStorage, ref ncaFsHeaderReader,
|
||||
in originalNcaReader, in currentNcaReader, partitionIndex);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
outFsType = ncaFsHeaderReader.GetFsType();
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result SetSdCardEncryptionSeed(in EncryptionSeed encryptionSeed)
|
||||
{
|
||||
_encryptionSeed = encryptionSeed;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result ResolveRomReferenceProgramId(out ProgramId outTargetProgramId, ProgramId programId,
|
||||
byte programIndex)
|
||||
{
|
||||
|
@ -1393,6 +1377,11 @@ public class NcaFileSystemServiceImpl : IDisposable
|
|||
return Result.Success;
|
||||
}
|
||||
|
||||
public DataId RedirectDataId(DataId dataId, StorageId storageId)
|
||||
{
|
||||
return _config.RedirectDataId?.Invoke(dataId, storageId) ?? dataId;
|
||||
}
|
||||
|
||||
public Result ResolveDataPath(ref Path outPath, out ContentAttributes outContentAttributes, DataId dataId,
|
||||
StorageId storageId)
|
||||
{
|
||||
|
@ -1418,17 +1407,38 @@ public class NcaFileSystemServiceImpl : IDisposable
|
|||
return _config.LocationResolverSet.ResolveRegisteredHtmlDocumentPath(ref outPath, out outContentAttributes, programId).Ret();
|
||||
}
|
||||
|
||||
internal StorageLayoutType GetStorageFlag(ulong programId)
|
||||
internal StorageLayoutType GetStorageFlag(ulong contentProgramId, ulong callerProgramId)
|
||||
{
|
||||
Assert.SdkRequiresNotEqual(_config.SpeedEmulationRange.ProgramIdWithoutPlatformIdMax, 0ul);
|
||||
|
||||
ulong programIdWithoutPlatformId = Impl.Utility.ClearPlatformIdInProgramId(programId);
|
||||
StorageLayoutType storageFlag = 0;
|
||||
|
||||
if (StorageAccessSpeedControl.IsTargetProgramId(_config.FsServer, callerProgramId))
|
||||
{
|
||||
storageFlag |= StorageLayoutType.IsApp;
|
||||
}
|
||||
|
||||
ulong programIdWithoutPlatformId = Impl.Utility.ClearPlatformIdInProgramId(contentProgramId);
|
||||
|
||||
if (programIdWithoutPlatformId >= _config.SpeedEmulationRange.ProgramIdWithoutPlatformIdMin &&
|
||||
programIdWithoutPlatformId <= _config.SpeedEmulationRange.ProgramIdWithoutPlatformIdMax)
|
||||
return StorageLayoutType.Bis;
|
||||
else
|
||||
return StorageLayoutType.All;
|
||||
return storageFlag | StorageLayoutType.All;
|
||||
}
|
||||
|
||||
internal StorageLayoutType GetStorageFlag(ulong programId)
|
||||
{
|
||||
return GetStorageFlag(programId, programId);
|
||||
}
|
||||
|
||||
internal StorageLayoutType GetStorageFlagByContentStorageId(ContentStorageId storageId, ulong programId)
|
||||
{
|
||||
if (storageId == ContentStorageId.System)
|
||||
return StorageLayoutType.Bis;
|
||||
|
||||
StorageLayoutType flag = StorageAccessSpeedControl.IsTargetProgramId(_config.FsServer, programId) ? StorageLayoutType.IsApp : 0;
|
||||
return flag | StorageLayoutType.All;
|
||||
}
|
||||
|
||||
public Result HandleResolubleAccessFailure(out bool wasDeferred, Result nonDeferredResult,
|
||||
|
|
|
@ -1192,7 +1192,7 @@ public class SaveDataFileSystemServiceImpl : IDisposable
|
|||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
res = _config.EncryptedFsCreator.Create(ref outFileSystem, in baseFileSystem,
|
||||
IEncryptedFileSystemCreator.KeyId.Save, in _encryptionSeed);
|
||||
IEncryptedFileSystemCreator.KeyId.Save);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
break;
|
||||
|
|
|
@ -7,13 +7,36 @@ using LibHac.Util;
|
|||
|
||||
namespace LibHac.FsSystem;
|
||||
|
||||
/// <summary>
|
||||
/// The default <see cref="IAsynchronousAccessSplitter"/> that is used when an <see cref="IStorage"/>
|
||||
/// or <see cref="IFile"/> doesn't need any special logic to split a request into multiple chunks.
|
||||
/// </summary>
|
||||
/// <remarks>Based on nnSdk 18.3.0 (FS 18.0.0)</remarks>
|
||||
public class DefaultAsynchronousAccessSplitter : IAsynchronousAccessSplitter
|
||||
{
|
||||
public void Dispose() { }
|
||||
|
||||
public Result QueryAppropriateOffset(out long offsetAppropriate, long startOffset, long accessSize, long alignmentSize)
|
||||
{
|
||||
offsetAppropriate = Alignment.AlignDown(startOffset + accessSize, alignmentSize);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result QueryInvocationCount(out long count, long startOffset, long endOffset, long accessSize, long alignmentSize)
|
||||
{
|
||||
long alignedStartOffset = Alignment.AlignDown(startOffset, alignmentSize);
|
||||
count = BitUtil.DivideUp(endOffset - alignedStartOffset, accessSize);
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Splits read and write requests on an <see cref="IFile"/> or <see cref="IStorage"/> into smaller chunks
|
||||
/// so the request can be processed by multiple threads simultaneously.</para>
|
||||
/// <para>This interface exists because of <see cref="CompressedStorage"/> where it will split requests into
|
||||
/// chunks that start and end on the boundaries of the compressed blocks.</para>
|
||||
/// </summary>
|
||||
/// <remarks>Based on nnSdk 13.4.0 (FS 13.1.0)</remarks>
|
||||
/// <remarks>Based on nnSdk 18.3.0 (FS 18.0.0)</remarks>
|
||||
public interface IAsynchronousAccessSplitter : IDisposable
|
||||
{
|
||||
private static readonly DefaultAsynchronousAccessSplitter DefaultAccessSplitter = new();
|
||||
|
@ -40,7 +63,7 @@ public interface IAsynchronousAccessSplitter : IDisposable
|
|||
if (res.IsFailure()) return res.Miss();
|
||||
Assert.SdkNotEqual(startOffset, offsetAppropriate);
|
||||
|
||||
nextOffset = Math.Min(startOffset, offsetAppropriate);
|
||||
nextOffset = Math.Min(offsetAppropriate, endOffset);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
|
@ -66,47 +89,18 @@ public interface IAsynchronousAccessSplitter : IDisposable
|
|||
Result QueryAppropriateOffset(out long offsetAppropriate, long startOffset, long accessSize, long alignmentSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The default <see cref="IAsynchronousAccessSplitter"/> that is used when an <see cref="IStorage"/>
|
||||
/// or <see cref="IFile"/> doesn't need any special logic to split a request into multiple chunks.
|
||||
/// </summary>
|
||||
/// <remarks>Based on nnSdk 13.4.0 (FS 13.1.0)</remarks>
|
||||
public class DefaultAsynchronousAccessSplitter : IAsynchronousAccessSplitter
|
||||
{
|
||||
public void Dispose() { }
|
||||
|
||||
public Result QueryAppropriateOffset(out long offsetAppropriate, long startOffset, long accessSize, long alignmentSize)
|
||||
{
|
||||
offsetAppropriate = Alignment.AlignDown(startOffset + accessSize, alignmentSize);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result QueryInvocationCount(out long count, long startOffset, long endOffset, long accessSize, long alignmentSize)
|
||||
{
|
||||
long alignedStartOffset = Alignment.AlignDown(startOffset, alignmentSize);
|
||||
count = BitUtil.DivideUp(endOffset - alignedStartOffset, accessSize);
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
public class AsynchronousAccessStorage : IStorage
|
||||
{
|
||||
private SharedRef<IStorage> _baseStorage;
|
||||
// private ThreadPool _threadPool;
|
||||
private IAsynchronousAccessSplitter _baseStorageAccessSplitter;
|
||||
private bool _baseStorageHasAccessSplitter;
|
||||
|
||||
public AsynchronousAccessStorage(ref readonly SharedRef<IStorage> baseStorage) : this(in baseStorage,
|
||||
IAsynchronousAccessSplitter.GetDefaultAsynchronousAccessSplitter())
|
||||
{
|
||||
}
|
||||
|
||||
public AsynchronousAccessStorage(ref readonly SharedRef<IStorage> baseStorage, IAsynchronousAccessSplitter baseStorageAccessSplitter)
|
||||
public AsynchronousAccessStorage(ref readonly SharedRef<IStorage> baseStorage)
|
||||
{
|
||||
_baseStorage = SharedRef<IStorage>.CreateCopy(in baseStorage);
|
||||
_baseStorageAccessSplitter = baseStorageAccessSplitter;
|
||||
_baseStorageHasAccessSplitter = true;
|
||||
|
||||
Assert.SdkRequiresNotNull(in _baseStorage);
|
||||
Assert.SdkRequiresNotNull(_baseStorageAccessSplitter);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
|
@ -115,10 +109,9 @@ public class AsynchronousAccessStorage : IStorage
|
|||
base.Dispose();
|
||||
}
|
||||
|
||||
public void SetBaseStorage(ref readonly SharedRef<IStorage> baseStorage, IAsynchronousAccessSplitter baseStorageAccessSplitter)
|
||||
public void SetBaseStorage(ref readonly SharedRef<IStorage> baseStorage)
|
||||
{
|
||||
_baseStorage.SetByCopy(in baseStorage);
|
||||
_baseStorageAccessSplitter = baseStorageAccessSplitter;
|
||||
}
|
||||
|
||||
// Todo: Implement
|
||||
|
|
|
@ -188,8 +188,7 @@ public class BlockCacheBufferedStorage : IStorage
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private Result GetAssociateBuffer(out Buffer outRange, out CacheEntry outEntry, long offset, int idealSize,
|
||||
bool isAllocateForWrite)
|
||||
private Result GetAssociateBuffer(out Buffer outRange, out CacheEntry outEntry, long offset, bool isAllocateForWrite)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
@ -241,8 +240,9 @@ public class BlockCacheBufferedStorage : IStorage
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private Result BulkRead(long offset, Span<byte> buffer, ref Buffer memoryRangeHead, ref Buffer memoryRangeTail,
|
||||
ref CacheEntry entryHead, ref CacheEntry entryTail, bool isHeadCacheNeeded, bool isTailCacheNeeded)
|
||||
private Result BulkRead(out bool outIsHeadCacheStored, out bool outIsTailCacheStored, long offset,
|
||||
Span<byte> buffer, ref Buffer memoryRangeHead, ref Buffer memoryRangeTail, ref CacheEntry entryHead,
|
||||
ref CacheEntry entryTail)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
|
|
@ -18,7 +18,8 @@ public class CompressedStorage : IStorage, IAsynchronousAccessSplitter
|
|||
private long _continuousReadingSizeMax;
|
||||
private readonly BucketTree _bucketTree;
|
||||
private ValueSubStorage _dataStorage;
|
||||
private GetDecompressorFunction _getDecompressorFunction;
|
||||
private IDecompressorFactory _decompressorFactory;
|
||||
private ulong _alignment;
|
||||
|
||||
public CompressedStorageCore()
|
||||
{
|
||||
|
@ -67,12 +68,12 @@ public class CompressedStorage : IStorage, IAsynchronousAccessSplitter
|
|||
public Result Initialize(MemoryResource allocatorForBucketTree, ref readonly ValueSubStorage dataStorage,
|
||||
ref readonly ValueSubStorage nodeStorage, ref readonly ValueSubStorage entryStorage,
|
||||
int bucketTreeEntryCount, long blockSizeMax, long continuousReadingSizeMax,
|
||||
GetDecompressorFunction getDecompressorFunc)
|
||||
IDecompressorFactory decompressorFactory, ulong alignment)
|
||||
{
|
||||
Assert.SdkRequiresNotNull(allocatorForBucketTree);
|
||||
Assert.SdkRequiresLess(0, blockSizeMax);
|
||||
Assert.SdkRequiresLessEqual(blockSizeMax, continuousReadingSizeMax);
|
||||
Assert.SdkRequiresNotNull(getDecompressorFunc);
|
||||
Assert.SdkRequiresNotNull(decompressorFactory);
|
||||
|
||||
Result res = _bucketTree.Initialize(allocatorForBucketTree, in nodeStorage, in entryStorage, NodeSize,
|
||||
Unsafe.SizeOf<Entry>(), bucketTreeEntryCount);
|
||||
|
@ -81,7 +82,8 @@ public class CompressedStorage : IStorage, IAsynchronousAccessSplitter
|
|||
_blockSizeMax = blockSizeMax;
|
||||
_continuousReadingSizeMax = continuousReadingSizeMax;
|
||||
_dataStorage.Set(in dataStorage);
|
||||
_getDecompressorFunction = getDecompressorFunc;
|
||||
_decompressorFactory = decompressorFactory;
|
||||
_alignment = alignment;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
@ -120,14 +122,6 @@ public class CompressedStorage : IStorage, IAsynchronousAccessSplitter
|
|||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private DecompressorFunction GetDecompressor(CompressionType type)
|
||||
{
|
||||
if (CompressionTypeUtility.IsUnknownType(type))
|
||||
return null;
|
||||
|
||||
return _getDecompressorFunction(type);
|
||||
}
|
||||
}
|
||||
|
||||
public class CacheManager : IDisposable
|
||||
|
@ -299,11 +293,11 @@ public class CompressedStorage : IStorage, IAsynchronousAccessSplitter
|
|||
public Result Initialize(MemoryResource allocatorForBucketTree, IBufferManager allocatorForCacheManager,
|
||||
ref readonly ValueSubStorage dataStorage, ref readonly ValueSubStorage nodeStorage,
|
||||
ref readonly ValueSubStorage entryStorage, int bucketTreeEntryCount, long blockSizeMax,
|
||||
long continuousReadingSizeMax, GetDecompressorFunction getDecompressorFunc, long cacheSize0, long cacheSize1,
|
||||
int maxCacheEntries)
|
||||
long continuousReadingSizeMax, IDecompressorFactory decompressorFactory, ulong alignment, long cacheSize0,
|
||||
long cacheSize1, int maxCacheEntries)
|
||||
{
|
||||
Result res = _core.Initialize(allocatorForBucketTree, in dataStorage, in nodeStorage, in entryStorage,
|
||||
bucketTreeEntryCount, blockSizeMax, continuousReadingSizeMax, getDecompressorFunc);
|
||||
bucketTreeEntryCount, blockSizeMax, continuousReadingSizeMax, decompressorFactory, alignment);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
res = _core.GetSize(out long size);
|
||||
|
|
|
@ -1,35 +1,29 @@
|
|||
using System;
|
||||
using LibHac.Common;
|
||||
|
||||
namespace LibHac.FsSystem;
|
||||
|
||||
public enum CompressionType : byte
|
||||
{
|
||||
None = 0,
|
||||
Zeroed = 1,
|
||||
FillZero = 1,
|
||||
Lz4 = 3,
|
||||
Unknown = 4
|
||||
}
|
||||
|
||||
public ref struct DecompressionTask
|
||||
{
|
||||
public Span<byte> Destination;
|
||||
public ReadOnlySpan<byte> Source;
|
||||
}
|
||||
|
||||
public delegate Result DecompressorFunction(DecompressionTask task);
|
||||
|
||||
public delegate DecompressorFunction GetDecompressorFunction(CompressionType compressionType);
|
||||
public delegate Result CompressionGetData(Span<byte> buffer);
|
||||
public delegate Result CompressionProcessData(Span<byte> workBuffer, int sizeBufferRequired, CompressionGetData getDataFunc);
|
||||
|
||||
public static class CompressionTypeUtility
|
||||
{
|
||||
public static bool IsBlockAlignmentRequired(CompressionType type)
|
||||
{
|
||||
return type != CompressionType.None && type != CompressionType.Zeroed;
|
||||
return type != CompressionType.None && type != CompressionType.FillZero;
|
||||
}
|
||||
|
||||
public static bool IsDataStorageAccessRequired(CompressionType type)
|
||||
{
|
||||
return type != CompressionType.Zeroed;
|
||||
return type != CompressionType.FillZero;
|
||||
}
|
||||
|
||||
public static bool IsRandomAccessible(CompressionType type)
|
||||
|
@ -41,4 +35,15 @@ public static class CompressionTypeUtility
|
|||
{
|
||||
return type >= CompressionType.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IDecompressorFactory
|
||||
{
|
||||
public Result GetDecompressor(ref UniqueRef<IDecompressor> outDecompressor, CompressionProcessData processDataFunc, Span<byte> buffer);
|
||||
}
|
||||
|
||||
public interface IDecompressor : IDisposable
|
||||
{
|
||||
Result Decompress(CompressionType type, int compressedSize, Span<byte> buffer, int decompressedSize);
|
||||
Result Unk();
|
||||
}
|
|
@ -31,7 +31,7 @@ public struct NcaCryptoConfiguration
|
|||
|
||||
public struct NcaCompressionConfiguration
|
||||
{
|
||||
public GetDecompressorFunction GetDecompressorFunc;
|
||||
public IDecompressorFactory DecompressorFactory;
|
||||
}
|
||||
|
||||
public static class NcaKeyFunctions
|
||||
|
@ -203,9 +203,7 @@ public class NcaFileSystemDriver : IDisposable
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenStorage(ref SharedRef<IStorage> outStorage,
|
||||
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, ref NcaFsHeaderReader outHeaderReader,
|
||||
int fsIndex)
|
||||
public Result OpenStorage(ref SharedRef<IStorage> outStorage, ref NcaFsHeaderReader outHeaderReader, int fsIndex)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
@ -323,7 +321,7 @@ public class NcaFileSystemDriver : IDisposable
|
|||
|
||||
private Result CreateIntegrityVerificationStorage(ref SharedRef<IStorage> outStorage,
|
||||
ref readonly SharedRef<IStorage> baseStorage, in NcaFsHeader.HashData.IntegrityMetaInfo metaInfo,
|
||||
IHash256GeneratorFactory hashGeneratorFactory)
|
||||
bool alwaysCreateCacheEntries, IHash256GeneratorFactory hashGeneratorFactory)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
@ -338,22 +336,22 @@ public class NcaFileSystemDriver : IDisposable
|
|||
private Result CreateIntegrityVerificationStorageImpl(ref SharedRef<IStorage> outStorage,
|
||||
ref readonly SharedRef<IStorage> baseStorage, in NcaFsHeader.HashData.IntegrityMetaInfo metaInfo,
|
||||
long layerInfoOffset, int maxDataCacheEntries, int maxHashCacheEntries, sbyte bufferLevel,
|
||||
IHash256GeneratorFactory hashGeneratorFactory)
|
||||
bool alwaysCreateCacheEntries, IHash256GeneratorFactory hashGeneratorFactory)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Result CreateCompressedStorage(ref SharedRef<IStorage> outStorage,
|
||||
ref SharedRef<CompressedStorage> outCompressedStorage, ref SharedRef<IStorage> outMetaStorage,
|
||||
ref readonly SharedRef<IStorage> baseStorage, in NcaCompressionInfo compressionInfo,
|
||||
GetDecompressorFunction getDecompressor, MemoryResource allocator, IBufferManager bufferManager)
|
||||
ref readonly SharedRef<IStorage> baseStorage, in NcaCompressionInfo compressionInfo, uint alignment,
|
||||
IDecompressorFactory decompressorFactory, MemoryResource allocator, IBufferManager bufferManager)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private Result CreateCompressedStorage(ref SharedRef<IStorage> outStorage,
|
||||
ref SharedRef<CompressedStorage> outCompressedStorage, ref SharedRef<IStorage> outMetaStorage,
|
||||
ref readonly SharedRef<IStorage> baseStorage, in NcaCompressionInfo compressionInfo)
|
||||
ref readonly SharedRef<IStorage> baseStorage, in NcaCompressionInfo compressionInfo, uint alignment)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ public class NcaReader : IDisposable
|
|||
private SharedRef<IStorage> _bodyStorage;
|
||||
private SharedRef<IStorage> _headerStorage;
|
||||
private SharedRef<IAesCtrDecryptor> _aesCtrDecryptor;
|
||||
private GetDecompressorFunction _getDecompressorFunc;
|
||||
private IDecompressorFactory _decompressorFactory;
|
||||
private IHash256GeneratorFactorySelector _hashGeneratorFactorySelector;
|
||||
|
||||
public NcaReader(in RuntimeNcaHeader runtimeNcaHeader, ref readonly SharedRef<IStorage> notVerifiedHeaderStorage,
|
||||
|
@ -39,7 +39,7 @@ public class NcaReader : IDisposable
|
|||
_bodyStorage = SharedRef<IStorage>.CreateCopy(in bodyStorage);
|
||||
_aesCtrDecryptor = SharedRef<IAesCtrDecryptor>.CreateCopy(in aesCtrDecryptor);
|
||||
|
||||
_getDecompressorFunc = compressionConfig.GetDecompressorFunc;
|
||||
_decompressorFactory = compressionConfig.DecompressorFactory;
|
||||
_hashGeneratorFactorySelector = hashGeneratorFactorySelector;
|
||||
}
|
||||
|
||||
|
@ -181,10 +181,10 @@ public class NcaReader : IDisposable
|
|||
return SharedRef<IAesCtrDecryptor>.CreateCopy(in _aesCtrDecryptor);
|
||||
}
|
||||
|
||||
public GetDecompressorFunction GetDecompressor()
|
||||
public IDecompressorFactory GetDecompressorFactory()
|
||||
{
|
||||
Assert.SdkRequiresNotNull(_getDecompressorFunc);
|
||||
return _getDecompressorFunc;
|
||||
Assert.SdkRequiresNotNull(_decompressorFactory);
|
||||
return _decompressorFactory;
|
||||
}
|
||||
|
||||
public IHash256GeneratorFactorySelector GetHashGeneratorFactorySelector()
|
||||
|
|
|
@ -13,6 +13,8 @@ internal enum StorageLayoutType
|
|||
SdCard = 1 << 1,
|
||||
GameCard = 1 << 2,
|
||||
Usb = 1 << 3,
|
||||
|
||||
IsApp = 1 << 24,
|
||||
|
||||
NonGameCard = Bis | SdCard | Usb,
|
||||
All = Bis | SdCard | GameCard | Usb
|
||||
|
|
|
@ -141,7 +141,7 @@ internal class CompressedStorage : IStorage
|
|||
res = _dataStorage.Read(currentEntry.PhysicalOffset + dataOffsetInEntry, entryDestination);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
}
|
||||
else if (currentEntry.CompressionType == CompressionType.Zeroed)
|
||||
else if (currentEntry.CompressionType == CompressionType.FillZero)
|
||||
{
|
||||
entryDestination.Clear();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue