mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add DeepRetryStorage
This commit is contained in:
parent
4699825564
commit
492716af74
4 changed files with 369 additions and 47 deletions
183
src/LibHac/FsSrv/Impl/DeepRetryStorage.cs
Normal file
183
src/LibHac/FsSrv/Impl/DeepRetryStorage.cs
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
using System;
|
||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Crypto;
|
||||||
|
using LibHac.Diag;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.FsSystem;
|
||||||
|
using LibHac.Ncm;
|
||||||
|
using LibHac.Os;
|
||||||
|
|
||||||
|
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;
|
||||||
|
private bool _deepRetryEnabled;
|
||||||
|
private ReaderWriterLock _readWriteLock;
|
||||||
|
|
||||||
|
// LibHac addition
|
||||||
|
private FileSystemServer _fsServer;
|
||||||
|
|
||||||
|
private struct DataStorageContext
|
||||||
|
{
|
||||||
|
private Hash _digest;
|
||||||
|
private ulong _programId;
|
||||||
|
private StorageId _storageId;
|
||||||
|
private bool _isValid;
|
||||||
|
|
||||||
|
public DataStorageContext()
|
||||||
|
{
|
||||||
|
_digest = default;
|
||||||
|
_programId = 0;
|
||||||
|
_storageId = StorageId.None;
|
||||||
|
_isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataStorageContext(in Hash digest, ulong programId, StorageId storageId)
|
||||||
|
{
|
||||||
|
_digest = digest;
|
||||||
|
_programId = programId;
|
||||||
|
_storageId = storageId;
|
||||||
|
_isValid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly bool IsValid() => _isValid;
|
||||||
|
public readonly Hash GetDigest() => _digest;
|
||||||
|
public readonly ulong GetProgramIdValue() => _programId;
|
||||||
|
public readonly StorageId GetStorageId() => _storageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
_parent = SharedRef<IRomFileSystemAccessFailureManager>.CreateCopy(in parent);
|
||||||
|
_mountCountLock = UniqueRef<IUniqueLock>.Create(ref mountCountSemaphore);
|
||||||
|
_dataStorageContext = new DataStorageContext();
|
||||||
|
_deepRetryEnabled = deepRetryEnabled;
|
||||||
|
_readWriteLock = new ReaderWriterLock(fsServer.Hos.Os);
|
||||||
|
|
||||||
|
_fsServer = fsServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
_parent = SharedRef<IRomFileSystemAccessFailureManager>.CreateCopy(in parent);
|
||||||
|
_mountCountLock = UniqueRef<IUniqueLock>.Create(ref mountCountSemaphore);
|
||||||
|
_dataStorageContext = new DataStorageContext(in hash, programId, storageId);
|
||||||
|
_deepRetryEnabled = true;
|
||||||
|
_readWriteLock = new ReaderWriterLock(fsServer.Hos.Os);
|
||||||
|
|
||||||
|
_fsServer = fsServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
_readWriteLock.Dispose();
|
||||||
|
_mountCountLock.Destroy();
|
||||||
|
_parent.Destroy();
|
||||||
|
_accessSplitter.Destroy();
|
||||||
|
_asyncStorage.Dispose();
|
||||||
|
|
||||||
|
base.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Result Read(long offset, Span<byte> destination)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Result Write(long offset, ReadOnlySpan<byte> source)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Result Flush()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Result SetSize(long size)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Result GetSize(out long size)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Result OperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan<byte> inBuffer)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result InvalidateCacheOnStorage(bool remount)
|
||||||
|
{
|
||||||
|
Abort.DoAbortUnless(_deepRetryEnabled);
|
||||||
|
|
||||||
|
using var scopedWriterLock = new UniqueLock<ReaderWriterLock>(_readWriteLock);
|
||||||
|
|
||||||
|
if (!remount || !_dataStorageContext.IsValid())
|
||||||
|
{
|
||||||
|
_asyncStorage.OperateRange(default, OperationId.InvalidateCache, 0, long.MaxValue, default).IgnoreResult();
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.SdkNotNull(_parent);
|
||||||
|
|
||||||
|
using var remountStorage = new SharedRef<IStorage>();
|
||||||
|
using var remountStorageAccessSplitter = new SharedRef<IAsynchronousAccessSplitter>();
|
||||||
|
|
||||||
|
const int maxRetryCount = 2;
|
||||||
|
|
||||||
|
Result retryResult = Result.Success;
|
||||||
|
Hash digest = default;
|
||||||
|
for (int i = 0; i < maxRetryCount; i++)
|
||||||
|
{
|
||||||
|
retryResult = _parent.Get.OpenDataStorageCore(ref remountStorage.Ref, ref remountStorageAccessSplitter.Ref,
|
||||||
|
out digest, _dataStorageContext.GetProgramIdValue(), _dataStorageContext.GetStorageId());
|
||||||
|
|
||||||
|
if (!ResultFs.DataCorrupted.Includes(retryResult))
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Todo: Log
|
||||||
|
// _fsServer.Hos.Diag.Impl.LogImpl()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retryResult.IsFailure()) return retryResult.Miss();
|
||||||
|
|
||||||
|
// Compare the digest of the remounted NCA header to the one we originally mounted
|
||||||
|
Hash originalDigest = _dataStorageContext.GetDigest();
|
||||||
|
if (!CryptoUtil.IsSameBytes(originalDigest.Value, digest.Value, digest.Value.Length))
|
||||||
|
{
|
||||||
|
return ResultFs.NcaDigestInconsistent.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
_accessSplitter.SetByMove(ref remountStorageAccessSplitter.Ref);
|
||||||
|
_asyncStorage.SetBaseStorage(in remountStorage, _accessSplitter.Get);
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AcquireReaderLockForCacheInvalidation(ref SharedLock<ReaderWriterLock> outReaderLock)
|
||||||
|
{
|
||||||
|
outReaderLock.Reset(_readWriteLock);
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,12 @@ using Utility = LibHac.FsSrv.Impl.Utility;
|
||||||
|
|
||||||
namespace LibHac.FsSrv;
|
namespace LibHac.FsSrv;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles NCA-related calls for <see cref="FileSystemProxyImpl"/>.
|
||||||
|
/// </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>
|
||||||
internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
||||||
{
|
{
|
||||||
private const int AocSemaphoreCount = 128;
|
private const int AocSemaphoreCount = 128;
|
||||||
|
@ -90,27 +96,26 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
||||||
Result res = GetProgramInfo(out ProgramInfo callerProgramInfo);
|
Result res = GetProgramInfo(out ProgramInfo callerProgramInfo);
|
||||||
if (res.IsFailure()) return res.Miss();
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
if (fsType != FileSystemProxyType.Manual)
|
switch (fsType)
|
||||||
{
|
{
|
||||||
switch (fsType)
|
case FileSystemProxyType.Manual:
|
||||||
{
|
Accessibility accessibility =
|
||||||
case FileSystemProxyType.Logo:
|
callerProgramInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountContentManual);
|
||||||
case FileSystemProxyType.Control:
|
|
||||||
case FileSystemProxyType.Meta:
|
if (!accessibility.CanRead)
|
||||||
case FileSystemProxyType.Data:
|
return ResultFs.PermissionDenied.Log();
|
||||||
case FileSystemProxyType.Package:
|
|
||||||
return ResultFs.NotImplemented.Log();
|
break;
|
||||||
default:
|
case FileSystemProxyType.Logo:
|
||||||
return ResultFs.InvalidArgument.Log();
|
case FileSystemProxyType.Control:
|
||||||
}
|
case FileSystemProxyType.Meta:
|
||||||
|
case FileSystemProxyType.Data:
|
||||||
|
case FileSystemProxyType.Package:
|
||||||
|
return ResultFs.NotImplemented.Log();
|
||||||
|
default:
|
||||||
|
return ResultFs.InvalidArgument.Log();
|
||||||
}
|
}
|
||||||
|
|
||||||
Accessibility accessibility =
|
|
||||||
callerProgramInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountContentManual);
|
|
||||||
|
|
||||||
if (!accessibility.CanRead)
|
|
||||||
return ResultFs.PermissionDenied.Log();
|
|
||||||
|
|
||||||
// Get the program info for the owner of the file system being opened
|
// Get the program info for the owner of the file system being opened
|
||||||
res = GetProgramInfoByProgramId(out ProgramInfo ownerProgramInfo, programId.Value);
|
res = GetProgramInfoByProgramId(out ProgramInfo ownerProgramInfo, programId.Value);
|
||||||
if (res.IsFailure()) return res.Miss();
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
@ -123,7 +128,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
||||||
|
|
||||||
// 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))
|
||||||
return originalResult;
|
return originalResult.Miss();
|
||||||
|
|
||||||
// Try to find the path to the patch file system
|
// Try to find the path to the patch file system
|
||||||
using var patchPath = new Path();
|
using var patchPath = new Path();
|
||||||
|
@ -136,17 +141,17 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
||||||
{
|
{
|
||||||
// There must either be an original version or patch version of the file system being opened
|
// There must either be an original version or patch version of the file system being opened
|
||||||
if (originalResult.IsFailure())
|
if (originalResult.IsFailure())
|
||||||
return originalResult;
|
return originalResult.Miss();
|
||||||
|
|
||||||
// There is an original version and no patch version. Open the original directly
|
// There is an original version and no patch version. Open the original directly
|
||||||
res = _serviceImpl.OpenFileSystem(ref fileSystem.Ref, in originalPath, default, fsType, programId.Value,
|
res = _serviceImpl.OpenFileSystem(ref fileSystem.Ref, in originalPath, contentAttributes, fsType,
|
||||||
isDirectory);
|
programId.Value, isDirectory);
|
||||||
if (res.IsFailure()) return res.Miss();
|
if (res.IsFailure()) return res.Miss();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (patchResult.IsFailure())
|
if (patchResult.IsFailure())
|
||||||
return patchResult;
|
return patchResult.Miss();
|
||||||
|
|
||||||
ref readonly Path originalNcaPath = ref originalResult.IsSuccess()
|
ref readonly Path originalNcaPath = ref originalResult.IsSuccess()
|
||||||
? ref originalPath
|
? ref originalPath
|
||||||
|
@ -214,7 +219,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
||||||
|
|
||||||
public void IncrementRomFsDeepRetryStartCount()
|
public void IncrementRomFsDeepRetryStartCount()
|
||||||
{
|
{
|
||||||
_serviceImpl.IncrementRomFsRemountForDataCorruptionCount();
|
_serviceImpl.IncrementRomFsDeepRetryStartCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void IncrementRomFsRemountForDataCorruptionCount()
|
public void IncrementRomFsRemountForDataCorruptionCount()
|
||||||
|
@ -234,7 +239,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
||||||
|
|
||||||
public void IncrementRomFsUnrecoverableByGameCardAccessFailedCount()
|
public void IncrementRomFsUnrecoverableByGameCardAccessFailedCount()
|
||||||
{
|
{
|
||||||
_serviceImpl.IncrementRomFsRecoveredByInvalidateCacheCount();
|
_serviceImpl.IncrementRomFsUnrecoverableByGameCardAccessFailedCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result OpenDataStorageCore(ref SharedRef<IStorage> outStorage,
|
private Result OpenDataStorageCore(ref SharedRef<IStorage> outStorage,
|
||||||
|
@ -249,13 +254,13 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result OpenDataStorageByProgramId(ref SharedRef<IStorageSf> outStorage, ProgramId programId)
|
public Result OpenDataStorageByPath(ref SharedRef<IStorageSf> outStorage, in FspPath path,
|
||||||
|
ContentAttributes attributes, FileSystemProxyType fspType)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result OpenDataStorageByPath(ref SharedRef<IStorageSf> outStorage, in FspPath path,
|
public Result OpenDataStorageByProgramId(ref SharedRef<IStorageSf> outStorage, ProgramId programId)
|
||||||
ContentAttributes attributes, FileSystemProxyType fspType)
|
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
@ -316,17 +321,17 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
||||||
res = pathNormalized.InitializeWithReplaceUnc(path.Str);
|
res = pathNormalized.InitializeWithReplaceUnc(path.Str);
|
||||||
if (res.IsFailure()) return res.Miss();
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
var pathFlags = new PathFlags();
|
var flags = new PathFlags();
|
||||||
pathFlags.AllowWindowsPath();
|
flags.AllowMountName();
|
||||||
pathFlags.AllowMountName();
|
flags.AllowWindowsPath();
|
||||||
res = pathNormalized.Normalize(pathFlags);
|
res = pathNormalized.Normalize(flags);
|
||||||
if (res.IsFailure()) return res.Miss();
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
bool isDirectory = PathUtility.IsDirectoryPath(in path);
|
bool isDirectory = PathUtility.IsDirectoryPath(in path);
|
||||||
|
|
||||||
using var fileSystem = new SharedRef<IFileSystem>();
|
using var fileSystem = new SharedRef<IFileSystem>();
|
||||||
res = _serviceImpl.OpenFileSystem(ref fileSystem.Ref, in pathNormalized, default, fsType, canMountSystemDataPrivate,
|
res = _serviceImpl.OpenFileSystem(ref fileSystem.Ref, in pathNormalized, attributes, fsType,
|
||||||
id, isDirectory);
|
canMountSystemDataPrivate, id, isDirectory);
|
||||||
if (res.IsFailure()) return res.Miss();
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
// Add all the wrappers for the file system
|
// Add all the wrappers for the file system
|
||||||
|
@ -399,7 +404,45 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
||||||
|
|
||||||
public Result OpenDataStorageWithProgramIndex(ref SharedRef<IStorageSf> outStorage, byte programIndex)
|
public Result OpenDataStorageWithProgramIndex(ref SharedRef<IStorageSf> outStorage, byte programIndex)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
Result res = GetProgramInfo(out ProgramInfo programInfo);
|
||||||
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
|
// Get the program ID of the program with the specified index if in a multi-program application
|
||||||
|
res = _serviceImpl.ResolveRomReferenceProgramId(out ProgramId targetProgramId, programInfo.ProgramId, programIndex);
|
||||||
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
|
StorageLayoutType storageFlag = _serviceImpl.GetStorageFlag(targetProgramId.Value);
|
||||||
|
using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
|
||||||
|
|
||||||
|
using var romMountCountSemaphore = new UniqueRef<IUniqueLock>();
|
||||||
|
res = TryAcquireRomMountCountSemaphore(ref romMountCountSemaphore.Ref);
|
||||||
|
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, out Hash digest, targetProgramId.Value, programInfo.StorageId);
|
||||||
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
|
if (programInfo.ProgramId != targetProgramId && !programInfo.AccessControl.HasContentOwnerId(targetProgramId.Value))
|
||||||
|
return ResultFs.PermissionDenied.Log();
|
||||||
|
|
||||||
|
using var romDivisionSizeUnitCountSemaphore = new UniqueRef<IUniqueLock>();
|
||||||
|
res = TryAcquireRomDivisionSizeUnitCountSemaphore(ref romDivisionSizeUnitCountSemaphore.Ref,
|
||||||
|
ref romMountCountSemaphore.Ref, storage.Get);
|
||||||
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
|
using SharedRef<IRomFileSystemAccessFailureManager> accessFailureManager = SharedRef<IRomFileSystemAccessFailureManager>.Create(in _selfReference);
|
||||||
|
|
||||||
|
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 typeSetStorage = new SharedRef<IStorage>(new StorageLayoutTypeSetStorage(ref retryStorage.Ref, storageFlag));
|
||||||
|
using var storageAdapter = new SharedRef<IStorageSf>(new StorageInterfaceAdapter(ref typeSetStorage.Ref));
|
||||||
|
|
||||||
|
outStorage.SetByMove(ref storageAdapter.Ref);
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result GetRightsId(out RightsId outRightsId, ProgramId programId, StorageId storageId)
|
public Result GetRightsId(out RightsId outRightsId, ProgramId programId, StorageId storageId)
|
||||||
|
@ -433,7 +476,6 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
||||||
public Result GetRightsIdAndKeyGenerationByPath(out RightsId outRightsId, out byte outKeyGeneration,
|
public Result GetRightsIdAndKeyGenerationByPath(out RightsId outRightsId, out byte outKeyGeneration,
|
||||||
ref readonly FspPath path, ContentAttributes attributes)
|
ref readonly FspPath path, ContentAttributes attributes)
|
||||||
{
|
{
|
||||||
const ulong checkThroughProgramId = ulong.MaxValue;
|
|
||||||
UnsafeHelpers.SkipParamInit(out outRightsId, out outKeyGeneration);
|
UnsafeHelpers.SkipParamInit(out outRightsId, out outKeyGeneration);
|
||||||
|
|
||||||
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.All);
|
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.All);
|
||||||
|
@ -448,16 +490,18 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
||||||
res = pathNormalized.Initialize(path.Str);
|
res = pathNormalized.Initialize(path.Str);
|
||||||
if (res.IsFailure()) return res.Miss();
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
var pathFlags = new PathFlags();
|
var flags = new PathFlags();
|
||||||
pathFlags.AllowWindowsPath();
|
flags.AllowMountName();
|
||||||
pathFlags.AllowMountName();
|
flags.AllowWindowsPath();
|
||||||
res = pathNormalized.Normalize(pathFlags);
|
res = pathNormalized.Normalize(flags);
|
||||||
if (res.IsFailure()) return res.Miss();
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
if (PathUtility.IsDirectoryPath(in path))
|
if (PathUtility.IsDirectoryPath(in path))
|
||||||
return ResultFs.TargetNotFound.Log();
|
return ResultFs.TargetNotFound.Log();
|
||||||
|
|
||||||
res = _serviceImpl.GetRightsId(out RightsId rightsId, out byte keyGeneration, in pathNormalized, default,
|
const ulong checkThroughProgramId = ulong.MaxValue;
|
||||||
|
|
||||||
|
res = _serviceImpl.GetRightsId(out RightsId rightsId, out byte keyGeneration, in pathNormalized, attributes,
|
||||||
new ProgramId(checkThroughProgramId));
|
new ProgramId(checkThroughProgramId));
|
||||||
if (res.IsFailure()) return res.Miss();
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
|
@ -469,7 +513,34 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
||||||
|
|
||||||
public Result GetProgramId(out ProgramId outProgramId, ref readonly FspPath path, ContentAttributes attributes)
|
public Result GetProgramId(out ProgramId outProgramId, ref readonly FspPath path, ContentAttributes attributes)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
UnsafeHelpers.SkipParamInit(out outProgramId);
|
||||||
|
|
||||||
|
using var scopedContext = new ScopedStorageLayoutTypeSetter(StorageLayoutType.All);
|
||||||
|
|
||||||
|
Result res = GetProgramInfo(out ProgramInfo programInfo);
|
||||||
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
|
if (!programInfo.AccessControl.CanCall(OperationType.GetProgramId))
|
||||||
|
return ResultFs.PermissionDenied.Log();
|
||||||
|
|
||||||
|
using var pathNormalized = new Path();
|
||||||
|
res = pathNormalized.Initialize(path.Str);
|
||||||
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
|
var flags = new PathFlags();
|
||||||
|
flags.AllowMountName();
|
||||||
|
flags.AllowWindowsPath();
|
||||||
|
res = pathNormalized.Normalize(flags);
|
||||||
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
|
if (PathUtility.IsDirectoryPath(in path))
|
||||||
|
return ResultFs.TargetNotFound.Log();
|
||||||
|
|
||||||
|
res = _serviceImpl.GetProgramId(out ProgramId programId, in pathNormalized, attributes);
|
||||||
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
|
outProgramId = programId;
|
||||||
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReSharper disable once OutParameterValueIsAlwaysDiscarded.Local
|
// ReSharper disable once OutParameterValueIsAlwaysDiscarded.Local
|
||||||
|
@ -489,13 +560,12 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
|
||||||
isHostFs = Utility.IsHostFsMountName(programPath.GetString());
|
isHostFs = Utility.IsHostFsMountName(programPath.GetString());
|
||||||
|
|
||||||
using var fileSystem = new SharedRef<IFileSystem>();
|
using var fileSystem = new SharedRef<IFileSystem>();
|
||||||
res = _serviceImpl.OpenDataFileSystem(ref fileSystem.Ref, in programPath, contentAttributes, FileSystemProxyType.Rom,
|
res = _serviceImpl.OpenDataFileSystem(ref fileSystem.Ref, in programPath, contentAttributes,
|
||||||
programId, isDirectory);
|
FileSystemProxyType.Rom, programId, isDirectory);
|
||||||
if (res.IsFailure()) return res.Miss();
|
if (res.IsFailure()) return res.Miss();
|
||||||
|
|
||||||
// Add all the file system wrappers
|
// Add all the file system wrappers
|
||||||
using var typeSetFileSystem =
|
using var typeSetFileSystem = new SharedRef<IFileSystem>(new StorageLayoutTypeSetFileSystem(ref fileSystem.Ref, storageFlag));
|
||||||
new SharedRef<IFileSystem>(new StorageLayoutTypeSetFileSystem(ref fileSystem.Ref, storageFlag));
|
|
||||||
|
|
||||||
outFileSystem.SetByMove(ref typeSetFileSystem.Ref);
|
outFileSystem.SetByMove(ref typeSetFileSystem.Ref);
|
||||||
|
|
||||||
|
|
|
@ -238,7 +238,7 @@ public class NcaFileSystemServiceImpl : IDisposable
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result GetProgramId(out ProgramId outProgramId, ref readonly Path path, ContentAttributes attributes, ProgramId programId)
|
public Result GetProgramId(out ProgramId outProgramId, ref readonly Path path, ContentAttributes attributes)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,3 +88,72 @@ public class DefaultAsynchronousAccessSplitter : IAsynchronousAccessSplitter
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class AsynchronousAccessStorage : IStorage
|
||||||
|
{
|
||||||
|
private SharedRef<IStorage> _baseStorage;
|
||||||
|
// private ThreadPool _threadPool;
|
||||||
|
private IAsynchronousAccessSplitter _baseStorageAccessSplitter;
|
||||||
|
|
||||||
|
public AsynchronousAccessStorage(ref readonly SharedRef<IStorage> baseStorage) : this(in baseStorage,
|
||||||
|
IAsynchronousAccessSplitter.GetDefaultAsynchronousAccessSplitter())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsynchronousAccessStorage(ref readonly SharedRef<IStorage> baseStorage, IAsynchronousAccessSplitter baseStorageAccessSplitter)
|
||||||
|
{
|
||||||
|
_baseStorage = SharedRef<IStorage>.CreateCopy(in baseStorage);
|
||||||
|
_baseStorageAccessSplitter = baseStorageAccessSplitter;
|
||||||
|
|
||||||
|
Assert.SdkRequiresNotNull(in _baseStorage);
|
||||||
|
Assert.SdkRequiresNotNull(_baseStorageAccessSplitter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Dispose()
|
||||||
|
{
|
||||||
|
_baseStorage.Destroy();
|
||||||
|
base.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetBaseStorage(ref readonly SharedRef<IStorage> baseStorage, IAsynchronousAccessSplitter baseStorageAccessSplitter)
|
||||||
|
{
|
||||||
|
_baseStorage.SetByCopy(in baseStorage);
|
||||||
|
_baseStorageAccessSplitter = baseStorageAccessSplitter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Todo: Implement
|
||||||
|
public override Result Read(long offset, Span<byte> destination)
|
||||||
|
{
|
||||||
|
return _baseStorage.Get.Read(offset, destination).Ret();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result ReadImpl(long offset, Span<byte> destination)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Result Write(long offset, ReadOnlySpan<byte> source)
|
||||||
|
{
|
||||||
|
return _baseStorage.Get.Write(offset, source).Ret();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Result GetSize(out long size)
|
||||||
|
{
|
||||||
|
return _baseStorage.Get.GetSize(out size).Ret();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Result SetSize(long size)
|
||||||
|
{
|
||||||
|
return _baseStorage.Get.SetSize(size).Ret();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Result Flush()
|
||||||
|
{
|
||||||
|
return _baseStorage.Get.Flush().Ret();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Result OperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan<byte> inBuffer)
|
||||||
|
{
|
||||||
|
return _baseStorage.Get.OperateRange(outBuffer, operationId, offset, size, inBuffer).Ret();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue