Update the NCA filesystem service for FS 18

This commit is contained in:
Alex Barney 2024-08-03 17:31:04 -07:00
parent 78d9847b6a
commit b5158764b9
24 changed files with 323 additions and 271 deletions

View file

@ -124,6 +124,7 @@ public enum OperationId
QueryUnpreparedRange = 4, QueryUnpreparedRange = 4,
QueryLazyLoadCompletionRate = 5, QueryLazyLoadCompletionRate = 5,
SetLazyLoadPriority = 6, SetLazyLoadPriority = 6,
GetAsynchronousAccessSplitter = 7,
ReadyLazyLoadFile = 10001 ReadyLazyLoadFile = 10001
} }

View file

@ -1,5 +1,6 @@
using System; using System;
using LibHac.Common; using LibHac.Common;
using LibHac.Fs;
using LibHac.FsSrv.FsCreator; using LibHac.FsSrv.FsCreator;
namespace LibHac.FsSrv; 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 GenerateSeedUniqueMac(Span<byte> outMacBuffer, ReadOnlySpan<byte> data, ReadOnlySpan<byte> seed);
public delegate Result GenerateDeviceUniqueMac(Span<byte> outMacBuffer, ReadOnlySpan<byte> data, DeviceUniqueMacType macType); 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, public delegate Result SaveTransferAesKeyGenerator(Span<byte> outKeyBuffer,
SaveDataTransferCryptoConfiguration.KeyIndex index, ReadOnlySpan<byte> keySource, int keyGeneration); SaveDataTransferCryptoConfiguration.KeyIndex index, ReadOnlySpan<byte> keySource, int keyGeneration);

View file

@ -66,7 +66,7 @@ public class FileSystemProxyCoreImpl
tempFs.SetByMove(ref fileSystem.Ref); tempFs.SetByMove(ref fileSystem.Ref);
res = _fsCreators.EncryptedFileSystemCreator.Create(ref fileSystem.Ref, in tempFs, res = _fsCreators.EncryptedFileSystemCreator.Create(ref fileSystem.Ref, in tempFs,
IEncryptedFileSystemCreator.KeyId.CustomStorage, in _sdEncryptionSeed); IEncryptedFileSystemCreator.KeyId.CustomStorage);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
} }
else else
@ -114,7 +114,6 @@ public class FileSystemProxyCoreImpl
public Result SetSdCardEncryptionSeed(in EncryptionSeed seed) public Result SetSdCardEncryptionSeed(in EncryptionSeed seed)
{ {
_sdEncryptionSeed = seed; return _fsCreators.EncryptedFileSystemCreator.SetEncryptionSeed(IEncryptedFileSystemCreator.KeyId.Save, in seed).Ret();
return Result.Success;
} }
} }

View file

@ -957,13 +957,7 @@ public class FileSystemProxyImpl : IFileSystemProxy, IFileSystemProxyForLoader
res = GetSaveDataFileSystemService(out SaveDataFileSystemService saveFsService); res = GetSaveDataFileSystemService(out SaveDataFileSystemService saveFsService);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
res = saveFsService.SetSdCardEncryptionSeed(in seed); return saveFsService.SetSdCardEncryptionSeed(in seed).Ret();
if (res.IsFailure()) return res.Miss();
res = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
if (res.IsFailure()) return res.Miss();
return ncaFsService.SetSdCardEncryptionSeed(in seed).Ret();
} }
public Result GetAndClearErrorInfo(out FileSystemProxyErrorInfo errorInfo) public Result GetAndClearErrorInfo(out FileSystemProxyErrorInfo errorInfo)

View file

@ -47,6 +47,7 @@ internal struct FileSystemServerGlobals : IDisposable
public GameCardServiceGlobals GameCardService; public GameCardServiceGlobals GameCardService;
public HierarchicalIntegrityVerificationStorageGlobals HierarchicalIntegrityVerificationStorage; public HierarchicalIntegrityVerificationStorageGlobals HierarchicalIntegrityVerificationStorage;
public SpeedEmulationConfigurationGlobals SpeedEmulationConfiguration; public SpeedEmulationConfigurationGlobals SpeedEmulationConfiguration;
public StorageAccessSpeedControlGlobals StorageAccessSpeedControl;
public void Initialize(HorizonClient horizonClient, FileSystemServer fsServer) public void Initialize(HorizonClient horizonClient, FileSystemServer fsServer)
{ {

View file

@ -15,6 +15,9 @@ public static class FileSystemServerInitializer
{ {
private const ulong SpeedEmulationProgramIdWithoutPlatformIdMinimum = 0; private const ulong SpeedEmulationProgramIdWithoutPlatformIdMinimum = 0;
private const ulong SpeedEmulationProgramIdWithoutPlatformIdMaximum = 0x1FFF; 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; private const uint ContentDivisionSize = ConcatenationFileSystem.DefaultInternalFileSize;
@ -109,9 +112,11 @@ public static class FileSystemServerInitializer
var accessFailureManagementService = var accessFailureManagementService =
new AccessFailureManagementServiceImpl(in accessFailureManagementServiceConfig); new AccessFailureManagementServiceImpl(in accessFailureManagementServiceConfig);
var speedEmulationRange = var speedEmulationRange = new InternalProgramIdRangeForSpeedEmulation(
new InternalProgramIdRangeForSpeedEmulation(SpeedEmulationProgramIdWithoutPlatformIdMinimum, SpeedEmulationProgramIdWithoutPlatformIdMinimum, SpeedEmulationProgramIdWithoutPlatformIdMaximum);
SpeedEmulationProgramIdWithoutPlatformIdMaximum);
var storageAccessSpeedControlRange = new InternalProgramIdRangeForStorageAccessSpeedControl(
StorageAccessSpeedControlProgramIdWithoutPlatformIdMinimum, StorageAccessSpeedControlProgramIdWithoutPlatformIdMaximum);
var ncaFsServiceConfig = new NcaFileSystemServiceImpl.Configuration(); var ncaFsServiceConfig = new NcaFileSystemServiceImpl.Configuration();
ncaFsServiceConfig.BaseFsService = baseFsService; ncaFsServiceConfig.BaseFsService = baseFsService;
@ -127,6 +132,7 @@ public static class FileSystemServerInitializer
ncaFsServiceConfig.SpeedEmulationRange = speedEmulationRange; ncaFsServiceConfig.SpeedEmulationRange = speedEmulationRange;
ncaFsServiceConfig.AddOnContentDivisionSize = ContentDivisionSize; ncaFsServiceConfig.AddOnContentDivisionSize = ContentDivisionSize;
ncaFsServiceConfig.RomDivisionSize = ContentDivisionSize; ncaFsServiceConfig.RomDivisionSize = ContentDivisionSize;
ncaFsServiceConfig.StorageAccessSpeedControlRange = storageAccessSpeedControlRange;
ncaFsServiceConfig.FsServer = server; ncaFsServiceConfig.FsServer = server;
var ncaFsService = new NcaFileSystemServiceImpl(in ncaFsServiceConfig); var ncaFsService = new NcaFileSystemServiceImpl(in ncaFsServiceConfig);

View file

@ -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.Common.Keys;
using LibHac.Diag;
using LibHac.Fs; using LibHac.Fs;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
using LibHac.FsSystem;
using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem;
using static LibHac.FsSrv.FsCreator.IEncryptedFileSystemCreator; using static LibHac.FsSrv.FsCreator.IEncryptedFileSystemCreator;
@ -9,28 +13,67 @@ namespace LibHac.FsSrv.FsCreator;
public class EncryptedFileSystemCreator : IEncryptedFileSystemCreator 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) 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(); return ResultFs.InvalidArgument.Log();
} }
// todo: "proper" key generation instead of a lazy hack // 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, 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); outEncryptedFileSystem.SetByMove(ref encryptedFileSystem.Ref);
return Result.Success; 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;
}
} }

View file

@ -14,5 +14,7 @@ public interface IEncryptedFileSystemCreator
} }
Result Create(ref SharedRef<IFileSystem> outEncryptedFileSystem, ref readonly SharedRef<IFileSystem> baseFileSystem, 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);
} }

View file

@ -6,12 +6,10 @@ namespace LibHac.FsSrv.FsCreator;
public interface IStorageOnNcaCreator public interface IStorageOnNcaCreator
{ {
Result Create(ref SharedRef<IStorage> outStorage, Result Create(ref SharedRef<IStorage> outStorage, ref NcaFsHeaderReader outHeaderReader,
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, ref NcaFsHeaderReader outHeaderReader,
ref readonly SharedRef<NcaReader> ncaReader, int fsIndex); ref readonly SharedRef<NcaReader> ncaReader, int fsIndex);
Result CreateWithPatch(ref SharedRef<IStorage> outStorage, Result CreateWithPatch(ref SharedRef<IStorage> outStorage, ref NcaFsHeaderReader outHeaderReader,
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, ref NcaFsHeaderReader outHeaderReader,
ref readonly SharedRef<NcaReader> originalNcaReader, ref readonly SharedRef<NcaReader> currentNcaReader, ref readonly SharedRef<NcaReader> originalNcaReader, ref readonly SharedRef<NcaReader> currentNcaReader,
int fsIndex); int fsIndex);

View file

@ -27,28 +27,22 @@ public class StorageOnNcaCreator : IStorageOnNcaCreator
_hashGeneratorFactorySelector = hashGeneratorFactorySelector; _hashGeneratorFactorySelector = hashGeneratorFactorySelector;
} }
public Result Create(ref SharedRef<IStorage> outStorage, public Result Create(ref SharedRef<IStorage> outStorage, ref NcaFsHeaderReader outHeaderReader, ref readonly SharedRef<NcaReader> ncaReader, int fsIndex)
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, ref NcaFsHeaderReader outHeaderReader,
ref readonly SharedRef<NcaReader> ncaReader, int fsIndex)
{ {
var ncaFsDriver = new NcaFileSystemDriver(in ncaReader, _memoryResource, _bufferManager, _hashGeneratorFactorySelector); var ncaFsDriver = new NcaFileSystemDriver(in ncaReader, _memoryResource, _bufferManager, _hashGeneratorFactorySelector);
using var storage = new SharedRef<IStorage>(); using var storage = new SharedRef<IStorage>();
using var storageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>(); Result res = RomResultConverter.ConvertRomResult(ncaFsDriver.OpenStorage(ref storage.Ref, ref outHeaderReader, fsIndex));
Result res = RomResultConverter.ConvertRomResult(ncaFsDriver.OpenStorage(ref storage.Ref,
ref storageAccessSplitter.Ref, ref outHeaderReader, fsIndex));
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
using var resultConvertStorage = new SharedRef<RomResultConvertStorage>(new RomResultConvertStorage(in storage)); using var resultConvertStorage = new SharedRef<RomResultConvertStorage>(new RomResultConvertStorage(in storage));
outStorage.SetByMove(ref resultConvertStorage.Ref); outStorage.SetByMove(ref resultConvertStorage.Ref);
outStorageAccessSplitter.SetByMove(ref storageAccessSplitter.Ref);
return Result.Success; return Result.Success;
} }
public Result CreateWithPatch(ref SharedRef<IStorage> outStorage, 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, ref readonly SharedRef<NcaReader> originalNcaReader, ref readonly SharedRef<NcaReader> currentNcaReader,
int fsIndex) int fsIndex)
{ {
@ -56,15 +50,11 @@ public class StorageOnNcaCreator : IStorageOnNcaCreator
_bufferManager, _hashGeneratorFactorySelector); _bufferManager, _hashGeneratorFactorySelector);
using var storage = new SharedRef<IStorage>(); using var storage = new SharedRef<IStorage>();
using var storageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>(); Result res = RomResultConverter.ConvertRomResult(ncaFsDriver.OpenStorage(ref storage.Ref, ref outHeaderReader, fsIndex));
Result res = RomResultConverter.ConvertRomResult(ncaFsDriver.OpenStorage(ref storage.Ref,
ref storageAccessSplitter.Ref, ref outHeaderReader, fsIndex));
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
using var resultConvertStorage = new SharedRef<RomResultConvertStorage>(new RomResultConvertStorage(in storage)); using var resultConvertStorage = new SharedRef<RomResultConvertStorage>(new RomResultConvertStorage(in storage));
outStorage.SetByMove(ref resultConvertStorage.Ref); outStorage.SetByMove(ref resultConvertStorage.Ref);
outStorageAccessSplitter.SetByMove(ref storageAccessSplitter.Ref);
return Result.Success; return Result.Success;
} }

View file

@ -12,7 +12,6 @@ namespace LibHac.FsSrv.Impl;
public class DeepRetryStorage : IStorage public class DeepRetryStorage : IStorage
{ {
private AsynchronousAccessStorage _asyncStorage; private AsynchronousAccessStorage _asyncStorage;
private SharedRef<IAsynchronousAccessSplitter> _accessSplitter;
private SharedRef<IRomFileSystemAccessFailureManager> _parent; private SharedRef<IRomFileSystemAccessFailureManager> _parent;
private UniqueRef<IUniqueLock> _mountCountLock; private UniqueRef<IUniqueLock> _mountCountLock;
private DataStorageContext _dataStorageContext; private DataStorageContext _dataStorageContext;
@ -52,14 +51,12 @@ public class DeepRetryStorage : IStorage
} }
public DeepRetryStorage(ref readonly SharedRef<IStorage> baseStorage, public DeepRetryStorage(ref readonly SharedRef<IStorage> baseStorage,
ref readonly SharedRef<IAsynchronousAccessSplitter> accessSplitter,
ref readonly SharedRef<IRomFileSystemAccessFailureManager> parent, ref readonly SharedRef<IRomFileSystemAccessFailureManager> parent,
ref UniqueRef<IUniqueLock> mountCountSemaphore, ref UniqueRef<IUniqueLock> mountCountSemaphore,
bool deepRetryEnabled, FileSystemServer fsServer) bool deepRetryEnabled, FileSystemServer fsServer)
{ {
// Missing: Getting the thread pool via GetRegisteredThreadPool() // Missing: Getting the thread pool via GetRegisteredThreadPool()
_asyncStorage = new AsynchronousAccessStorage(in baseStorage, accessSplitter.Get); _asyncStorage = new AsynchronousAccessStorage(in baseStorage);
_accessSplitter = SharedRef<IAsynchronousAccessSplitter>.CreateCopy(in accessSplitter);
_parent = SharedRef<IRomFileSystemAccessFailureManager>.CreateCopy(in parent); _parent = SharedRef<IRomFileSystemAccessFailureManager>.CreateCopy(in parent);
_mountCountLock = UniqueRef<IUniqueLock>.Create(ref mountCountSemaphore); _mountCountLock = UniqueRef<IUniqueLock>.Create(ref mountCountSemaphore);
_dataStorageContext = new DataStorageContext(); _dataStorageContext = new DataStorageContext();
@ -70,14 +67,12 @@ public class DeepRetryStorage : IStorage
} }
public DeepRetryStorage(ref readonly SharedRef<IStorage> baseStorage, public DeepRetryStorage(ref readonly SharedRef<IStorage> baseStorage,
ref readonly SharedRef<IAsynchronousAccessSplitter> accessSplitter,
ref readonly SharedRef<IRomFileSystemAccessFailureManager> parent, ref readonly SharedRef<IRomFileSystemAccessFailureManager> parent,
ref UniqueRef<IUniqueLock> mountCountSemaphore, ref UniqueRef<IUniqueLock> mountCountSemaphore,
in Hash hash, ulong programId, StorageId storageId, FileSystemServer fsServer) in Hash hash, ulong programId, StorageId storageId, FileSystemServer fsServer)
{ {
// Missing: Getting the thread pool via GetRegisteredThreadPool() // Missing: Getting the thread pool via GetRegisteredThreadPool()
_asyncStorage = new AsynchronousAccessStorage(in baseStorage, accessSplitter.Get); _asyncStorage = new AsynchronousAccessStorage(in baseStorage);
_accessSplitter = SharedRef<IAsynchronousAccessSplitter>.CreateCopy(in accessSplitter);
_parent = SharedRef<IRomFileSystemAccessFailureManager>.CreateCopy(in parent); _parent = SharedRef<IRomFileSystemAccessFailureManager>.CreateCopy(in parent);
_mountCountLock = UniqueRef<IUniqueLock>.Create(ref mountCountSemaphore); _mountCountLock = UniqueRef<IUniqueLock>.Create(ref mountCountSemaphore);
_dataStorageContext = new DataStorageContext(in hash, programId, storageId); _dataStorageContext = new DataStorageContext(in hash, programId, storageId);
@ -92,7 +87,6 @@ public class DeepRetryStorage : IStorage
_readWriteLock.Dispose(); _readWriteLock.Dispose();
_mountCountLock.Destroy(); _mountCountLock.Destroy();
_parent.Destroy(); _parent.Destroy();
_accessSplitter.Destroy();
_asyncStorage.Dispose(); _asyncStorage.Dispose();
base.Dispose(); base.Dispose();
@ -143,7 +137,6 @@ public class DeepRetryStorage : IStorage
Assert.SdkNotNull(_parent); Assert.SdkNotNull(_parent);
using var remountStorage = new SharedRef<IStorage>(); using var remountStorage = new SharedRef<IStorage>();
using var remountStorageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>();
const int maxRetryCount = 2; const int maxRetryCount = 2;
@ -151,8 +144,8 @@ public class DeepRetryStorage : IStorage
Hash digest = default; Hash digest = default;
for (int i = 0; i < maxRetryCount; i++) for (int i = 0; i < maxRetryCount; i++)
{ {
retryResult = _parent.Get.OpenDataStorageCore(ref remountStorage.Ref, ref remountStorageAccessSplitter.Ref, retryResult = _parent.Get.OpenDataStorageCore(ref remountStorage.Ref, ref digest,
ref digest, _dataStorageContext.GetProgramIdValue(), _dataStorageContext.GetStorageId()); _dataStorageContext.GetProgramIdValue(), _dataStorageContext.GetStorageId());
if (!ResultFs.DataCorrupted.Includes(retryResult)) if (!ResultFs.DataCorrupted.Includes(retryResult))
break; break;
@ -170,8 +163,7 @@ public class DeepRetryStorage : IStorage
return ResultFs.NcaDigestInconsistent.Log(); return ResultFs.NcaDigestInconsistent.Log();
} }
_accessSplitter.SetByMove(ref remountStorageAccessSplitter.Ref); _asyncStorage.SetBaseStorage(in remountStorage);
_asyncStorage.SetBaseStorage(in remountStorage, _accessSplitter.Get);
return Result.Success; return Result.Success;
} }

View file

@ -8,7 +8,7 @@ namespace LibHac.FsSrv.Impl;
public interface IRomFileSystemAccessFailureManager : IDisposable 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); Result HandleResolubleAccessFailure(out bool wasDeferred, Result nonDeferredResult);
void IncrementRomFsDeepRetryStartCount(); void IncrementRomFsDeepRetryStartCount();
void IncrementRomFsRemountForDataCorruptionCount(); void IncrementRomFsRemountForDataCorruptionCount();

View 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;
}
}
}

View file

@ -24,7 +24,7 @@ namespace LibHac.FsSrv;
/// </summary> /// </summary>
/// <remarks>FS will have one instance of this class for every connected process. /// <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. /// 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 internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
{ {
private const int AocSemaphoreCount = 128; private const int AocSemaphoreCount = 128;
@ -95,18 +95,18 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
public Result OpenFileSystemWithPatch(ref SharedRef<IFileSystemSf> outFileSystem, ProgramId programId, public Result OpenFileSystemWithPatch(ref SharedRef<IFileSystemSf> outFileSystem, ProgramId programId,
FileSystemProxyType type) FileSystemProxyType type)
{ {
const StorageLayoutType storageFlag = StorageLayoutType.All;
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
// Get the program info for the caller and verify permissions // 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(); if (res.IsFailure()) return res.Miss();
StorageLayoutType storageFlag = _serviceImpl.GetStorageFlag(programInfo.ProgramIdValue);
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
switch (type) switch (type)
{ {
case FileSystemProxyType.Manual: case FileSystemProxyType.Manual:
Accessibility accessibility = Accessibility accessibility =
callerProgramInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountContentManual); programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountContentManual);
if (!accessibility.CanRead) if (!accessibility.CanRead)
return ResultFs.PermissionDenied.Log(); 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 // 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(); if (res.IsFailure()) return res.Miss();
// Try to find the path to the original version of the file system // Try to find the path to the original version of the file system
using var originalPath = new Path(); using var originalPath = new Path();
Result originalResult = _serviceImpl.ResolveApplicationHtmlDocumentPath(out bool isDirectory, Result originalResult = _serviceImpl.ResolveApplicationHtmlDocumentPath(out bool isDirectory,
ref originalPath.Ref(), out ContentAttributes contentAttributes, out ulong originalProgramId, 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 // The file system might have a patch version with no original version, so continue if not found
if (originalResult.IsFailure() && !ResultLr.HtmlDocumentNotFound.Includes(originalResult)) if (originalResult.IsFailure() && !ResultLr.HtmlDocumentNotFound.Includes(originalResult))
@ -334,8 +334,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
_serviceImpl.IncrementRomFsUnrecoverableByGameCardAccessFailedCount(); _serviceImpl.IncrementRomFsUnrecoverableByGameCardAccessFailedCount();
} }
private Result OpenDataStorageCore(ref SharedRef<IStorage> outStorage, private Result OpenDataStorageCore(ref SharedRef<IStorage> outStorage, ref Hash outNcaDigest, ulong id,
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, ref Hash outNcaDigest, ulong id,
StorageId storageId) StorageId storageId)
{ {
using var programPath = new Path(); using var programPath = new Path();
@ -347,7 +346,6 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
out ContentAttributes patchContentAttributes, id); out ContentAttributes patchContentAttributes, id);
using var storage = new SharedRef<IStorage>(); using var storage = new SharedRef<IStorage>();
using var storageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>();
if (ResultLr.ProgramNotFound.Includes(patchResult)) if (ResultLr.ProgramNotFound.Includes(patchResult))
{ {
@ -360,8 +358,8 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
if (originalResult.IsFailure()) if (originalResult.IsFailure())
return originalResult.Miss(); return originalResult.Miss();
Result res = _serviceImpl.OpenDataStorage(ref storage.Ref, ref storageAccessSplitter.Ref, Result res = _serviceImpl.OpenDataStorage(ref storage.Ref, ref outNcaDigest, in programPath,
ref outNcaDigest, in programPath, contentAttributes, FileSystemProxyType.Rom, id); contentAttributes, FileSystemProxyType.Rom, id);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
} }
else else
@ -373,14 +371,12 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
? ref programPath ? ref programPath
: ref PathExtensions.GetNullRef(); : ref PathExtensions.GetNullRef();
Result res = _serviceImpl.OpenStorageWithPatch(ref storage.Ref, ref storageAccessSplitter.Ref, Result res = _serviceImpl.OpenStorageWithPatch(ref storage.Ref, ref outNcaDigest, in originalNcaPath,
ref outNcaDigest, in originalNcaPath, contentAttributes, in patchPath, patchContentAttributes, contentAttributes, in patchPath, patchContentAttributes, FileSystemProxyType.Rom, originalProgramId, id);
FileSystemProxyType.Rom, originalProgramId, id);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
} }
outStorage.SetByMove(ref storage.Ref); outStorage.SetByMove(ref storage.Ref);
outStorageAccessSplitter.SetByMove(ref storageAccessSplitter.Ref);
return Result.Success; return Result.Success;
} }
@ -401,8 +397,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
StorageId storageId = programInfo.StorageId; StorageId storageId = programInfo.StorageId;
using var storage = new SharedRef<IStorage>(); using var storage = new SharedRef<IStorage>();
using var storageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>(); res = OpenDataStorageCore(ref storage.Ref, ref digest, programInfo.ProgramIdValue, storageId);
res = OpenDataStorageCore(ref storage.Ref, ref storageAccessSplitter.Ref, ref digest, programInfo.ProgramIdValue, storageId);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
using var romDivisionSizeUnitCountSemaphore = new UniqueRef<IUniqueLock>(); using var romDivisionSizeUnitCountSemaphore = new UniqueRef<IUniqueLock>();
@ -412,9 +407,9 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
using SharedRef<IRomFileSystemAccessFailureManager> accessFailureManager = GetSharedAccessFailureManagerFromThis(); using SharedRef<IRomFileSystemAccessFailureManager> accessFailureManager = GetSharedAccessFailureManagerFromThis();
using var retryStorage = new SharedRef<IStorage>(new DeepRetryStorage(in storage, in storageAccessSplitter, using var retryStorage = new SharedRef<IStorage>(new DeepRetryStorage(in storage, in accessFailureManager,
in accessFailureManager, ref romDivisionSizeUnitCountSemaphore.Ref, in digest, programInfo.ProgramIdValue, ref romDivisionSizeUnitCountSemaphore.Ref, in digest, programInfo.ProgramIdValue, storageId,
storageId, _serviceImpl.FsServer)); _serviceImpl.FsServer));
using var typeSetStorage = new SharedRef<IStorage>(new StorageLayoutTypeSetStorage(in retryStorage, storageFlag)); using var typeSetStorage = new SharedRef<IStorage>(new StorageLayoutTypeSetStorage(in retryStorage, storageFlag));
using var storageAdapter = new SharedRef<IStorageSf>(new StorageInterfaceAdapter(in typeSetStorage)); using var storageAdapter = new SharedRef<IStorageSf>(new StorageInterfaceAdapter(in typeSetStorage));
@ -452,9 +447,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
res = ncaPath.Normalize(flags); res = ncaPath.Normalize(flags);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
using var storageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>(); res = _serviceImpl.OpenDataStorage(ref storage.Ref, ref digest, in ncaPath, attributes, type, ulong.MaxValue);
res = _serviceImpl.OpenDataStorage(ref storage.Ref, ref storageAccessSplitter.Ref, ref digest, in ncaPath,
attributes, type, ulong.MaxValue);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
using var romDivisionSizeUnitCountSemaphore = new UniqueRef<IUniqueLock>(); using var romDivisionSizeUnitCountSemaphore = new UniqueRef<IUniqueLock>();
@ -464,9 +457,9 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
using SharedRef<IRomFileSystemAccessFailureManager> accessFailureManager = GetSharedAccessFailureManagerFromThis(); using SharedRef<IRomFileSystemAccessFailureManager> accessFailureManager = GetSharedAccessFailureManagerFromThis();
using var retryStorage = new SharedRef<IStorage>(new DeepRetryStorage(in storage, in storageAccessSplitter, using var retryStorage = new SharedRef<IStorage>(new DeepRetryStorage(in storage, in accessFailureManager,
in accessFailureManager, ref romDivisionSizeUnitCountSemaphore.Ref, in digest, ProgramId.InvalidId.Value, ref romDivisionSizeUnitCountSemaphore.Ref, in digest, ProgramId.InvalidId.Value, StorageId.None,
StorageId.None, _serviceImpl.FsServer)); _serviceImpl.FsServer));
using var typeSetStorage = new SharedRef<IStorage>(new StorageLayoutTypeSetStorage(in retryStorage, storageFlag)); using var typeSetStorage = new SharedRef<IStorage>(new StorageLayoutTypeSetStorage(in retryStorage, storageFlag));
using var storageAdapter = new SharedRef<IStorageSf>(new StorageInterfaceAdapter(in typeSetStorage)); 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) 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); Result res = GetProgramInfoByProgramId(out ProgramInfo programInfo, programId.Value);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
StorageLayoutType storageFlag = _serviceImpl.GetStorageFlag(programId.Value, programInfo.ProgramIdValue);
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
Hash digest = default; Hash digest = default;
using var romMountCountSemaphore = new UniqueRef<IUniqueLock>(); using var romMountCountSemaphore = new UniqueRef<IUniqueLock>();
@ -491,8 +484,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
using var storage = new SharedRef<IStorage>(); using var storage = new SharedRef<IStorage>();
using var storageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>(); res = OpenDataStorageCore(ref storage.Ref, ref digest, programId.Value, programInfo.StorageId);
res = OpenDataStorageCore(ref storage.Ref, ref storageAccessSplitter.Ref, ref digest, programId.Value, programInfo.StorageId);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
res = GetProgramInfo(out ProgramInfo properProgramInfo); res = GetProgramInfo(out ProgramInfo properProgramInfo);
@ -508,9 +500,9 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
using SharedRef<IRomFileSystemAccessFailureManager> accessFailureManager = GetSharedAccessFailureManagerFromThis(); using SharedRef<IRomFileSystemAccessFailureManager> accessFailureManager = GetSharedAccessFailureManagerFromThis();
using var retryStorage = new SharedRef<IStorage>(new DeepRetryStorage(in storage, in storageAccessSplitter, using var retryStorage = new SharedRef<IStorage>(new DeepRetryStorage(in storage, in accessFailureManager,
in accessFailureManager, ref romDivisionSizeUnitCountSemaphore.Ref, in digest, programId.Value, ref romDivisionSizeUnitCountSemaphore.Ref, in digest, programId.Value, programInfo.StorageId,
programInfo.StorageId, _serviceImpl.FsServer)); _serviceImpl.FsServer));
using var typeSetStorage = new SharedRef<IStorage>(new StorageLayoutTypeSetStorage(in retryStorage, storageFlag)); using var typeSetStorage = new SharedRef<IStorage>(new StorageLayoutTypeSetStorage(in retryStorage, storageFlag));
using var storageAdapter = new SharedRef<IStorageSf>(new StorageInterfaceAdapter(in typeSetStorage)); 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, public Result OpenFileSystemWithId(ref SharedRef<IFileSystemSf> outFileSystem, in FspPath path,
ContentAttributes attributes, ulong id, FileSystemProxyType type) ContentAttributes attributes, ulong id, FileSystemProxyType type)
{ {
const StorageLayoutType storageFlag = StorageLayoutType.All;
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
Result res = GetProgramInfo(out ProgramInfo programInfo); Result res = GetProgramInfo(out ProgramInfo programInfo);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
StorageLayoutType storageFlag = _serviceImpl.GetStorageFlag(id, programInfo.ProgramIdValue);
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
AccessControl ac = programInfo.AccessControl; AccessControl ac = programInfo.AccessControl;
switch (type) switch (type)
@ -626,12 +618,14 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
public Result OpenDataStorageByDataId(ref SharedRef<IStorageSf> outStorage, DataId dataId, StorageId storageId) public Result OpenDataStorageByDataId(ref SharedRef<IStorageSf> outStorage, DataId dataId, StorageId storageId)
{ {
Result res; Result res = GetProgramInfo(out ProgramInfo programInfo);
if (res.IsFailure()) return res.Miss();
StorageLayoutType storageFlag = _serviceImpl.GetStorageFlag(dataId.Value);
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
bool isAoc = storageId == StorageId.None; 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 systemDataPath = new Path();
using var systemDataPatchPath = new Path(); using var systemDataPatchPath = new Path();
@ -642,38 +636,33 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
if (isAoc) if (isAoc)
{ {
res = _serviceImpl.ResolveAddOnContentPath(ref systemDataPath.Ref(), out contentAttributes, 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(); if (res.IsFailure()) return res.Miss();
} }
else 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(); if (res.IsFailure()) return res.Miss();
Assert.SdkAssert(systemDataPatchPath.IsEmpty()); Assert.SdkAssert(systemDataPatchPath.IsEmpty());
} }
res = GetProgramInfo(out ProgramInfo programInfo);
if (res.IsFailure()) return res.Miss();
var accessibility = programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountSystemDataPrivate); var accessibility = programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountSystemDataPrivate);
bool canMountSystemDataPrivate = accessibility.CanRead; bool canMountSystemDataPrivate = accessibility.CanRead;
using var storage = new SharedRef<IStorage>(); using var storage = new SharedRef<IStorage>();
using var storageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>();
if (systemDataPatchPath.IsEmpty()) if (systemDataPatchPath.IsEmpty())
{ {
res = _serviceImpl.OpenDataStorage(ref storage.Ref, ref storageAccessSplitter.Ref, res = _serviceImpl.OpenDataStorage(ref storage.Ref, ref Unsafe.NullRef<Hash>(), in systemDataPath,
ref Unsafe.NullRef<Hash>(), in systemDataPath, contentAttributes, FileSystemProxyType.Data, contentAttributes, FileSystemProxyType.Data, targetDataId.Value, canMountSystemDataPrivate);
dataId.Value, canMountSystemDataPrivate);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
} }
else else
{ {
res = _serviceImpl.OpenStorageWithPatch(ref storage.Ref, ref storageAccessSplitter.Ref, res = _serviceImpl.OpenStorageWithPatch(ref storage.Ref, ref Unsafe.NullRef<Hash>(), in systemDataPath,
ref Unsafe.NullRef<Hash>(), in systemDataPath, contentAttributes, in systemDataPatchPath, contentAttributes, in systemDataPatchPath, patchContentAttributes, FileSystemProxyType.Data,
patchContentAttributes, FileSystemProxyType.Data, dataId.Value, dataId.Value, canMountSystemDataPrivate); targetDataId.Value, targetDataId.Value, canMountSystemDataPrivate);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
} }
@ -687,8 +676,8 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
using SharedRef<IRomFileSystemAccessFailureManager> accessFailureManager = GetSharedAccessFailureManagerFromThis(); using SharedRef<IRomFileSystemAccessFailureManager> accessFailureManager = GetSharedAccessFailureManagerFromThis();
using var retryStorage = new SharedRef<IStorage>(new DeepRetryStorage(in storage, in storageAccessSplitter, using var retryStorage = new SharedRef<IStorage>(new DeepRetryStorage(in storage, in accessFailureManager,
in accessFailureManager, ref mountCountSemaphore.Ref, deepRetryEnabled: isAoc, _serviceImpl.FsServer)); ref mountCountSemaphore.Ref, deepRetryEnabled: isAoc, _serviceImpl.FsServer));
using var typeSetStorage = new SharedRef<IStorage>(new StorageLayoutTypeSetStorage(in retryStorage, storageFlag)); using var typeSetStorage = new SharedRef<IStorage>(new StorageLayoutTypeSetStorage(in retryStorage, storageFlag));
using var storageAdapter = new SharedRef<IStorageSf>(new StorageInterfaceAdapter(in typeSetStorage)); 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) public Result OpenDataFileSystemByDataId(ref SharedRef<IFileSystemSf> outFileSystem, DataId dataId, StorageId storageId)
{ {
Result res; Result res;
bool isAoc = storageId == StorageId.None;
DataId targetDataId = _serviceImpl.RedirectDataId(dataId, storageId);
StorageLayoutType storageFlag = _serviceImpl.GetStorageFlag(dataId.Value); StorageLayoutType storageFlag = _serviceImpl.GetStorageFlag(dataId.Value);
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag); using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
bool isAoc = storageId == StorageId.None;
using var dataPath = new Path(); using var dataPath = new Path();
using var dataPatchPathUnused = new Path(); using var dataPatchPathUnused = new Path();
@ -715,12 +705,12 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
if (isAoc) if (isAoc)
{ {
res = _serviceImpl.ResolveAddOnContentPath(ref dataPath.Ref(), out contentAttributes, res = _serviceImpl.ResolveAddOnContentPath(ref dataPath.Ref(), out contentAttributes,
ref dataPatchPathUnused.Ref(), out _, dataId); ref dataPatchPathUnused.Ref(), out _, targetDataId);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
} }
else 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(); if (res.IsFailure()) return res.Miss();
Assert.SdkAssert(dataPatchPathUnused.IsEmpty()); Assert.SdkAssert(dataPatchPathUnused.IsEmpty());
@ -728,7 +718,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
using var fileSystem = new SharedRef<IFileSystem>(); using var fileSystem = new SharedRef<IFileSystem>();
res = _serviceImpl.OpenDataFileSystem(ref fileSystem.Ref, in dataPath, contentAttributes, res = _serviceImpl.OpenDataFileSystem(ref fileSystem.Ref, in dataPath, contentAttributes,
FileSystemProxyType.Data, dataId.Value, isDirectory: true); FileSystemProxyType.Data, targetDataId.Value, isDirectory: true);
if (!res.IsSuccess()) if (!res.IsSuccess())
{ {
@ -766,7 +756,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
// Verify the caller has access to the file system // Verify the caller has access to the file system
if (targetProgramId != programInfo.ProgramId && if (programInfo.ProgramId != targetProgramId &&
!programInfo.AccessControl.HasContentOwnerId(targetProgramId.Value)) !programInfo.AccessControl.HasContentOwnerId(targetProgramId.Value))
{ {
return ResultFs.PermissionDenied.Log(); return ResultFs.PermissionDenied.Log();
@ -790,7 +780,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
res = _serviceImpl.ResolveRomReferenceProgramId(out ProgramId targetProgramId, programInfo.ProgramId, programIndex); res = _serviceImpl.ResolveRomReferenceProgramId(out ProgramId targetProgramId, programInfo.ProgramId, programIndex);
if (res.IsFailure()) return res.Miss(); 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); using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
Hash digest = default; Hash digest = default;
@ -800,8 +790,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
using var storage = new SharedRef<IStorage>(); using var storage = new SharedRef<IStorage>();
using var storageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>(); res = OpenDataStorageCore(ref storage.Ref, ref 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 (res.IsFailure()) return res.Miss();
if (programInfo.ProgramId != targetProgramId && !programInfo.AccessControl.HasContentOwnerId(targetProgramId.Value)) if (programInfo.ProgramId != targetProgramId && !programInfo.AccessControl.HasContentOwnerId(targetProgramId.Value))
@ -814,9 +803,9 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
using SharedRef<IRomFileSystemAccessFailureManager> accessFailureManager = GetSharedAccessFailureManagerFromThis(); using SharedRef<IRomFileSystemAccessFailureManager> accessFailureManager = GetSharedAccessFailureManagerFromThis();
using var retryStorage = new SharedRef<IStorage>(new DeepRetryStorage(in storage, in storageAccessSplitter, using var retryStorage = new SharedRef<IStorage>(new DeepRetryStorage(in storage, in accessFailureManager,
in accessFailureManager, ref romDivisionSizeUnitCountSemaphore.Ref, in digest, targetProgramId.Value, ref romDivisionSizeUnitCountSemaphore.Ref, in digest, targetProgramId.Value, programInfo.StorageId,
programInfo.StorageId, _serviceImpl.FsServer)); _serviceImpl.FsServer));
using var typeSetStorage = new SharedRef<IStorage>(new StorageLayoutTypeSetStorage(in retryStorage, storageFlag)); using var typeSetStorage = new SharedRef<IStorage>(new StorageLayoutTypeSetStorage(in retryStorage, storageFlag));
using var storageAdapter = new SharedRef<IStorageSf>(new StorageInterfaceAdapter(in typeSetStorage)); 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, public Result OpenContentStorageFileSystem(ref SharedRef<IFileSystemSf> outFileSystem,
ContentStorageId contentStorageId) ContentStorageId contentStorageId)
{ {
StorageLayoutType storageFlag = contentStorageId == ContentStorageId.System ? StorageLayoutType.Bis : StorageLayoutType.All;
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
Result res = GetProgramInfo(out ProgramInfo programInfo); Result res = GetProgramInfo(out ProgramInfo programInfo);
if (res.IsFailure()) return res.Miss(); 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); Accessibility accessibility = programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountContentStorage);
if (!accessibility.CanRead || !accessibility.CanWrite) if (!accessibility.CanRead || !accessibility.CanWrite)
@ -1088,17 +1077,6 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
return Result.Success; 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) public Result OpenHostFileSystem(ref SharedRef<IFileSystemSf> outFileSystem, ref readonly FspPath path)
{ {
Result res = GetProgramInfo(out ProgramInfo programInfo); Result res = GetProgramInfo(out ProgramInfo programInfo);
@ -1163,9 +1141,8 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
} }
Result IRomFileSystemAccessFailureManager.OpenDataStorageCore(ref SharedRef<IStorage> outStorage, Result IRomFileSystemAccessFailureManager.OpenDataStorageCore(ref SharedRef<IStorage> outStorage,
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, ref Hash outNcaDigest, ulong id, ref Hash outNcaDigest, ulong id, StorageId storageId)
StorageId storageId)
{ {
return OpenDataStorageCore(ref outStorage, ref outStorageAccessSplitter, ref outNcaDigest, id, storageId).Ret(); return OpenDataStorageCore(ref outStorage, ref outNcaDigest, id, storageId).Ret();
} }
} }

View file

@ -166,14 +166,13 @@ file static class Anonymous
/// <summary> /// <summary>
/// Handles locating and opening NCA content files. /// Handles locating and opening NCA content files.
/// </summary> /// </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 public class NcaFileSystemServiceImpl : IDisposable
{ {
private readonly Configuration _config; private readonly Configuration _config;
private readonly UpdatePartitionPath _updatePartitionPath; private readonly UpdatePartitionPath _updatePartitionPath;
private readonly ExternalKeyManager _externalKeyManager; private readonly ExternalKeyManager _externalKeyManager;
private readonly SystemDataUpdateEventManager _systemDataUpdateEventManager; private readonly SystemDataUpdateEventManager _systemDataUpdateEventManager;
private EncryptionSeed _encryptionSeed;
private uint _romFsDeepRetryStartCount; private uint _romFsDeepRetryStartCount;
private uint _romFsRemountForDataCorruptionCount; private uint _romFsRemountForDataCorruptionCount;
private uint _romfsUnrecoverableDataCorruptionByRemountCount; private uint _romfsUnrecoverableDataCorruptionByRemountCount;
@ -193,11 +192,13 @@ public class NcaFileSystemServiceImpl : IDisposable
public IEncryptedFileSystemCreator EncryptedFsCreator; public IEncryptedFileSystemCreator EncryptedFsCreator;
public INspRootFileSystemCreator NspRootFileSystemCreator; public INspRootFileSystemCreator NspRootFileSystemCreator;
public LocationResolverSet LocationResolverSet; public LocationResolverSet LocationResolverSet;
public Func<DataId, StorageId, DataId> RedirectDataId;
public ProgramRegistryServiceImpl ProgramRegistryService; public ProgramRegistryServiceImpl ProgramRegistryService;
public AccessFailureManagementServiceImpl AccessFailureManagementService; public AccessFailureManagementServiceImpl AccessFailureManagementService;
public InternalProgramIdRangeForSpeedEmulation SpeedEmulationRange; public InternalProgramIdRangeForSpeedEmulation SpeedEmulationRange;
public long AddOnContentDivisionSize; public long AddOnContentDivisionSize;
public long RomDivisionSize; public long RomDivisionSize;
public InternalProgramIdRangeForStorageAccessSpeedControl StorageAccessSpeedControlRange;
// LibHac additions // LibHac additions
public FileSystemServer FsServer; public FileSystemServer FsServer;
@ -244,6 +245,8 @@ public class NcaFileSystemServiceImpl : IDisposable
_romFsUnrecoverableByGameCardAccessFailedCount = 0; _romFsUnrecoverableByGameCardAccessFailedCount = 0;
_romfsCountMutex = new SdkMutexType(); _romfsCountMutex = new SdkMutexType();
StorageAccessSpeedControl.SetTargetProgramIdRange(_config.FsServer, _config.StorageAccessSpeedControlRange);
} }
public void Dispose() public void Dispose()
@ -386,9 +389,8 @@ public class NcaFileSystemServiceImpl : IDisposable
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
using var storage = new SharedRef<IStorage>(); using var storage = new SharedRef<IStorage>();
using var storageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>(); res = OpenStorageByContentType(ref storage.Ref, in ncaReader, out NcaFsHeader.FsType fsType, type,
res = OpenStorageByContentType(ref storage.Ref, ref storageAccessSplitter.Ref, in ncaReader, mountInfo.IsGameCard(), canMountSystemDataPrivate);
out NcaFsHeader.FsType fsType, type, mountInfo.IsGameCard(), canMountSystemDataPrivate);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
switch (fsType) switch (fsType)
@ -475,9 +477,8 @@ public class NcaFileSystemServiceImpl : IDisposable
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
using var storage = new SharedRef<IStorage>(); using var storage = new SharedRef<IStorage>();
using var storageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>(); res = OpenStorageByContentType(ref storage.Ref, in ncaReader, out NcaFsHeader.FsType fsType, type,
res = OpenStorageByContentType(ref storage.Ref, ref storageAccessSplitter.Ref, in ncaReader, mountInfo.IsGameCard(), canMountSystemDataPrivate: false);
out NcaFsHeader.FsType fsType, type, mountInfo.IsGameCard(), canMountSystemDataPrivate: false);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
if (fsType != NcaFsHeader.FsType.RomFs) if (fsType != NcaFsHeader.FsType.RomFs)
@ -486,18 +487,15 @@ public class NcaFileSystemServiceImpl : IDisposable
return _config.RomFsCreator.Create(ref outFileSystem, in storage).Ret(); return _config.RomFsCreator.Create(ref outFileSystem, in storage).Ret();
} }
public Result OpenDataStorage(ref SharedRef<IStorage> outStorage, public Result OpenDataStorage(ref SharedRef<IStorage> outStorage, ref Hash outNcaDigest, ref readonly Path path,
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, ref Hash outNcaDigest, ContentAttributes attributes, FileSystemProxyType type, ulong id)
ref readonly Path path, ContentAttributes attributes, FileSystemProxyType type, ulong id)
{ {
return OpenDataStorage(ref outStorage, ref outStorageAccessSplitter, ref outNcaDigest, in path, attributes, return OpenDataStorage(ref outStorage, ref outNcaDigest, in path, attributes, type, id,
type, id, canMountSystemDataPrivate: false).Ret(); canMountSystemDataPrivate: false).Ret();
} }
public Result OpenDataStorage(ref SharedRef<IStorage> outStorage, public Result OpenDataStorage(ref SharedRef<IStorage> outStorage, ref Hash outNcaDigest, ref readonly Path path,
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, ref Hash outNcaDigest, ContentAttributes attributes, FileSystemProxyType type, ulong id, bool canMountSystemDataPrivate)
ref readonly Path path, ContentAttributes attributes, FileSystemProxyType type, ulong id,
bool canMountSystemDataPrivate)
{ {
using var ncaReader = new SharedRef<NcaReader>(); using var ncaReader = new SharedRef<NcaReader>();
Result res = ParseNca(ref ncaReader.Ref, out bool isGameCard, path.GetString(), attributes, id); 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); GenerateNcaDigest(out outNcaDigest, ncaReader.Get, null);
} }
res = OpenStorageByContentType(ref outStorage, ref outStorageAccessSplitter, in ncaReader, res = OpenStorageByContentType(ref outStorage, in ncaReader, out NcaFsHeader.FsType fsType, type, isGameCard,
out NcaFsHeader.FsType fsType, type, isGameCard, canMountSystemDataPrivate); canMountSystemDataPrivate);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
if (fsType != NcaFsHeader.FsType.RomFs) if (fsType != NcaFsHeader.FsType.RomFs)
@ -518,18 +516,15 @@ public class NcaFileSystemServiceImpl : IDisposable
return Result.Success; return Result.Success;
} }
public Result OpenStorageWithPatch(ref SharedRef<IStorage> outStorage, public Result OpenStorageWithPatch(ref SharedRef<IStorage> outStorage, ref Hash outNcaDigest,
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, ref Hash outNcaDigest,
ref readonly Path originalNcaPath, ContentAttributes originalAttributes, ref readonly Path currentNcaPath, ref readonly Path originalNcaPath, ContentAttributes originalAttributes, ref readonly Path currentNcaPath,
ContentAttributes currentAttributes, FileSystemProxyType type, ulong originalId, ulong currentId) ContentAttributes currentAttributes, FileSystemProxyType type, ulong originalId, ulong currentId)
{ {
return OpenStorageWithPatch(ref outStorage, ref outStorageAccessSplitter, ref outNcaDigest, return OpenStorageWithPatch(ref outStorage, ref outNcaDigest, in originalNcaPath, originalAttributes,
in originalNcaPath, originalAttributes, in currentNcaPath, currentAttributes, type, originalId, currentId, in currentNcaPath, currentAttributes, type, originalId, currentId, false).Ret();
false).Ret();
} }
public Result OpenStorageWithPatch(ref SharedRef<IStorage> outStorage, public Result OpenStorageWithPatch(ref SharedRef<IStorage> outStorage, ref Hash outNcaDigest,
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, ref Hash outNcaDigest,
ref readonly Path originalNcaPath, ContentAttributes originalAttributes, ref readonly Path currentNcaPath, ref readonly Path originalNcaPath, ContentAttributes originalAttributes, ref readonly Path currentNcaPath,
ContentAttributes currentAttributes, FileSystemProxyType type, ulong originalId, ulong currentId, ContentAttributes currentAttributes, FileSystemProxyType type, ulong originalId, ulong currentId,
bool canMountSystemDataPrivate) bool canMountSystemDataPrivate)
@ -567,8 +562,8 @@ public class NcaFileSystemServiceImpl : IDisposable
GenerateNcaDigest(out outNcaDigest, originalNcaReader.Get, currentNcaReader.Get); GenerateNcaDigest(out outNcaDigest, originalNcaReader.Get, currentNcaReader.Get);
} }
res = OpenStorageWithPatchByContentType(ref outStorage, ref outStorageAccessSplitter, in originalNcaReader, res = OpenStorageWithPatchByContentType(ref outStorage, in originalNcaReader, in currentNcaReader,
in currentNcaReader, out NcaFsHeader.FsType fsType, type, canMountSystemDataPrivate); out NcaFsHeader.FsType fsType, type, canMountSystemDataPrivate);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
if (fsType != NcaFsHeader.FsType.RomFs) if (fsType != NcaFsHeader.FsType.RomFs)
@ -582,11 +577,10 @@ public class NcaFileSystemServiceImpl : IDisposable
FileSystemProxyType type, ulong originalId, ulong currentId) FileSystemProxyType type, ulong originalId, ulong currentId)
{ {
using var storage = new SharedRef<IStorage>(); using var storage = new SharedRef<IStorage>();
using var storageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>();
using var fileSystem = new SharedRef<IFileSystem>(); using var fileSystem = new SharedRef<IFileSystem>();
Result res = OpenStorageWithPatch(ref storage.Ref, ref storageAccessSplitter.Ref, ref Unsafe.NullRef<Hash>(), Result res = OpenStorageWithPatch(ref storage.Ref, ref Unsafe.NullRef<Hash>(), in originalNcaPath,
in originalNcaPath, originalAttributes, in currentNcaPath, currentAttributes, type, originalId, currentId, originalAttributes, in currentNcaPath, currentAttributes, type, originalId, currentId,
canMountSystemDataPrivate: false); canMountSystemDataPrivate: false);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
@ -658,7 +652,7 @@ public class NcaFileSystemServiceImpl : IDisposable
{ {
using SharedRef<IFileSystem> tempFileSystem = SharedRef<IFileSystem>.CreateMove(ref subDirFs.Ref); using SharedRef<IFileSystem> tempFileSystem = SharedRef<IFileSystem>.CreateMove(ref subDirFs.Ref);
res = _config.EncryptedFsCreator.Create(ref subDirFs.Ref, in tempFileSystem, res = _config.EncryptedFsCreator.Create(ref subDirFs.Ref, in tempFileSystem,
IEncryptedFileSystemCreator.KeyId.Content, in _encryptionSeed); IEncryptedFileSystemCreator.KeyId.Content);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
} }
@ -933,7 +927,7 @@ public class NcaFileSystemServiceImpl : IDisposable
res = OpenHostFileSystem(ref outFileSystem, in pathRoot, openCaseSensitive: true); res = OpenHostFileSystem(ref outFileSystem, in pathRoot, openCaseSensitive: true);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
break; break;
case MountInfo.FileSystemType.LocalFs: case MountInfo.FileSystemType.LocalFs:
res = _config.LocalFsCreator.Create(ref outFileSystem, in pathRoot, openCaseSensitive: true); res = _config.LocalFsCreator.Create(ref outFileSystem, in pathRoot, openCaseSensitive: true);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
@ -1148,7 +1142,6 @@ public class NcaFileSystemServiceImpl : IDisposable
} }
public Result OpenStorageByContentType(ref SharedRef<IStorage> outNcaStorage, public Result OpenStorageByContentType(ref SharedRef<IStorage> outNcaStorage,
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter,
ref readonly SharedRef<NcaReader> ncaReader, out NcaFsHeader.FsType outFsType, FileSystemProxyType fsProxyType, ref readonly SharedRef<NcaReader> ncaReader, out NcaFsHeader.FsType outFsType, FileSystemProxyType fsProxyType,
bool isGameCard, bool canMountSystemDataPrivate) bool isGameCard, bool canMountSystemDataPrivate)
{ {
@ -1221,8 +1214,7 @@ public class NcaFileSystemServiceImpl : IDisposable
var ncaFsHeaderReader = new NcaFsHeaderReader(); var ncaFsHeaderReader = new NcaFsHeaderReader();
res = _config.StorageOnNcaCreator.Create(ref outNcaStorage, ref outStorageAccessSplitter, ref ncaFsHeaderReader, res = _config.StorageOnNcaCreator.Create(ref outNcaStorage, ref ncaFsHeaderReader, in ncaReader, partitionIndex);
in ncaReader, partitionIndex);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
outFsType = ncaFsHeaderReader.GetFsType(); outFsType = ncaFsHeaderReader.GetFsType();
@ -1230,7 +1222,6 @@ public class NcaFileSystemServiceImpl : IDisposable
} }
public Result OpenStorageWithPatchByContentType(ref SharedRef<IStorage> outNcaStorage, public Result OpenStorageWithPatchByContentType(ref SharedRef<IStorage> outNcaStorage,
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter,
ref readonly SharedRef<NcaReader> originalNcaReader, ref readonly SharedRef<NcaReader> currentNcaReader, ref readonly SharedRef<NcaReader> originalNcaReader, ref readonly SharedRef<NcaReader> currentNcaReader,
out NcaFsHeader.FsType outFsType, FileSystemProxyType fsProxyType, bool canMountSystemDataPrivate) out NcaFsHeader.FsType outFsType, FileSystemProxyType fsProxyType, bool canMountSystemDataPrivate)
{ {
@ -1279,21 +1270,14 @@ public class NcaFileSystemServiceImpl : IDisposable
var ncaFsHeaderReader = new NcaFsHeaderReader(); var ncaFsHeaderReader = new NcaFsHeaderReader();
res = _config.StorageOnNcaCreator.CreateWithPatch(ref outNcaStorage, ref outStorageAccessSplitter, res = _config.StorageOnNcaCreator.CreateWithPatch(ref outNcaStorage, ref ncaFsHeaderReader,
ref ncaFsHeaderReader, in originalNcaReader, in currentNcaReader, partitionIndex); in originalNcaReader, in currentNcaReader, partitionIndex);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
outFsType = ncaFsHeaderReader.GetFsType(); outFsType = ncaFsHeaderReader.GetFsType();
return Result.Success; return Result.Success;
} }
public Result SetSdCardEncryptionSeed(in EncryptionSeed encryptionSeed)
{
_encryptionSeed = encryptionSeed;
return Result.Success;
}
public Result ResolveRomReferenceProgramId(out ProgramId outTargetProgramId, ProgramId programId, public Result ResolveRomReferenceProgramId(out ProgramId outTargetProgramId, ProgramId programId,
byte programIndex) byte programIndex)
{ {
@ -1393,6 +1377,11 @@ public class NcaFileSystemServiceImpl : IDisposable
return Result.Success; 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, public Result ResolveDataPath(ref Path outPath, out ContentAttributes outContentAttributes, DataId dataId,
StorageId storageId) StorageId storageId)
{ {
@ -1418,17 +1407,38 @@ public class NcaFileSystemServiceImpl : IDisposable
return _config.LocationResolverSet.ResolveRegisteredHtmlDocumentPath(ref outPath, out outContentAttributes, programId).Ret(); 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); 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 && if (programIdWithoutPlatformId >= _config.SpeedEmulationRange.ProgramIdWithoutPlatformIdMin &&
programIdWithoutPlatformId <= _config.SpeedEmulationRange.ProgramIdWithoutPlatformIdMax) programIdWithoutPlatformId <= _config.SpeedEmulationRange.ProgramIdWithoutPlatformIdMax)
return StorageLayoutType.Bis; return StorageLayoutType.Bis;
else 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, public Result HandleResolubleAccessFailure(out bool wasDeferred, Result nonDeferredResult,

View file

@ -1192,7 +1192,7 @@ public class SaveDataFileSystemServiceImpl : IDisposable
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
res = _config.EncryptedFsCreator.Create(ref outFileSystem, in baseFileSystem, res = _config.EncryptedFsCreator.Create(ref outFileSystem, in baseFileSystem,
IEncryptedFileSystemCreator.KeyId.Save, in _encryptionSeed); IEncryptedFileSystemCreator.KeyId.Save);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
break; break;

View file

@ -7,13 +7,36 @@ using LibHac.Util;
namespace LibHac.FsSystem; 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> /// <summary>
/// <para>Splits read and write requests on an <see cref="IFile"/> or <see cref="IStorage"/> into smaller chunks /// <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> /// 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 /// <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> /// chunks that start and end on the boundaries of the compressed blocks.</para>
/// </summary> /// </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 public interface IAsynchronousAccessSplitter : IDisposable
{ {
private static readonly DefaultAsynchronousAccessSplitter DefaultAccessSplitter = new(); private static readonly DefaultAsynchronousAccessSplitter DefaultAccessSplitter = new();
@ -40,7 +63,7 @@ public interface IAsynchronousAccessSplitter : IDisposable
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
Assert.SdkNotEqual(startOffset, offsetAppropriate); Assert.SdkNotEqual(startOffset, offsetAppropriate);
nextOffset = Math.Min(startOffset, offsetAppropriate); nextOffset = Math.Min(offsetAppropriate, endOffset);
return Result.Success; return Result.Success;
} }
@ -66,47 +89,18 @@ public interface IAsynchronousAccessSplitter : IDisposable
Result QueryAppropriateOffset(out long offsetAppropriate, long startOffset, long accessSize, long alignmentSize); 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 public class AsynchronousAccessStorage : IStorage
{ {
private SharedRef<IStorage> _baseStorage; private SharedRef<IStorage> _baseStorage;
// private ThreadPool _threadPool; // private ThreadPool _threadPool;
private IAsynchronousAccessSplitter _baseStorageAccessSplitter; private bool _baseStorageHasAccessSplitter;
public AsynchronousAccessStorage(ref readonly SharedRef<IStorage> baseStorage) : this(in baseStorage, public AsynchronousAccessStorage(ref readonly SharedRef<IStorage> baseStorage)
IAsynchronousAccessSplitter.GetDefaultAsynchronousAccessSplitter())
{
}
public AsynchronousAccessStorage(ref readonly SharedRef<IStorage> baseStorage, IAsynchronousAccessSplitter baseStorageAccessSplitter)
{ {
_baseStorage = SharedRef<IStorage>.CreateCopy(in baseStorage); _baseStorage = SharedRef<IStorage>.CreateCopy(in baseStorage);
_baseStorageAccessSplitter = baseStorageAccessSplitter; _baseStorageHasAccessSplitter = true;
Assert.SdkRequiresNotNull(in _baseStorage); Assert.SdkRequiresNotNull(in _baseStorage);
Assert.SdkRequiresNotNull(_baseStorageAccessSplitter);
} }
public override void Dispose() public override void Dispose()
@ -115,10 +109,9 @@ public class AsynchronousAccessStorage : IStorage
base.Dispose(); base.Dispose();
} }
public void SetBaseStorage(ref readonly SharedRef<IStorage> baseStorage, IAsynchronousAccessSplitter baseStorageAccessSplitter) public void SetBaseStorage(ref readonly SharedRef<IStorage> baseStorage)
{ {
_baseStorage.SetByCopy(in baseStorage); _baseStorage.SetByCopy(in baseStorage);
_baseStorageAccessSplitter = baseStorageAccessSplitter;
} }
// Todo: Implement // Todo: Implement

View file

@ -188,8 +188,7 @@ public class BlockCacheBufferedStorage : IStorage
throw new NotImplementedException(); throw new NotImplementedException();
} }
private Result GetAssociateBuffer(out Buffer outRange, out CacheEntry outEntry, long offset, int idealSize, private Result GetAssociateBuffer(out Buffer outRange, out CacheEntry outEntry, long offset, bool isAllocateForWrite)
bool isAllocateForWrite)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
@ -241,8 +240,9 @@ public class BlockCacheBufferedStorage : IStorage
throw new NotImplementedException(); throw new NotImplementedException();
} }
private Result BulkRead(long offset, Span<byte> buffer, ref Buffer memoryRangeHead, ref Buffer memoryRangeTail, private Result BulkRead(out bool outIsHeadCacheStored, out bool outIsTailCacheStored, long offset,
ref CacheEntry entryHead, ref CacheEntry entryTail, bool isHeadCacheNeeded, bool isTailCacheNeeded) Span<byte> buffer, ref Buffer memoryRangeHead, ref Buffer memoryRangeTail, ref CacheEntry entryHead,
ref CacheEntry entryTail)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

View file

@ -18,7 +18,8 @@ public class CompressedStorage : IStorage, IAsynchronousAccessSplitter
private long _continuousReadingSizeMax; private long _continuousReadingSizeMax;
private readonly BucketTree _bucketTree; private readonly BucketTree _bucketTree;
private ValueSubStorage _dataStorage; private ValueSubStorage _dataStorage;
private GetDecompressorFunction _getDecompressorFunction; private IDecompressorFactory _decompressorFactory;
private ulong _alignment;
public CompressedStorageCore() public CompressedStorageCore()
{ {
@ -67,12 +68,12 @@ public class CompressedStorage : IStorage, IAsynchronousAccessSplitter
public Result Initialize(MemoryResource allocatorForBucketTree, ref readonly ValueSubStorage dataStorage, public Result Initialize(MemoryResource allocatorForBucketTree, ref readonly ValueSubStorage dataStorage,
ref readonly ValueSubStorage nodeStorage, ref readonly ValueSubStorage entryStorage, ref readonly ValueSubStorage nodeStorage, ref readonly ValueSubStorage entryStorage,
int bucketTreeEntryCount, long blockSizeMax, long continuousReadingSizeMax, int bucketTreeEntryCount, long blockSizeMax, long continuousReadingSizeMax,
GetDecompressorFunction getDecompressorFunc) IDecompressorFactory decompressorFactory, ulong alignment)
{ {
Assert.SdkRequiresNotNull(allocatorForBucketTree); Assert.SdkRequiresNotNull(allocatorForBucketTree);
Assert.SdkRequiresLess(0, blockSizeMax); Assert.SdkRequiresLess(0, blockSizeMax);
Assert.SdkRequiresLessEqual(blockSizeMax, continuousReadingSizeMax); Assert.SdkRequiresLessEqual(blockSizeMax, continuousReadingSizeMax);
Assert.SdkRequiresNotNull(getDecompressorFunc); Assert.SdkRequiresNotNull(decompressorFactory);
Result res = _bucketTree.Initialize(allocatorForBucketTree, in nodeStorage, in entryStorage, NodeSize, Result res = _bucketTree.Initialize(allocatorForBucketTree, in nodeStorage, in entryStorage, NodeSize,
Unsafe.SizeOf<Entry>(), bucketTreeEntryCount); Unsafe.SizeOf<Entry>(), bucketTreeEntryCount);
@ -81,7 +82,8 @@ public class CompressedStorage : IStorage, IAsynchronousAccessSplitter
_blockSizeMax = blockSizeMax; _blockSizeMax = blockSizeMax;
_continuousReadingSizeMax = continuousReadingSizeMax; _continuousReadingSizeMax = continuousReadingSizeMax;
_dataStorage.Set(in dataStorage); _dataStorage.Set(in dataStorage);
_getDecompressorFunction = getDecompressorFunc; _decompressorFactory = decompressorFactory;
_alignment = alignment;
return Result.Success; return Result.Success;
} }
@ -120,14 +122,6 @@ public class CompressedStorage : IStorage, IAsynchronousAccessSplitter
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
private DecompressorFunction GetDecompressor(CompressionType type)
{
if (CompressionTypeUtility.IsUnknownType(type))
return null;
return _getDecompressorFunction(type);
}
} }
public class CacheManager : IDisposable public class CacheManager : IDisposable
@ -299,11 +293,11 @@ public class CompressedStorage : IStorage, IAsynchronousAccessSplitter
public Result Initialize(MemoryResource allocatorForBucketTree, IBufferManager allocatorForCacheManager, public Result Initialize(MemoryResource allocatorForBucketTree, IBufferManager allocatorForCacheManager,
ref readonly ValueSubStorage dataStorage, ref readonly ValueSubStorage nodeStorage, ref readonly ValueSubStorage dataStorage, ref readonly ValueSubStorage nodeStorage,
ref readonly ValueSubStorage entryStorage, int bucketTreeEntryCount, long blockSizeMax, ref readonly ValueSubStorage entryStorage, int bucketTreeEntryCount, long blockSizeMax,
long continuousReadingSizeMax, GetDecompressorFunction getDecompressorFunc, long cacheSize0, long cacheSize1, long continuousReadingSizeMax, IDecompressorFactory decompressorFactory, ulong alignment, long cacheSize0,
int maxCacheEntries) long cacheSize1, int maxCacheEntries)
{ {
Result res = _core.Initialize(allocatorForBucketTree, in dataStorage, in nodeStorage, in entryStorage, 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(); if (res.IsFailure()) return res.Miss();
res = _core.GetSize(out long size); res = _core.GetSize(out long size);

View file

@ -1,35 +1,29 @@
using System; using System;
using LibHac.Common;
namespace LibHac.FsSystem; namespace LibHac.FsSystem;
public enum CompressionType : byte public enum CompressionType : byte
{ {
None = 0, None = 0,
Zeroed = 1, FillZero = 1,
Lz4 = 3, Lz4 = 3,
Unknown = 4 Unknown = 4
} }
public ref struct DecompressionTask public delegate Result CompressionGetData(Span<byte> buffer);
{ public delegate Result CompressionProcessData(Span<byte> workBuffer, int sizeBufferRequired, CompressionGetData getDataFunc);
public Span<byte> Destination;
public ReadOnlySpan<byte> Source;
}
public delegate Result DecompressorFunction(DecompressionTask task);
public delegate DecompressorFunction GetDecompressorFunction(CompressionType compressionType);
public static class CompressionTypeUtility public static class CompressionTypeUtility
{ {
public static bool IsBlockAlignmentRequired(CompressionType type) 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) public static bool IsDataStorageAccessRequired(CompressionType type)
{ {
return type != CompressionType.Zeroed; return type != CompressionType.FillZero;
} }
public static bool IsRandomAccessible(CompressionType type) public static bool IsRandomAccessible(CompressionType type)
@ -41,4 +35,15 @@ public static class CompressionTypeUtility
{ {
return type >= CompressionType.Unknown; 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();
} }

View file

@ -31,7 +31,7 @@ public struct NcaCryptoConfiguration
public struct NcaCompressionConfiguration public struct NcaCompressionConfiguration
{ {
public GetDecompressorFunction GetDecompressorFunc; public IDecompressorFactory DecompressorFactory;
} }
public static class NcaKeyFunctions public static class NcaKeyFunctions
@ -203,9 +203,7 @@ public class NcaFileSystemDriver : IDisposable
throw new NotImplementedException(); throw new NotImplementedException();
} }
public Result OpenStorage(ref SharedRef<IStorage> outStorage, public Result OpenStorage(ref SharedRef<IStorage> outStorage, ref NcaFsHeaderReader outHeaderReader, int fsIndex)
ref SharedRef<IAsynchronousAccessSplitter> outStorageAccessSplitter, ref NcaFsHeaderReader outHeaderReader,
int fsIndex)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
@ -323,7 +321,7 @@ public class NcaFileSystemDriver : IDisposable
private Result CreateIntegrityVerificationStorage(ref SharedRef<IStorage> outStorage, private Result CreateIntegrityVerificationStorage(ref SharedRef<IStorage> outStorage,
ref readonly SharedRef<IStorage> baseStorage, in NcaFsHeader.HashData.IntegrityMetaInfo metaInfo, ref readonly SharedRef<IStorage> baseStorage, in NcaFsHeader.HashData.IntegrityMetaInfo metaInfo,
IHash256GeneratorFactory hashGeneratorFactory) bool alwaysCreateCacheEntries, IHash256GeneratorFactory hashGeneratorFactory)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
@ -338,22 +336,22 @@ public class NcaFileSystemDriver : IDisposable
private Result CreateIntegrityVerificationStorageImpl(ref SharedRef<IStorage> outStorage, private Result CreateIntegrityVerificationStorageImpl(ref SharedRef<IStorage> outStorage,
ref readonly SharedRef<IStorage> baseStorage, in NcaFsHeader.HashData.IntegrityMetaInfo metaInfo, ref readonly SharedRef<IStorage> baseStorage, in NcaFsHeader.HashData.IntegrityMetaInfo metaInfo,
long layerInfoOffset, int maxDataCacheEntries, int maxHashCacheEntries, sbyte bufferLevel, long layerInfoOffset, int maxDataCacheEntries, int maxHashCacheEntries, sbyte bufferLevel,
IHash256GeneratorFactory hashGeneratorFactory) bool alwaysCreateCacheEntries, IHash256GeneratorFactory hashGeneratorFactory)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public static Result CreateCompressedStorage(ref SharedRef<IStorage> outStorage, public static Result CreateCompressedStorage(ref SharedRef<IStorage> outStorage,
ref SharedRef<CompressedStorage> outCompressedStorage, ref SharedRef<IStorage> outMetaStorage, 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,
GetDecompressorFunction getDecompressor, MemoryResource allocator, IBufferManager bufferManager) IDecompressorFactory decompressorFactory, MemoryResource allocator, IBufferManager bufferManager)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
private Result CreateCompressedStorage(ref SharedRef<IStorage> outStorage, private Result CreateCompressedStorage(ref SharedRef<IStorage> outStorage,
ref SharedRef<CompressedStorage> outCompressedStorage, ref SharedRef<IStorage> outMetaStorage, 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(); throw new NotImplementedException();
} }

View file

@ -22,7 +22,7 @@ public class NcaReader : IDisposable
private SharedRef<IStorage> _bodyStorage; private SharedRef<IStorage> _bodyStorage;
private SharedRef<IStorage> _headerStorage; private SharedRef<IStorage> _headerStorage;
private SharedRef<IAesCtrDecryptor> _aesCtrDecryptor; private SharedRef<IAesCtrDecryptor> _aesCtrDecryptor;
private GetDecompressorFunction _getDecompressorFunc; private IDecompressorFactory _decompressorFactory;
private IHash256GeneratorFactorySelector _hashGeneratorFactorySelector; private IHash256GeneratorFactorySelector _hashGeneratorFactorySelector;
public NcaReader(in RuntimeNcaHeader runtimeNcaHeader, ref readonly SharedRef<IStorage> notVerifiedHeaderStorage, public NcaReader(in RuntimeNcaHeader runtimeNcaHeader, ref readonly SharedRef<IStorage> notVerifiedHeaderStorage,
@ -39,7 +39,7 @@ public class NcaReader : IDisposable
_bodyStorage = SharedRef<IStorage>.CreateCopy(in bodyStorage); _bodyStorage = SharedRef<IStorage>.CreateCopy(in bodyStorage);
_aesCtrDecryptor = SharedRef<IAesCtrDecryptor>.CreateCopy(in aesCtrDecryptor); _aesCtrDecryptor = SharedRef<IAesCtrDecryptor>.CreateCopy(in aesCtrDecryptor);
_getDecompressorFunc = compressionConfig.GetDecompressorFunc; _decompressorFactory = compressionConfig.DecompressorFactory;
_hashGeneratorFactorySelector = hashGeneratorFactorySelector; _hashGeneratorFactorySelector = hashGeneratorFactorySelector;
} }
@ -181,10 +181,10 @@ public class NcaReader : IDisposable
return SharedRef<IAesCtrDecryptor>.CreateCopy(in _aesCtrDecryptor); return SharedRef<IAesCtrDecryptor>.CreateCopy(in _aesCtrDecryptor);
} }
public GetDecompressorFunction GetDecompressor() public IDecompressorFactory GetDecompressorFactory()
{ {
Assert.SdkRequiresNotNull(_getDecompressorFunc); Assert.SdkRequiresNotNull(_decompressorFactory);
return _getDecompressorFunc; return _decompressorFactory;
} }
public IHash256GeneratorFactorySelector GetHashGeneratorFactorySelector() public IHash256GeneratorFactorySelector GetHashGeneratorFactorySelector()

View file

@ -13,6 +13,8 @@ internal enum StorageLayoutType
SdCard = 1 << 1, SdCard = 1 << 1,
GameCard = 1 << 2, GameCard = 1 << 2,
Usb = 1 << 3, Usb = 1 << 3,
IsApp = 1 << 24,
NonGameCard = Bis | SdCard | Usb, NonGameCard = Bis | SdCard | Usb,
All = Bis | SdCard | GameCard | Usb All = Bis | SdCard | GameCard | Usb

View file

@ -141,7 +141,7 @@ internal class CompressedStorage : IStorage
res = _dataStorage.Read(currentEntry.PhysicalOffset + dataOffsetInEntry, entryDestination); res = _dataStorage.Read(currentEntry.PhysicalOffset + dataOffsetInEntry, entryDestination);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
} }
else if (currentEntry.CompressionType == CompressionType.Zeroed) else if (currentEntry.CompressionType == CompressionType.FillZero)
{ {
entryDestination.Clear(); entryDestination.Clear();
} }