mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2025-02-09 13:14:46 +01:00
Fully implement LR client and LocationResolverSet
This commit is contained in:
parent
4113bc2d36
commit
5dc7c57851
@ -1,6 +1,7 @@
|
||||
Name,Index
|
||||
Svc,1
|
||||
Fs,2
|
||||
Lr,8
|
||||
Loader,9
|
||||
Sf,10
|
||||
Kvdb,20
|
||||
|
|
@ -1,6 +1,7 @@
|
||||
Name,Namespace,Path
|
||||
Svc,LibHac.Svc,LibHac/Svc/ResultSvc.cs
|
||||
Fs,LibHac.Fs,LibHac/Fs/ResultFs.cs
|
||||
Lr,LibHac.Lr,LibHac/Lr/ResultLr.cs
|
||||
Loader,LibHac.Loader,LibHac/Loader/ResultLoader.cs
|
||||
Sf,LibHac.Sf,LibHac/Sf/ResultSf.cs
|
||||
Kvdb,LibHac.Kvdb,LibHac/Kvdb/ResultKvdb.cs
|
||||
|
|
@ -89,12 +89,28 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
|
||||
2,3005,,OutOfRange,
|
||||
|
||||
2,3200,3499,AllocationMemoryFailed,
|
||||
2,3256,,AllocationFailureInNcaFileSystemServiceImplA,In ParseNsp allocating FileStorageBasedFileSystem
|
||||
2,3257,,AllocationFailureInNcaFileSystemServiceImplB,In ParseNca allocating FileStorageBasedFileSystem
|
||||
2,3258,,AllocationFailureInProgramRegistryManagerA,In RegisterProgram allocating ProgramInfoNode
|
||||
2,3264,,AllocationFailureFatFileSystemA,In Initialize allocating ProgramInfoNode
|
||||
2,3280,,AllocationFailureInPartitionFileSystemCreatorA,In Create allocating PartitionFileSystemCore
|
||||
2,3312,,AllocationFailureInAesXtsFileA,In Initialize allocating FileStorage
|
||||
2,3313,,AllocationFailureInAesXtsFileB,In Initialize allocating AesXtsStorage
|
||||
2,3314,,AllocationFailureInAesXtsFileC,In Initialize allocating AlignmentMatchingStoragePooledBuffer
|
||||
2,3315,,AllocationFailureInAesXtsFileD,In Initialize allocating StorageFile
|
||||
2,3383,,AllocationFailureInAesXtsFileE,In Initialize allocating SubStorage
|
||||
2,3347,,AllocationFailureInPartitionFileSystemA,In Initialize allocating PartitionFileSystemMetaCore
|
||||
2,3348,,AllocationFailureInPartitionFileSystemB,In DoOpenFile allocating PartitionFile
|
||||
2,3349,,AllocationFailureInPartitionFileSystemC,In DoOpenDirectory allocating PartitionDirectory
|
||||
2,3350,,AllocationFailureInPartitionFileSystemMetaA,In Initialize allocating metadata buffer
|
||||
2,3351,,AllocationFailureInPartitionFileSystemMetaB,In Sha256 Initialize allocating metadata buffer
|
||||
2,3355,,AllocationFailureInSubdirectoryFileSystemA,In Initialize allocating RootPathBuffer
|
||||
2,3383,,AllocationFailureInAesXtsFileE,In Initialize
|
||||
2,3394,,AllocationFailureInEncryptedFileSystemCreatorA,In Create allocating AesXtsFileSystem
|
||||
2,3420,,AllocationFailureInNew,
|
||||
2,3421,,AllocationFailureInCreateShared,
|
||||
2,3422,,AllocationFailureInMakeUnique,
|
||||
2,3423,,AllocationFailureInAllocateShared,
|
||||
2,3424,,AllocationFailurePooledBufferNotEnoughSize,
|
||||
|
||||
2,3500,3999,MmcAccessFailed,
|
||||
|
||||
@ -148,8 +164,8 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
|
||||
2,4464,,AllocationTableIteratedRangeEntry,
|
||||
|
||||
2,4501,4599,NcaCorrupted,
|
||||
2,4512,,InvalidNcaFsType,
|
||||
2,4527,,InvalidNcaProgramId,
|
||||
2,4512,,InvalidNcaFileSystemType,
|
||||
2,4527,,InvalidNcaId,
|
||||
|
||||
2,4601,4639,IntegrityVerificationStorageCorrupted,
|
||||
2,4602,,InvalidIvfcMagic,
|
||||
@ -303,6 +319,17 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
|
||||
2,6905,,NotMounted,
|
||||
2,6906,,SaveDataIsExtending,
|
||||
|
||||
8,2,,ProgramNotFound,
|
||||
8,3,,DataNotFound,
|
||||
8,4,,UnknownStorageId,
|
||||
8,5,,LocationResolverNotFound,
|
||||
8,6,,HtmlDocumentNotFound,
|
||||
8,7,,AddOnContentNotFound,
|
||||
8,8,,ControlNotFound,
|
||||
8,9,,LegalInformationNotFound,
|
||||
8,10,,DebugProgramNotFound,
|
||||
8,90,,TooManyRegisteredPaths,
|
||||
|
||||
9,1,,TooLongArgument,
|
||||
9,2,,TooManyArguments,
|
||||
9,3,,TooLargeMeta,
|
||||
|
Can't render this file because it has a wrong number of fields in line 219.
|
19
src/LibHac/Fat/FatError.cs
Normal file
19
src/LibHac/Fat/FatError.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common;
|
||||
|
||||
namespace LibHac.Fat
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x20)]
|
||||
public struct FatError
|
||||
{
|
||||
private const int FunctionNameLength = 0x10;
|
||||
|
||||
[FieldOffset(0x00)] public int Error;
|
||||
[FieldOffset(0x04)] public int ExtraError;
|
||||
[FieldOffset(0x08)] public int DriveId;
|
||||
[FieldOffset(0x0C)] private byte _functionName;
|
||||
|
||||
public U8SpanMutable ErrorName =>
|
||||
new U8SpanMutable(SpanHelpers.CreateSpan(ref _functionName, FunctionNameLength));
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@ namespace LibHac.Fs
|
||||
{
|
||||
private readonly Key128 Key;
|
||||
|
||||
public ReadOnlySpan<byte> Value => SpanHelpers.AsByteSpan(ref this);
|
||||
public readonly ReadOnlySpan<byte> Value => SpanHelpers.AsReadOnlyByteSpan(in this);
|
||||
|
||||
public EncryptionSeed(ReadOnlySpan<byte> bytes)
|
||||
{
|
||||
|
@ -6,35 +6,17 @@ namespace LibHac.Fs
|
||||
public class FileStorageBasedFileSystem : FileStorage2
|
||||
{
|
||||
// ReSharper disable once UnusedAutoPropertyAccessor.Local
|
||||
// FS keeps a shared pointer to the base filesystem
|
||||
private IFileSystem BaseFileSystem { get; set; }
|
||||
private ReferenceCountedDisposable<IFileSystem> BaseFileSystem { get; set; }
|
||||
private IFile BaseFile { get; set; }
|
||||
|
||||
private FileStorageBasedFileSystem()
|
||||
public FileStorageBasedFileSystem()
|
||||
{
|
||||
FileSize = SizeNotInitialized;
|
||||
}
|
||||
|
||||
public static Result CreateNew(out FileStorageBasedFileSystem created, IFileSystem baseFileSystem, U8Span path,
|
||||
OpenMode mode)
|
||||
public Result Initialize(ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path, OpenMode mode)
|
||||
{
|
||||
var obj = new FileStorageBasedFileSystem();
|
||||
Result rc = obj.Initialize(baseFileSystem, path, mode);
|
||||
|
||||
if (rc.IsSuccess())
|
||||
{
|
||||
created = obj;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
obj.Dispose();
|
||||
created = default;
|
||||
return rc;
|
||||
}
|
||||
|
||||
private Result Initialize(IFileSystem baseFileSystem, U8Span path, OpenMode mode)
|
||||
{
|
||||
Result rc = baseFileSystem.OpenFile(out IFile file, path, mode);
|
||||
Result rc = baseFileSystem.Target.OpenFile(out IFile file, path, mode);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
SetFile(file);
|
||||
@ -49,6 +31,7 @@ namespace LibHac.Fs
|
||||
if (disposing)
|
||||
{
|
||||
BaseFile?.Dispose();
|
||||
BaseFileSystem?.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
|
15
src/LibHac/Fs/FileSystemProxyErrorInfo.cs
Normal file
15
src/LibHac/Fs/FileSystemProxyErrorInfo.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Fat;
|
||||
|
||||
namespace LibHac.Fs
|
||||
{
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x80)]
|
||||
public struct FileSystemProxyErrorInfo
|
||||
{
|
||||
[FieldOffset(0x00)] public int RomFsRemountForDataCorruptionCount;
|
||||
[FieldOffset(0x04)] public int RomFsUnrecoverableDataCorruptionByRemountCount;
|
||||
[FieldOffset(0x08)] public FatError FatError;
|
||||
[FieldOffset(0x28)] public int RomFsRecoveredByInvalidateCacheCount;
|
||||
[FieldOffset(0x2C)] public int SaveDataIndexCount;
|
||||
}
|
||||
}
|
@ -96,8 +96,16 @@ namespace LibHac.Fs
|
||||
|
||||
/// <summary>Error code: 2002-3200; Range: 3200-3499; Inner value: 0x190002</summary>
|
||||
public static Result.Base AllocationMemoryFailed { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 3200, 3499); }
|
||||
/// <summary>In ParseNsp allocating FileStorageBasedFileSystem<br/>Error code: 2002-3256; Inner value: 0x197002</summary>
|
||||
public static Result.Base AllocationFailureInNcaFileSystemServiceImplA => new Result.Base(ModuleFs, 3256);
|
||||
/// <summary>In ParseNca allocating FileStorageBasedFileSystem<br/>Error code: 2002-3257; Inner value: 0x197202</summary>
|
||||
public static Result.Base AllocationFailureInNcaFileSystemServiceImplB => new Result.Base(ModuleFs, 3257);
|
||||
/// <summary>In RegisterProgram allocating ProgramInfoNode<br/>Error code: 2002-3258; Inner value: 0x197402</summary>
|
||||
public static Result.Base AllocationFailureInProgramRegistryManagerA => new Result.Base(ModuleFs, 3258);
|
||||
/// <summary>In Initialize allocating ProgramInfoNode<br/>Error code: 2002-3264; Inner value: 0x198002</summary>
|
||||
public static Result.Base AllocationFailureFatFileSystemA => new Result.Base(ModuleFs, 3264);
|
||||
/// <summary>In Create allocating PartitionFileSystemCore<br/>Error code: 2002-3280; Inner value: 0x19a002</summary>
|
||||
public static Result.Base AllocationFailureInPartitionFileSystemCreatorA => new Result.Base(ModuleFs, 3280);
|
||||
/// <summary>In Initialize allocating FileStorage<br/>Error code: 2002-3312; Inner value: 0x19e002</summary>
|
||||
public static Result.Base AllocationFailureInAesXtsFileA => new Result.Base(ModuleFs, 3312);
|
||||
/// <summary>In Initialize allocating AesXtsStorage<br/>Error code: 2002-3313; Inner value: 0x19e202</summary>
|
||||
@ -106,8 +114,32 @@ namespace LibHac.Fs
|
||||
public static Result.Base AllocationFailureInAesXtsFileC => new Result.Base(ModuleFs, 3314);
|
||||
/// <summary>In Initialize allocating StorageFile<br/>Error code: 2002-3315; Inner value: 0x19e602</summary>
|
||||
public static Result.Base AllocationFailureInAesXtsFileD => new Result.Base(ModuleFs, 3315);
|
||||
/// <summary>In Initialize allocating SubStorage<br/>Error code: 2002-3383; Inner value: 0x1a6e02</summary>
|
||||
/// <summary>In Initialize allocating PartitionFileSystemMetaCore<br/>Error code: 2002-3347; Inner value: 0x1a2602</summary>
|
||||
public static Result.Base AllocationFailureInPartitionFileSystemA => new Result.Base(ModuleFs, 3347);
|
||||
/// <summary>In DoOpenFile allocating PartitionFile<br/>Error code: 2002-3348; Inner value: 0x1a2802</summary>
|
||||
public static Result.Base AllocationFailureInPartitionFileSystemB => new Result.Base(ModuleFs, 3348);
|
||||
/// <summary>In DoOpenDirectory allocating PartitionDirectory<br/>Error code: 2002-3349; Inner value: 0x1a2a02</summary>
|
||||
public static Result.Base AllocationFailureInPartitionFileSystemC => new Result.Base(ModuleFs, 3349);
|
||||
/// <summary>In Initialize allocating metadata buffer<br/>Error code: 2002-3350; Inner value: 0x1a2c02</summary>
|
||||
public static Result.Base AllocationFailureInPartitionFileSystemMetaA => new Result.Base(ModuleFs, 3350);
|
||||
/// <summary>In Sha256 Initialize allocating metadata buffer<br/>Error code: 2002-3351; Inner value: 0x1a2e02</summary>
|
||||
public static Result.Base AllocationFailureInPartitionFileSystemMetaB => new Result.Base(ModuleFs, 3351);
|
||||
/// <summary>In Initialize allocating RootPathBuffer<br/>Error code: 2002-3355; Inner value: 0x1a3602</summary>
|
||||
public static Result.Base AllocationFailureInSubdirectoryFileSystemA => new Result.Base(ModuleFs, 3355);
|
||||
/// <summary>In Initialize<br/>Error code: 2002-3383; Inner value: 0x1a6e02</summary>
|
||||
public static Result.Base AllocationFailureInAesXtsFileE => new Result.Base(ModuleFs, 3383);
|
||||
/// <summary>In Create allocating AesXtsFileSystem<br/>Error code: 2002-3394; Inner value: 0x1a8402</summary>
|
||||
public static Result.Base AllocationFailureInEncryptedFileSystemCreatorA => new Result.Base(ModuleFs, 3394);
|
||||
/// <summary>Error code: 2002-3420; Inner value: 0x1ab802</summary>
|
||||
public static Result.Base AllocationFailureInNew => new Result.Base(ModuleFs, 3420);
|
||||
/// <summary>Error code: 2002-3421; Inner value: 0x1aba02</summary>
|
||||
public static Result.Base AllocationFailureInCreateShared => new Result.Base(ModuleFs, 3421);
|
||||
/// <summary>Error code: 2002-3422; Inner value: 0x1abc02</summary>
|
||||
public static Result.Base AllocationFailureInMakeUnique => new Result.Base(ModuleFs, 3422);
|
||||
/// <summary>Error code: 2002-3423; Inner value: 0x1abe02</summary>
|
||||
public static Result.Base AllocationFailureInAllocateShared => new Result.Base(ModuleFs, 3423);
|
||||
/// <summary>Error code: 2002-3424; Inner value: 0x1ac002</summary>
|
||||
public static Result.Base AllocationFailurePooledBufferNotEnoughSize => new Result.Base(ModuleFs, 3424);
|
||||
|
||||
/// <summary>Error code: 2002-3500; Range: 3500-3999; Inner value: 0x1b5802</summary>
|
||||
public static Result.Base MmcAccessFailed { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 3500, 3999); }
|
||||
@ -205,9 +237,9 @@ namespace LibHac.Fs
|
||||
/// <summary>Error code: 2002-4501; Range: 4501-4599; Inner value: 0x232a02</summary>
|
||||
public static Result.Base NcaCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 4501, 4599); }
|
||||
/// <summary>Error code: 2002-4512; Inner value: 0x234002</summary>
|
||||
public static Result.Base InvalidNcaFsType => new Result.Base(ModuleFs, 4512);
|
||||
public static Result.Base InvalidNcaFileSystemType => new Result.Base(ModuleFs, 4512);
|
||||
/// <summary>Error code: 2002-4527; Inner value: 0x235e02</summary>
|
||||
public static Result.Base InvalidNcaProgramId => new Result.Base(ModuleFs, 4527);
|
||||
public static Result.Base InvalidNcaId => new Result.Base(ModuleFs, 4527);
|
||||
|
||||
/// <summary>Error code: 2002-4601; Range: 4601-4639; Inner value: 0x23f202</summary>
|
||||
public static Result.Base IntegrityVerificationStorageCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 4601, 4639); }
|
||||
|
@ -2,7 +2,7 @@
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSrv;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.FsSrv.Sf;
|
||||
|
||||
namespace LibHac.Fs.Shim
|
||||
{
|
||||
@ -40,14 +40,15 @@ namespace LibHac.Fs.Shim
|
||||
Result rc = MountHelpers.CheckMountName(mountName);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
FsPath.FromSpan(out FsPath fsPath, path);
|
||||
FspPath.FromSpan(out FspPath sfPath, path);
|
||||
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
rc = fsProxy.OpenFileSystemWithId(out IFileSystem fileSystem, ref fsPath, default, FileSystemProxyType.Package);
|
||||
rc = fsProxy.OpenFileSystemWithId(out ReferenceCountedDisposable<IFileSystem> fileSystem, in sfPath,
|
||||
default, FileSystemProxyType.Package);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return fs.Register(mountName, fileSystem);
|
||||
return fs.Register(mountName, fileSystem.Target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -91,12 +91,13 @@ namespace LibHac.Fs.Shim
|
||||
// Nintendo doesn't use the provided rootPath
|
||||
FspPath.CreateEmpty(out FspPath sfPath);
|
||||
|
||||
rc = fsProxy.OpenBisFileSystem(out IFileSystem fileSystem, in sfPath, partitionId);
|
||||
rc = fsProxy.OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, in sfPath,
|
||||
partitionId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var nameGenerator = new BisCommonMountNameGenerator(partitionId);
|
||||
|
||||
return fs.Register(mountName, fileSystem, nameGenerator);
|
||||
return fs.Register(mountName, fileSystem.Target, nameGenerator);
|
||||
}
|
||||
|
||||
public static U8Span GetBisMountName(BisPartitionId partitionId)
|
||||
|
@ -49,10 +49,11 @@ namespace LibHac.Fs.Shim
|
||||
|
||||
IFileSystemProxyForLoader fsProxy = fs.GetFileSystemProxyForLoaderServiceObject();
|
||||
|
||||
rc = fsProxy.OpenCodeFileSystem(out IFileSystem codeFs, out verificationData, in fsPath, programId);
|
||||
rc = fsProxy.OpenCodeFileSystem(out ReferenceCountedDisposable<IFileSystem> codeFs, out verificationData,
|
||||
in fsPath, programId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return fs.Register(mountName, codeFs);
|
||||
return fs.Register(mountName, codeFs.Target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSrv;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.Ncm;
|
||||
|
||||
namespace LibHac.Fs.Shim
|
||||
@ -27,10 +27,11 @@ namespace LibHac.Fs.Shim
|
||||
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
rc = fsProxy.OpenFileSystemWithPatch(out IFileSystem fileSystem, programId, fspType);
|
||||
rc = fsProxy.OpenFileSystemWithPatch(out ReferenceCountedDisposable<IFileSystem> fileSystem, programId,
|
||||
fspType);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return fs.Register(mountName, fileSystem);
|
||||
return fs.Register(mountName, fileSystem.Target);
|
||||
}
|
||||
|
||||
public static Result MountContent(this FileSystemClient fs, U8Span mountName, U8Span path, ProgramId programId, ContentType type)
|
||||
@ -55,14 +56,15 @@ namespace LibHac.Fs.Shim
|
||||
|
||||
private static Result MountContentImpl(FileSystemClient fs, U8Span mountName, U8Span path, ulong id, FileSystemProxyType type)
|
||||
{
|
||||
FsPath.FromSpan(out FsPath fsPath, path);
|
||||
FspPath.FromSpan(out FspPath fsPath, path);
|
||||
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
Result rc = fsProxy.OpenFileSystemWithId(out IFileSystem fileSystem, ref fsPath, id, type);
|
||||
Result rc = fsProxy.OpenFileSystemWithId(out ReferenceCountedDisposable<IFileSystem> fileSystem, in fsPath,
|
||||
id, type);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return fs.Register(mountName, fileSystem);
|
||||
return fs.Register(mountName, fileSystem.Target);
|
||||
}
|
||||
|
||||
private static FileSystemProxyType ConvertToFileSystemProxyType(ContentType type) => type switch
|
||||
|
@ -20,12 +20,12 @@ namespace LibHac.Fs.Shim
|
||||
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
rc = fsProxy.OpenContentStorageFileSystem(out IFileSystem contentFs, storageId);
|
||||
rc = fsProxy.OpenContentStorageFileSystem(out ReferenceCountedDisposable<IFileSystem> contentFs, storageId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var mountNameGenerator = new ContentStorageCommonMountNameGenerator(storageId);
|
||||
|
||||
return fs.Register(mountName, contentFs, mountNameGenerator);
|
||||
return fs.Register(mountName, contentFs.Target, mountNameGenerator);
|
||||
}
|
||||
|
||||
public static U8String GetContentStorageMountName(ContentStorageId storageId)
|
||||
|
@ -48,12 +48,12 @@ namespace LibHac.Fs.Shim
|
||||
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
rc = fsProxy.OpenGameCardFileSystem(out IFileSystem cardFs, handle, partitionId);
|
||||
rc = fsProxy.OpenGameCardFileSystem(out ReferenceCountedDisposable<IFileSystem> cardFs, handle, partitionId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var mountNameGenerator = new GameCardCommonMountNameGenerator(handle, partitionId);
|
||||
|
||||
return fs.Register(mountName, cardFs, mountNameGenerator);
|
||||
return fs.Register(mountName, cardFs.Target, mountNameGenerator);
|
||||
}
|
||||
|
||||
private class GameCardCommonMountNameGenerator : ICommonMountNameGenerator
|
||||
|
@ -1,6 +1,6 @@
|
||||
using LibHac.Common;
|
||||
using LibHac.FsSrv;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Spl;
|
||||
using FsRightsId = LibHac.Fs.RightsId;
|
||||
@ -23,10 +23,10 @@ namespace LibHac.Fs.Shim
|
||||
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
Result rc = FsPath.FromSpan(out FsPath fsPath, path);
|
||||
Result rc = FspPath.FromSpan(out FspPath sfPath, path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return fsProxy.GetRightsIdByPath(out rightsId, ref fsPath);
|
||||
return fsProxy.GetRightsIdByPath(out rightsId, in sfPath);
|
||||
}
|
||||
|
||||
public static Result GetRightsId(this FileSystemClient fs, out FsRightsId rightsId, out byte keyGeneration, U8Span path)
|
||||
@ -36,24 +36,24 @@ namespace LibHac.Fs.Shim
|
||||
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
Result rc = FsPath.FromSpan(out FsPath fsPath, path);
|
||||
Result rc = FspPath.FromSpan(out FspPath sfPath, path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return fsProxy.GetRightsIdAndKeyGenerationByPath(out rightsId, out keyGeneration, ref fsPath);
|
||||
return fsProxy.GetRightsIdAndKeyGenerationByPath(out rightsId, out keyGeneration, in sfPath);
|
||||
}
|
||||
|
||||
public static Result RegisterExternalKey(this FileSystemClient fs, ref FsRightsId rightsId, ref AccessKey key)
|
||||
public static Result RegisterExternalKey(this FileSystemClient fs, in FsRightsId rightsId, in AccessKey key)
|
||||
{
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
return fsProxy.RegisterExternalKey(ref rightsId, ref key);
|
||||
return fsProxy.RegisterExternalKey(in rightsId, in key);
|
||||
}
|
||||
|
||||
public static Result UnregisterExternalKey(this FileSystemClient fs, ref FsRightsId rightsId)
|
||||
{
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
return fsProxy.UnregisterExternalKey(ref rightsId);
|
||||
return fsProxy.UnregisterExternalKey(in rightsId);
|
||||
}
|
||||
|
||||
public static Result UnregisterAllExternalKey(this FileSystemClient fs)
|
||||
|
@ -41,10 +41,10 @@ namespace LibHac.Fs.Shim
|
||||
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
rc = fsProxy.OpenSdCardFileSystem(out IFileSystem fileSystem);
|
||||
rc = fsProxy.OpenSdCardFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return fs.Register(mountName, fileSystem);
|
||||
return fs.Register(mountName, fileSystem.Target);
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,11 +61,11 @@ namespace LibHac.Fs.Shim
|
||||
return isInserted;
|
||||
}
|
||||
|
||||
public static Result SetSdCardEncryptionSeed(this FileSystemClient fs, ref EncryptionSeed seed)
|
||||
public static Result SetSdCardEncryptionSeed(this FileSystemClient fs, in EncryptionSeed seed)
|
||||
{
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
Result rc = fsProxy.SetSdCardEncryptionSeed(ref seed);
|
||||
Result rc = fsProxy.SetSdCardEncryptionSeed(in seed);
|
||||
if (rc.IsFailure()) throw new HorizonResultException(rc, "Abort");
|
||||
|
||||
return Result.Success;
|
||||
|
@ -17,7 +17,8 @@ namespace LibHac.FsSrv
|
||||
_processId = processId;
|
||||
}
|
||||
|
||||
public Result OpenBisFileSystem(out IFileSystem fileSystem, in FspPath rootPath, BisPartitionId partitionId)
|
||||
public Result OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, in FspPath rootPath,
|
||||
BisPartitionId partitionId)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
@ -49,7 +50,8 @@ namespace LibHac.FsSrv
|
||||
var normalizer = new PathNormalizer(rootPath, PathNormalizer.Option.AcceptEmpty);
|
||||
if (normalizer.Result.IsFailure()) return normalizer.Result;
|
||||
|
||||
rc = _serviceImpl.OpenBisFileSystem(out IFileSystem bisFs, normalizer.Path, partitionId);
|
||||
rc = _serviceImpl.OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystem> bisFs, normalizer.Path,
|
||||
partitionId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
fileSystem = bisFs;
|
||||
@ -84,7 +86,7 @@ namespace LibHac.FsSrv
|
||||
return _serviceImpl.DeleteAllPaddingFiles();
|
||||
}
|
||||
|
||||
public Result OpenGameCardFileSystem(out IFileSystem fileSystem, GameCardHandle handle,
|
||||
public Result OpenGameCardFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, GameCardHandle handle,
|
||||
GameCardPartition partitionId)
|
||||
{
|
||||
fileSystem = default;
|
||||
@ -95,14 +97,14 @@ namespace LibHac.FsSrv
|
||||
if (!programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountGameCard).CanRead)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
rc = _serviceImpl.OpenGameCardFileSystem(out IFileSystem gcFs, handle, partitionId);
|
||||
rc = _serviceImpl.OpenGameCardFileSystem(out ReferenceCountedDisposable<IFileSystem> gcFs, handle, partitionId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
fileSystem = gcFs;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result OpenSdCardFileSystem(out IFileSystem fileSystem)
|
||||
public Result OpenSdCardFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
@ -114,7 +116,7 @@ namespace LibHac.FsSrv
|
||||
if (!accessibility.CanRead || !accessibility.CanWrite)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
rc = _serviceImpl.OpenSdCardProxyFileSystem(out IFileSystem sdCardFs);
|
||||
rc = _serviceImpl.OpenSdCardProxyFileSystem(out ReferenceCountedDisposable<IFileSystem> sdCardFs);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
fileSystem = sdCardFs;
|
||||
@ -148,7 +150,8 @@ namespace LibHac.FsSrv
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result OpenImageDirectoryFileSystem(out IFileSystem fileSystem, ImageDirectoryId directoryId)
|
||||
public Result OpenImageDirectoryFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
ImageDirectoryId directoryId)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
@ -166,13 +169,17 @@ namespace LibHac.FsSrv
|
||||
int id;
|
||||
switch (directoryId)
|
||||
{
|
||||
case ImageDirectoryId.Nand: id = 0; break;
|
||||
case ImageDirectoryId.SdCard: id = 1; break;
|
||||
case ImageDirectoryId.Nand:
|
||||
id = 0;
|
||||
break;
|
||||
case ImageDirectoryId.SdCard:
|
||||
id = 1;
|
||||
break;
|
||||
default:
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
}
|
||||
|
||||
rc = _serviceImpl.OpenBaseFileSystem(out IFileSystem imageFs, id);
|
||||
rc = _serviceImpl.OpenBaseFileSystem(out ReferenceCountedDisposable<IFileSystem> imageFs, id);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
fileSystem = imageFs;
|
||||
|
@ -35,14 +35,20 @@ namespace LibHac.FsSrv
|
||||
public ProgramRegistryImpl ProgramRegistry;
|
||||
}
|
||||
|
||||
public Result OpenBaseFileSystem(out IFileSystem fileSystem, int fileSystemId)
|
||||
public Result OpenBaseFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, int fileSystemId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenBisFileSystem(out IFileSystem fileSystem, U8Span rootPath, BisPartitionId partitionId)
|
||||
public Result OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, U8Span rootPath, BisPartitionId partitionId)
|
||||
{
|
||||
return _config.BisFileSystemCreator.Create(out fileSystem, rootPath.ToString(), partitionId);
|
||||
fileSystem = default;
|
||||
|
||||
Result rc = _config.BisFileSystemCreator.Create(out IFileSystem fs, rootPath.ToString(), partitionId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
fileSystem = new ReferenceCountedDisposable<IFileSystem>(fs);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result CreatePaddingFile(long size)
|
||||
@ -55,7 +61,7 @@ namespace LibHac.FsSrv
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenGameCardFileSystem(out IFileSystem fileSystem, GameCardHandle handle,
|
||||
public Result OpenGameCardFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, GameCardHandle handle,
|
||||
GameCardPartition partitionId)
|
||||
{
|
||||
Result rc;
|
||||
@ -74,14 +80,21 @@ namespace LibHac.FsSrv
|
||||
return rc;
|
||||
}
|
||||
|
||||
public Result OpenSdCardProxyFileSystem(out IFileSystem fileSystem)
|
||||
public Result OpenSdCardProxyFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem)
|
||||
{
|
||||
return OpenSdCardProxyFileSystem(out fileSystem, false);
|
||||
}
|
||||
|
||||
public Result OpenSdCardProxyFileSystem(out IFileSystem fileSystem, bool isCaseSensitive)
|
||||
public Result OpenSdCardProxyFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, bool isCaseSensitive)
|
||||
{
|
||||
return _config.SdCardFileSystemCreator.Create(out fileSystem, isCaseSensitive);
|
||||
fileSystem = default;
|
||||
|
||||
// Todo: Shared
|
||||
Result rc = _config.SdCardFileSystemCreator.Create(out IFileSystem fs, isCaseSensitive);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
fileSystem = new ReferenceCountedDisposable<IFileSystem>(fs);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result FormatSdCardProxyFileSystem()
|
||||
|
@ -1,6 +1,8 @@
|
||||
using System.Diagnostics;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSrv.Impl;
|
||||
|
||||
namespace LibHac.FsSrv.Creators
|
||||
{
|
||||
@ -85,6 +87,43 @@ namespace LibHac.FsSrv.Creators
|
||||
return Util.CreateSubFileSystemImpl(out fileSystem, subFileSystem, rootPath);
|
||||
}
|
||||
|
||||
public Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, U8Span rootPath, BisPartitionId partitionId)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
if (!IsValidPartitionId(partitionId)) return ResultFs.InvalidArgument.Log();
|
||||
if (rootPath.IsNull()) return ResultFs.NullptrArgument.Log();
|
||||
|
||||
if (Config.TryGetFileSystem(out IFileSystem fs, partitionId))
|
||||
{
|
||||
fileSystem = new ReferenceCountedDisposable<IFileSystem>(fs);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
if (Config.RootFileSystem == null)
|
||||
{
|
||||
return ResultFs.PreconditionViolation.Log();
|
||||
}
|
||||
|
||||
var partitionPath = GetPartitionPath(partitionId).ToU8String();
|
||||
|
||||
// Todo: Store shared file systems
|
||||
using var sharedRootFs = new ReferenceCountedDisposable<IFileSystem>(Config.RootFileSystem);
|
||||
|
||||
Result rc = Utility.WrapSubDirectory(out ReferenceCountedDisposable<IFileSystem> partitionFileSystem,
|
||||
sharedRootFs, partitionPath, true);
|
||||
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (rootPath.IsEmpty())
|
||||
{
|
||||
fileSystem = partitionFileSystem.AddReference();
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
return Utility.CreateSubDirectoryFileSystem(out fileSystem, partitionFileSystem, rootPath);
|
||||
}
|
||||
|
||||
public Result CreateFatFileSystem(out IFileSystem fileSystem, BisPartitionId partitionId)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
@ -14,6 +14,7 @@ namespace LibHac.FsSrv.Creators
|
||||
StorageCreator = storageCreator;
|
||||
GameCard = gameCard;
|
||||
}
|
||||
|
||||
public Result Create(out IFileSystem fileSystem, GameCardHandle handle, GameCardPartition partitionType)
|
||||
{
|
||||
// Use the old xci code temporarily
|
||||
@ -31,5 +32,24 @@ namespace LibHac.FsSrv.Creators
|
||||
fileSystem = xci.OpenPartition((XciPartitionType)partitionType);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, GameCardHandle handle, GameCardPartition partitionType)
|
||||
{
|
||||
// Use the old xci code temporarily
|
||||
|
||||
fileSystem = default;
|
||||
|
||||
Result rc = GameCard.GetXci(out Xci xci, handle);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (!xci.HasPartition((XciPartitionType)partitionType))
|
||||
{
|
||||
return ResultFs.PartitionNotFound.Log();
|
||||
}
|
||||
|
||||
XciPartition fs = xci.OpenPartition((XciPartitionType)partitionType);
|
||||
fileSystem = new ReferenceCountedDisposable<IFileSystem>(fs);
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,27 @@ namespace LibHac.FsSrv.Creators
|
||||
KeySet.SetSdSeed(encryptionSeed.ToArray());
|
||||
|
||||
encryptedFileSystem = new AesXtsFileSystem(baseFileSystem,
|
||||
KeySet.SdCardEncryptionKeys[(int) keyId].DataRo.ToArray(), 0x4000);
|
||||
KeySet.SdCardEncryptionKeys[(int)keyId].DataRo.ToArray(), 0x4000);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result Create(out ReferenceCountedDisposable<IFileSystem> encryptedFileSystem, ReferenceCountedDisposable<IFileSystem> baseFileSystem,
|
||||
EncryptedFsKeyId keyId, in EncryptionSeed encryptionSeed)
|
||||
{
|
||||
encryptedFileSystem = default;
|
||||
|
||||
if (keyId < EncryptedFsKeyId.Save || keyId > EncryptedFsKeyId.CustomStorage)
|
||||
{
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
}
|
||||
|
||||
// todo: "proper" key generation instead of a lazy hack
|
||||
KeySet.SetSdSeed(encryptionSeed.Value);
|
||||
|
||||
// Todo: pass ReferenceCountedDisposable to AesXtsFileSystem
|
||||
var fs = new AesXtsFileSystem(baseFileSystem.AddReference().Target, KeySet.SdCardEncryptionKeys[(int)keyId].DataRo.ToArray(), 0x4000);
|
||||
encryptedFileSystem = new ReferenceCountedDisposable<IFileSystem>(fs);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
@ -1,11 +1,14 @@
|
||||
using LibHac.Fs;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
|
||||
namespace LibHac.FsSrv.Creators
|
||||
{
|
||||
public interface IBuiltInStorageFileSystemCreator
|
||||
{
|
||||
// Todo: Remove raw IFileSystem overload
|
||||
Result Create(out IFileSystem fileSystem, string rootPath, BisPartitionId partitionId);
|
||||
Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, U8Span rootPath, BisPartitionId partitionId);
|
||||
Result CreateFatFileSystem(out IFileSystem fileSystem, BisPartitionId partitionId);
|
||||
Result SetBisRootForHost(BisPartitionId partitionId, string rootPath);
|
||||
}
|
||||
|
@ -1,12 +1,18 @@
|
||||
using System;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
|
||||
namespace LibHac.FsSrv.Creators
|
||||
{
|
||||
public interface IEncryptedFileSystemCreator
|
||||
{
|
||||
// Todo: remove the function using raw IFileSystems
|
||||
Result Create(out IFileSystem encryptedFileSystem, IFileSystem baseFileSystem, EncryptedFsKeyId keyId,
|
||||
ReadOnlySpan<byte> encryptionSeed);
|
||||
|
||||
Result Create(out ReferenceCountedDisposable<IFileSystem> encryptedFileSystem,
|
||||
ReferenceCountedDisposable<IFileSystem> baseFileSystem, EncryptedFsKeyId keyId,
|
||||
in EncryptionSeed encryptionSeed);
|
||||
}
|
||||
|
||||
public enum EncryptedFsKeyId
|
||||
|
@ -6,5 +6,6 @@ namespace LibHac.FsSrv.Creators
|
||||
public interface IGameCardFileSystemCreator
|
||||
{
|
||||
Result Create(out IFileSystem fileSystem, GameCardHandle handle, GameCardPartition partitionType);
|
||||
Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, GameCardHandle handle, GameCardPartition partitionType);
|
||||
}
|
||||
}
|
@ -5,6 +5,8 @@ namespace LibHac.FsSrv.Creators
|
||||
{
|
||||
public interface IPartitionFileSystemCreator
|
||||
{
|
||||
// Todo: Remove non-shared overload
|
||||
Result Create(out IFileSystem fileSystem, IStorage pFsStorage);
|
||||
Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, ReferenceCountedDisposable<IStorage> pFsStorage);
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,6 @@ namespace LibHac.FsSrv.Creators
|
||||
{
|
||||
public interface IRomFileSystemCreator
|
||||
{
|
||||
Result Create(out IFileSystem fileSystem, IStorage romFsStorage);
|
||||
Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, ReferenceCountedDisposable<IStorage> romFsStorage);
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,8 @@ namespace LibHac.FsSrv.Creators
|
||||
{
|
||||
public interface IStorageOnNcaCreator
|
||||
{
|
||||
Result Create(out IStorage storage, out NcaFsHeader fsHeader, Nca nca, int fsIndex, bool isCodeFs);
|
||||
Result CreateWithPatch(out IStorage storage, out NcaFsHeader fsHeader, Nca baseNca, Nca patchNca, int fsIndex, bool isCodeFs);
|
||||
Result Create(out ReferenceCountedDisposable<IStorage> storage, out NcaFsHeader fsHeader, Nca nca, int fsIndex, bool isCodeFs);
|
||||
Result CreateWithPatch(out ReferenceCountedDisposable<IStorage> storage, out NcaFsHeader fsHeader, Nca baseNca, Nca patchNca, int fsIndex, bool isCodeFs);
|
||||
Result OpenNca(out Nca nca, IStorage ncaStorage);
|
||||
Result VerifyAcidSignature(IFileSystem codeFileSystem, Nca nca);
|
||||
}
|
||||
|
@ -5,7 +5,11 @@ namespace LibHac.FsSrv.Creators
|
||||
{
|
||||
public interface ISubDirectoryFileSystemCreator
|
||||
{
|
||||
// Todo: Remove the raw IFileSystem overloads
|
||||
Result Create(out IFileSystem subDirFileSystem, IFileSystem baseFileSystem, U8Span path);
|
||||
Result Create(out IFileSystem subDirFileSystem, IFileSystem baseFileSystem, U8Span path, bool preserveUnc);
|
||||
|
||||
Result Create(out ReferenceCountedDisposable<IFileSystem> subDirFileSystem, ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path);
|
||||
Result Create(out ReferenceCountedDisposable<IFileSystem> subDirFileSystem, ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path, bool preserveUnc);
|
||||
}
|
||||
}
|
@ -5,7 +5,9 @@ namespace LibHac.FsSrv.Creators
|
||||
{
|
||||
public interface ITargetManagerFileSystemCreator
|
||||
{
|
||||
// Todo: Remove raw IFilesystem function
|
||||
Result Create(out IFileSystem fileSystem, bool openCaseSensitive);
|
||||
Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, bool openCaseSensitive);
|
||||
Result GetCaseSensitivePath(out bool isSuccess, Span<byte> path);
|
||||
}
|
||||
}
|
@ -21,5 +21,20 @@ namespace LibHac.FsSrv.Creators
|
||||
fileSystem = partitionFs;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, ReferenceCountedDisposable<IStorage> pFsStorage)
|
||||
{
|
||||
var partitionFs = new PartitionFileSystemCore<StandardEntry>();
|
||||
|
||||
Result rc = partitionFs.Initialize(pFsStorage);
|
||||
if (rc.IsFailure())
|
||||
{
|
||||
fileSystem = default;
|
||||
return rc;
|
||||
}
|
||||
|
||||
fileSystem = new ReferenceCountedDisposable<IFileSystem>(partitionFs);
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,9 +7,10 @@ namespace LibHac.FsSrv.Creators
|
||||
public class RomFileSystemCreator : IRomFileSystemCreator
|
||||
{
|
||||
// todo: Implement properly
|
||||
public Result Create(out IFileSystem fileSystem, IStorage romFsStorage)
|
||||
public Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, ReferenceCountedDisposable<IStorage> romFsStorage)
|
||||
{
|
||||
fileSystem = new RomFsFileSystem(romFsStorage);
|
||||
// Todo: Properly use shared references
|
||||
fileSystem = new ReferenceCountedDisposable<IFileSystem>(new RomFsFileSystem(romFsStorage.AddReference().Target));
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,8 @@ namespace LibHac.FsSrv.Creators
|
||||
}
|
||||
|
||||
// todo: Implement NcaReader and other Nca classes
|
||||
public Result Create(out IStorage storage, out NcaFsHeader fsHeader, Nca nca, int fsIndex, bool isCodeFs)
|
||||
public Result Create(out ReferenceCountedDisposable<IStorage> storage, out NcaFsHeader fsHeader, Nca nca,
|
||||
int fsIndex, bool isCodeFs)
|
||||
{
|
||||
storage = default;
|
||||
fsHeader = default;
|
||||
@ -40,13 +41,14 @@ namespace LibHac.FsSrv.Creators
|
||||
}
|
||||
}
|
||||
|
||||
storage = storageTemp;
|
||||
storage = new ReferenceCountedDisposable<IStorage>(storageTemp);
|
||||
fsHeader = nca.GetFsHeader(fsIndex);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result CreateWithPatch(out IStorage storage, out NcaFsHeader fsHeader, Nca baseNca, Nca patchNca, int fsIndex, bool isCodeFs)
|
||||
public Result CreateWithPatch(out ReferenceCountedDisposable<IStorage> storage, out NcaFsHeader fsHeader,
|
||||
Nca baseNca, Nca patchNca, int fsIndex, bool isCodeFs)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
@ -22,5 +22,31 @@ namespace LibHac.FsSrv.Creators
|
||||
subDirFileSystem = fs;
|
||||
return rc;
|
||||
}
|
||||
|
||||
public Result Create(out ReferenceCountedDisposable<IFileSystem> subDirFileSystem,
|
||||
ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path)
|
||||
{
|
||||
return Create(out subDirFileSystem, baseFileSystem, path, false);
|
||||
}
|
||||
|
||||
public Result Create(out ReferenceCountedDisposable<IFileSystem> subDirFileSystem,
|
||||
ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path, bool preserveUnc)
|
||||
{
|
||||
subDirFileSystem = default;
|
||||
|
||||
// Verify the sub-path exists
|
||||
Result rc = baseFileSystem.Target.OpenDirectory(out IDirectory _, path, OpenDirectoryMode.Directory);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Initialize the SubdirectoryFileSystem
|
||||
var subDir = new SubdirectoryFileSystem(baseFileSystem, preserveUnc);
|
||||
using var subDirShared = new ReferenceCountedDisposable<SubdirectoryFileSystem>(subDir);
|
||||
|
||||
rc = subDirShared.Target.Initialize(path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
subDirFileSystem = subDirShared.AddReference<IFileSystem>();
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,11 @@ namespace LibHac.FsSrv.Creators
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, bool openCaseSensitive)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result GetCaseSensitivePath(out bool isSuccess, Span<byte> path)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
|
@ -5,7 +5,8 @@ namespace LibHac.FsSrv
|
||||
public class FileSystemProxyConfiguration
|
||||
{
|
||||
public FileSystemCreators FsCreatorInterfaces { get; set; }
|
||||
public BaseFileSystemServiceImpl BaseFileSystemServiceImpl { get; set; }
|
||||
public ProgramRegistryServiceImpl ProgramRegistryServiceImpl { get; set; }
|
||||
public BaseFileSystemServiceImpl BaseFileSystemService { get; set; }
|
||||
public NcaFileSystemServiceImpl NcaFileSystemService { get; set; }
|
||||
public ProgramRegistryServiceImpl ProgramRegistryService { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,10 @@
|
||||
using System;
|
||||
using System.Buffers.Text;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Fs.Shim;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.FsSrv.Creators;
|
||||
using LibHac.FsSystem.NcaUtils;
|
||||
using LibHac.Spl;
|
||||
using LibHac.Util;
|
||||
using RightsId = LibHac.Fs.RightsId;
|
||||
|
||||
namespace LibHac.FsSrv
|
||||
{
|
||||
@ -26,7 +20,6 @@ namespace LibHac.FsSrv
|
||||
private byte[] SdEncryptionSeed { get; } = new byte[0x10];
|
||||
|
||||
private const string NintendoDirectoryName = "Nintendo";
|
||||
private const string ContentDirectoryName = "Contents";
|
||||
|
||||
private GlobalAccessLogMode LogMode { get; set; }
|
||||
public bool IsSdCardAccessible { get; set; }
|
||||
@ -36,590 +29,11 @@ namespace LibHac.FsSrv
|
||||
public FileSystemProxyCoreImpl(FileSystemProxyConfiguration config, ExternalKeySet externalKeys, IDeviceOperator deviceOperator)
|
||||
{
|
||||
Config = config;
|
||||
ProgramRegistry = new ProgramRegistryImpl(Config.ProgramRegistryServiceImpl);
|
||||
ProgramRegistry = new ProgramRegistryImpl(Config.ProgramRegistryService);
|
||||
ExternalKeys = externalKeys ?? new ExternalKeySet();
|
||||
DeviceOperator = deviceOperator;
|
||||
}
|
||||
|
||||
public Result OpenFileSystem(out IFileSystem fileSystem, U8Span path, FileSystemProxyType type,
|
||||
bool canMountSystemDataPrivate, ulong programId)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
// Get a reference to the path that will be advanced as each part of the path is parsed
|
||||
U8Span currentPath = path.Slice(0, StringUtils.GetLength(path));
|
||||
|
||||
// Open the root filesystem based on the path's mount name
|
||||
Result rc = OpenFileSystemFromMountName(ref currentPath, out IFileSystem baseFileSystem, out bool shouldContinue,
|
||||
out MountNameInfo mountNameInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Don't continue if the rest of the path is empty
|
||||
if (!shouldContinue)
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
|
||||
if (type == FileSystemProxyType.Logo && mountNameInfo.IsGameCard)
|
||||
{
|
||||
rc = OpenGameCardFileSystem(out fileSystem, new GameCardHandle(mountNameInfo.GcHandle),
|
||||
GameCardPartition.Logo);
|
||||
|
||||
if (rc.IsSuccess())
|
||||
return Result.Success;
|
||||
|
||||
if (!ResultFs.PartitionNotFound.Includes(rc))
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = IsContentPathDir(ref currentPath, out bool isDirectory);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (isDirectory)
|
||||
{
|
||||
if (!mountNameInfo.IsHostFs)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
if (type == FileSystemProxyType.Manual)
|
||||
{
|
||||
rc = TryOpenCaseSensitiveContentDirectory(out IFileSystem manualFileSystem, baseFileSystem, currentPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
fileSystem = new ReadOnlyFileSystem(manualFileSystem);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
return TryOpenContentDirectory(currentPath, out fileSystem, baseFileSystem, type, true);
|
||||
}
|
||||
|
||||
rc = TryOpenNsp(ref currentPath, out IFileSystem nspFileSystem, baseFileSystem);
|
||||
|
||||
if (rc.IsSuccess())
|
||||
{
|
||||
// Must be the end of the path to open Application Package FS type
|
||||
if (currentPath.Length == 0 || currentPath[0] == 0)
|
||||
{
|
||||
if (type == FileSystemProxyType.Package)
|
||||
{
|
||||
fileSystem = nspFileSystem;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
}
|
||||
|
||||
baseFileSystem = nspFileSystem;
|
||||
}
|
||||
|
||||
if (!mountNameInfo.CanMountNca)
|
||||
{
|
||||
return ResultFs.InvalidNcaMountPoint.Log();
|
||||
}
|
||||
|
||||
ulong openProgramId = mountNameInfo.IsHostFs ? ulong.MaxValue : programId;
|
||||
|
||||
rc = TryOpenNca(ref currentPath, out Nca nca, baseFileSystem, openProgramId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = OpenNcaStorage(out IStorage ncaSectionStorage, nca, out NcaFormatType fsType, type,
|
||||
mountNameInfo.IsGameCard, canMountSystemDataPrivate);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
switch (fsType)
|
||||
{
|
||||
case NcaFormatType.Romfs:
|
||||
return FsCreators.RomFileSystemCreator.Create(out fileSystem, ncaSectionStorage);
|
||||
case NcaFormatType.Pfs0:
|
||||
return FsCreators.PartitionFileSystemCreator.Create(out fileSystem, ncaSectionStorage);
|
||||
default:
|
||||
return ResultFs.InvalidNcaFsType.Log();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores info obtained by parsing a common mount name.
|
||||
/// </summary>
|
||||
private struct MountNameInfo
|
||||
{
|
||||
public bool IsGameCard;
|
||||
public int GcHandle;
|
||||
public bool IsHostFs;
|
||||
public bool CanMountNca;
|
||||
}
|
||||
|
||||
private Result OpenFileSystemFromMountName(ref U8Span path, out IFileSystem fileSystem, out bool shouldContinue,
|
||||
out MountNameInfo info)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
info = new MountNameInfo();
|
||||
shouldContinue = true;
|
||||
|
||||
if (StringUtils.Compare(path, CommonMountNames.GameCardFileSystemMountName,
|
||||
CommonMountNames.GameCardFileSystemMountName.Length) == 0)
|
||||
{
|
||||
path = path.Slice(CommonMountNames.GameCardFileSystemMountName.Length);
|
||||
|
||||
if (StringUtils.GetLength(path.Value, 9) < 9)
|
||||
return ResultFs.InvalidPath.Log();
|
||||
|
||||
GameCardPartition partition;
|
||||
switch ((char)path[0])
|
||||
{
|
||||
case CommonMountNames.GameCardFileSystemMountNameUpdateSuffix:
|
||||
partition = GameCardPartition.Update;
|
||||
break;
|
||||
case CommonMountNames.GameCardFileSystemMountNameNormalSuffix:
|
||||
partition = GameCardPartition.Normal;
|
||||
break;
|
||||
case CommonMountNames.GameCardFileSystemMountNameSecureSuffix:
|
||||
partition = GameCardPartition.Secure;
|
||||
break;
|
||||
default:
|
||||
return ResultFs.InvalidPath.Log();
|
||||
}
|
||||
|
||||
path = path.Slice(1);
|
||||
bool handleParsed = Utf8Parser.TryParse(path, out int handle, out int bytesConsumed);
|
||||
|
||||
if (!handleParsed || handle == -1 || bytesConsumed != 8)
|
||||
return ResultFs.InvalidPath.Log();
|
||||
|
||||
path = path.Slice(8);
|
||||
|
||||
Result rc = OpenGameCardFileSystem(out fileSystem, new GameCardHandle(handle), partition);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
info.GcHandle = handle;
|
||||
info.IsGameCard = true;
|
||||
info.CanMountNca = true;
|
||||
}
|
||||
|
||||
else if (StringUtils.Compare(path, CommonMountNames.ContentStorageSystemMountName,
|
||||
CommonMountNames.ContentStorageSystemMountName.Length) == 0)
|
||||
{
|
||||
path = path.Slice(CommonMountNames.ContentStorageSystemMountName.Length);
|
||||
|
||||
Result rc = OpenContentStorageFileSystem(out fileSystem, ContentStorageId.System);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
info.CanMountNca = true;
|
||||
}
|
||||
|
||||
else if (StringUtils.Compare(path, CommonMountNames.ContentStorageUserMountName,
|
||||
CommonMountNames.ContentStorageUserMountName.Length) == 0)
|
||||
{
|
||||
path = path.Slice(CommonMountNames.ContentStorageUserMountName.Length);
|
||||
|
||||
Result rc = OpenContentStorageFileSystem(out fileSystem, ContentStorageId.User);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
info.CanMountNca = true;
|
||||
}
|
||||
|
||||
else if (StringUtils.Compare(path, CommonMountNames.ContentStorageSdCardMountName,
|
||||
CommonMountNames.ContentStorageSdCardMountName.Length) == 0)
|
||||
{
|
||||
path = path.Slice(CommonMountNames.ContentStorageSdCardMountName.Length);
|
||||
|
||||
Result rc = OpenContentStorageFileSystem(out fileSystem, ContentStorageId.SdCard);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
info.CanMountNca = true;
|
||||
}
|
||||
|
||||
else if (StringUtils.Compare(path, CommonMountNames.BisCalibrationFilePartitionMountName,
|
||||
CommonMountNames.BisCalibrationFilePartitionMountName.Length) == 0)
|
||||
{
|
||||
path = path.Slice(CommonMountNames.BisCalibrationFilePartitionMountName.Length);
|
||||
|
||||
Result rc = OpenBisFileSystem(out fileSystem, string.Empty, BisPartitionId.CalibrationFile);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
else if (StringUtils.Compare(path, CommonMountNames.BisSafeModePartitionMountName,
|
||||
CommonMountNames.BisSafeModePartitionMountName.Length) == 0)
|
||||
{
|
||||
path = path.Slice(CommonMountNames.BisSafeModePartitionMountName.Length);
|
||||
|
||||
Result rc = OpenBisFileSystem(out fileSystem, string.Empty, BisPartitionId.SafeMode);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
else if (StringUtils.Compare(path, CommonMountNames.BisUserPartitionMountName,
|
||||
CommonMountNames.BisUserPartitionMountName.Length) == 0)
|
||||
{
|
||||
path = path.Slice(CommonMountNames.BisUserPartitionMountName.Length);
|
||||
|
||||
Result rc = OpenBisFileSystem(out fileSystem, string.Empty, BisPartitionId.User);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
else if (StringUtils.Compare(path, CommonMountNames.BisSystemPartitionMountName,
|
||||
CommonMountNames.BisSystemPartitionMountName.Length) == 0)
|
||||
{
|
||||
path = path.Slice(CommonMountNames.BisSystemPartitionMountName.Length);
|
||||
|
||||
Result rc = OpenBisFileSystem(out fileSystem, string.Empty, BisPartitionId.System);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
else if (StringUtils.Compare(path, CommonMountNames.SdCardFileSystemMountName,
|
||||
CommonMountNames.SdCardFileSystemMountName.Length) == 0)
|
||||
{
|
||||
path = path.Slice(CommonMountNames.SdCardFileSystemMountName.Length);
|
||||
|
||||
Result rc = OpenSdCardFileSystem(out fileSystem);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
else if (StringUtils.Compare(path, CommonMountNames.HostRootFileSystemMountName,
|
||||
CommonMountNames.HostRootFileSystemMountName.Length) == 0)
|
||||
{
|
||||
path = path.Slice(CommonMountNames.HostRootFileSystemMountName.Length);
|
||||
|
||||
info.IsHostFs = true;
|
||||
info.CanMountNca = true;
|
||||
|
||||
Result rc = OpenHostFileSystem(out fileSystem, U8Span.Empty, openCaseSensitive: false);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
else if (StringUtils.Compare(path, CommonMountNames.RegisteredUpdatePartitionMountName,
|
||||
CommonMountNames.RegisteredUpdatePartitionMountName.Length) == 0)
|
||||
{
|
||||
path = path.Slice(CommonMountNames.RegisteredUpdatePartitionMountName.Length);
|
||||
|
||||
info.CanMountNca = true;
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
if (StringUtils.GetLength(path, FsPath.MaxLength) == 0)
|
||||
{
|
||||
shouldContinue = false;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private Result IsContentPathDir(ref U8Span path, out bool isDirectory)
|
||||
{
|
||||
isDirectory = default;
|
||||
|
||||
ReadOnlySpan<byte> mountSeparator = new[] { (byte)':', (byte)'/' };
|
||||
|
||||
if (StringUtils.Compare(mountSeparator, path, mountSeparator.Length) != 0)
|
||||
{
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
path = path.Slice(1);
|
||||
int pathLen = StringUtils.GetLength(path);
|
||||
|
||||
if (path[pathLen - 1] == '/')
|
||||
{
|
||||
isDirectory = true;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
// Now make sure the path has a content file extension
|
||||
if (pathLen < 5)
|
||||
return ResultFs.PathNotFound.Log();
|
||||
|
||||
ReadOnlySpan<byte> fileExtension = path.Value.Slice(pathLen - 4);
|
||||
|
||||
ReadOnlySpan<byte> ncaExtension = new[] { (byte)'.', (byte)'n', (byte)'c', (byte)'a' };
|
||||
ReadOnlySpan<byte> nspExtension = new[] { (byte)'.', (byte)'n', (byte)'s', (byte)'p' };
|
||||
|
||||
if (StringUtils.CompareCaseInsensitive(fileExtension, ncaExtension) == 0 ||
|
||||
StringUtils.CompareCaseInsensitive(fileExtension, nspExtension) == 0)
|
||||
{
|
||||
isDirectory = false;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
private Result TryOpenContentDirectory(U8Span path, out IFileSystem contentFileSystem,
|
||||
IFileSystem baseFileSystem, FileSystemProxyType fsType, bool preserveUnc)
|
||||
{
|
||||
contentFileSystem = default;
|
||||
|
||||
FsPath fullPath;
|
||||
unsafe { _ = &fullPath; } // workaround for CS0165
|
||||
|
||||
Result rc = FsCreators.SubDirectoryFileSystemCreator.Create(out IFileSystem subDirFs,
|
||||
baseFileSystem, path, preserveUnc);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return OpenSubDirectoryForFsType(out contentFileSystem, subDirFs, fsType);
|
||||
}
|
||||
|
||||
private Result TryOpenCaseSensitiveContentDirectory(out IFileSystem contentFileSystem,
|
||||
IFileSystem baseFileSystem, U8Span path)
|
||||
{
|
||||
contentFileSystem = default;
|
||||
FsPath fullPath;
|
||||
unsafe { _ = &fullPath; } // workaround for CS0165
|
||||
|
||||
var sb = new U8StringBuilder(fullPath.Str);
|
||||
sb.Append(path)
|
||||
.Append(new[] { (byte)'d', (byte)'a', (byte)'t', (byte)'a', (byte)'/' });
|
||||
|
||||
if (sb.Overflowed)
|
||||
return ResultFs.TooLongPath.Log();
|
||||
|
||||
Result rc = FsCreators.TargetManagerFileSystemCreator.GetCaseSensitivePath(out bool success, fullPath.Str);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Reopen the host filesystem as case sensitive
|
||||
if (success)
|
||||
{
|
||||
baseFileSystem.Dispose();
|
||||
|
||||
rc = OpenHostFileSystem(out baseFileSystem, U8Span.Empty, openCaseSensitive: true);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
return FsCreators.SubDirectoryFileSystemCreator.Create(out contentFileSystem, baseFileSystem, fullPath);
|
||||
}
|
||||
|
||||
private Result OpenSubDirectoryForFsType(out IFileSystem fileSystem, IFileSystem baseFileSystem,
|
||||
FileSystemProxyType fsType)
|
||||
{
|
||||
fileSystem = default;
|
||||
ReadOnlySpan<byte> dirName;
|
||||
|
||||
// Get the name of the subdirectory for the filesystem type
|
||||
switch (fsType)
|
||||
{
|
||||
case FileSystemProxyType.Package:
|
||||
fileSystem = baseFileSystem;
|
||||
return Result.Success;
|
||||
|
||||
case FileSystemProxyType.Code:
|
||||
dirName = new[] { (byte)'/', (byte)'c', (byte)'o', (byte)'d', (byte)'e', (byte)'/' };
|
||||
break;
|
||||
case FileSystemProxyType.Rom:
|
||||
case FileSystemProxyType.Control:
|
||||
case FileSystemProxyType.Manual:
|
||||
case FileSystemProxyType.Meta:
|
||||
case FileSystemProxyType.RegisteredUpdate:
|
||||
dirName = new[] { (byte)'/', (byte)'d', (byte)'a', (byte)'t', (byte)'a', (byte)'/' };
|
||||
break;
|
||||
case FileSystemProxyType.Logo:
|
||||
dirName = new[] { (byte)'/', (byte)'l', (byte)'o', (byte)'g', (byte)'o', (byte)'/' };
|
||||
break;
|
||||
|
||||
default:
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
}
|
||||
|
||||
// Open the subdirectory filesystem
|
||||
Result rc = FsCreators.SubDirectoryFileSystemCreator.Create(out IFileSystem subDirFs, baseFileSystem,
|
||||
new U8Span(dirName));
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (fsType == FileSystemProxyType.Code)
|
||||
{
|
||||
rc = FsCreators.StorageOnNcaCreator.VerifyAcidSignature(subDirFs, null);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
fileSystem = subDirFs;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private Result TryOpenNsp(ref U8Span path, out IFileSystem outFileSystem, IFileSystem baseFileSystem)
|
||||
{
|
||||
outFileSystem = default;
|
||||
|
||||
ReadOnlySpan<byte> nspExtension = new[] { (byte)'.', (byte)'n', (byte)'s', (byte)'p' };
|
||||
|
||||
// Search for the end of the nsp part of the path
|
||||
int nspPathLen = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
U8Span currentSpan;
|
||||
|
||||
while (true)
|
||||
{
|
||||
currentSpan = path.Slice(nspPathLen);
|
||||
if (StringUtils.CompareCaseInsensitive(nspExtension, currentSpan, 4) == 0)
|
||||
break;
|
||||
|
||||
if (currentSpan.Length == 0 || currentSpan[0] == 0)
|
||||
{
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
nspPathLen++;
|
||||
}
|
||||
|
||||
// The nsp filename must be the end of the entire path or the end of a path segment
|
||||
if (currentSpan.Length <= 4 || currentSpan[4] == 0 || currentSpan[4] == (byte)'/')
|
||||
break;
|
||||
|
||||
nspPathLen += 4;
|
||||
}
|
||||
|
||||
nspPathLen += 4;
|
||||
|
||||
if (nspPathLen > FsPath.MaxLength + 1)
|
||||
return ResultFs.TooLongPath.Log();
|
||||
|
||||
Result rc = FsPath.FromSpan(out FsPath nspPath, path.Slice(0, nspPathLen));
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = FileStorageBasedFileSystem.CreateNew(out FileStorageBasedFileSystem nspFileStorage, baseFileSystem,
|
||||
new U8Span(nspPath.Str), OpenMode.Read);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = FsCreators.PartitionFileSystemCreator.Create(out outFileSystem, nspFileStorage);
|
||||
|
||||
if (rc.IsSuccess())
|
||||
{
|
||||
path = path.Slice(nspPathLen);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
private Result TryOpenNca(ref U8Span path, out Nca nca, IFileSystem baseFileSystem, ulong ncaId)
|
||||
{
|
||||
nca = default;
|
||||
|
||||
Result rc = FileStorageBasedFileSystem.CreateNew(out FileStorageBasedFileSystem ncaFileStorage,
|
||||
baseFileSystem, path, OpenMode.Read);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = FsCreators.StorageOnNcaCreator.OpenNca(out Nca ncaTemp, ncaFileStorage);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (ncaId == ulong.MaxValue)
|
||||
{
|
||||
ulong ncaProgramId = ncaTemp.Header.TitleId;
|
||||
|
||||
if (ncaProgramId != ulong.MaxValue && ncaId != ncaProgramId)
|
||||
{
|
||||
return ResultFs.InvalidNcaProgramId.Log();
|
||||
}
|
||||
}
|
||||
|
||||
nca = ncaTemp;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private Result OpenNcaStorage(out IStorage ncaStorage, Nca nca, out NcaFormatType fsType,
|
||||
FileSystemProxyType fsProxyType, bool isGameCard, bool canMountSystemDataPrivate)
|
||||
{
|
||||
ncaStorage = default;
|
||||
fsType = default;
|
||||
|
||||
NcaContentType contentType = nca.Header.ContentType;
|
||||
|
||||
switch (fsProxyType)
|
||||
{
|
||||
case FileSystemProxyType.Code:
|
||||
case FileSystemProxyType.Rom:
|
||||
case FileSystemProxyType.Logo:
|
||||
case FileSystemProxyType.RegisteredUpdate:
|
||||
if (contentType != NcaContentType.Program)
|
||||
return ResultFs.PreconditionViolation.Log();
|
||||
|
||||
break;
|
||||
|
||||
case FileSystemProxyType.Control:
|
||||
if (contentType != NcaContentType.Control)
|
||||
return ResultFs.PreconditionViolation.Log();
|
||||
|
||||
break;
|
||||
case FileSystemProxyType.Manual:
|
||||
if (contentType != NcaContentType.Manual)
|
||||
return ResultFs.PreconditionViolation.Log();
|
||||
|
||||
break;
|
||||
case FileSystemProxyType.Meta:
|
||||
if (contentType != NcaContentType.Meta)
|
||||
return ResultFs.PreconditionViolation.Log();
|
||||
|
||||
break;
|
||||
case FileSystemProxyType.Data:
|
||||
if (contentType != NcaContentType.Data && contentType != NcaContentType.PublicData)
|
||||
return ResultFs.PreconditionViolation.Log();
|
||||
|
||||
if (contentType == NcaContentType.Data && !canMountSystemDataPrivate)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
break;
|
||||
default:
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
}
|
||||
|
||||
if (nca.Header.DistributionType == DistributionType.GameCard && !isGameCard)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
Result rc = SetNcaExternalKey(nca);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = GetNcaSectionIndex(out int sectionIndex, fsProxyType);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = FsCreators.StorageOnNcaCreator.Create(out ncaStorage, out NcaFsHeader fsHeader, nca,
|
||||
sectionIndex, fsProxyType == FileSystemProxyType.Code);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
fsType = fsHeader.FormatType;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private Result SetNcaExternalKey(Nca nca)
|
||||
{
|
||||
var rightsId = new RightsId(nca.Header.RightsId);
|
||||
var zero = new RightsId(0, 0);
|
||||
|
||||
if (Crypto.CryptoUtil.IsSameBytes(rightsId.AsBytes(), zero.AsBytes(), Unsafe.SizeOf<RightsId>()))
|
||||
return Result.Success;
|
||||
|
||||
// ReSharper disable once UnusedVariable
|
||||
Result rc = ExternalKeys.Get(rightsId, out AccessKey accessKey);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// todo: Set key in nca reader
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private Result GetNcaSectionIndex(out int index, FileSystemProxyType fspType)
|
||||
{
|
||||
switch (fspType)
|
||||
{
|
||||
case FileSystemProxyType.Code:
|
||||
case FileSystemProxyType.Control:
|
||||
case FileSystemProxyType.Manual:
|
||||
case FileSystemProxyType.Meta:
|
||||
case FileSystemProxyType.Data:
|
||||
index = 0;
|
||||
return Result.Success;
|
||||
case FileSystemProxyType.Rom:
|
||||
case FileSystemProxyType.RegisteredUpdate:
|
||||
index = 1;
|
||||
return Result.Success;
|
||||
case FileSystemProxyType.Logo:
|
||||
index = 2;
|
||||
return Result.Success;
|
||||
default:
|
||||
index = default;
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
}
|
||||
}
|
||||
|
||||
public Result OpenBisFileSystem(out IFileSystem fileSystem, string rootPath, BisPartitionId partitionId)
|
||||
{
|
||||
return FsCreators.BuiltInStorageFileSystemCreator.Create(out fileSystem, rootPath, partitionId);
|
||||
@ -651,54 +65,6 @@ namespace LibHac.FsSrv
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result OpenContentStorageFileSystem(out IFileSystem fileSystem, ContentStorageId storageId)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
U8String contentDirPath = default;
|
||||
IFileSystem baseFileSystem = default;
|
||||
bool isEncrypted = false;
|
||||
Result rc;
|
||||
|
||||
switch (storageId)
|
||||
{
|
||||
case ContentStorageId.System:
|
||||
rc = OpenBisFileSystem(out baseFileSystem, string.Empty, BisPartitionId.System);
|
||||
contentDirPath = $"/{ContentDirectoryName}".ToU8String();
|
||||
break;
|
||||
case ContentStorageId.User:
|
||||
rc = OpenBisFileSystem(out baseFileSystem, string.Empty, BisPartitionId.User);
|
||||
contentDirPath = $"/{ContentDirectoryName}".ToU8String();
|
||||
break;
|
||||
case ContentStorageId.SdCard:
|
||||
rc = OpenSdCardFileSystem(out baseFileSystem);
|
||||
contentDirPath = $"/{NintendoDirectoryName}/{ContentDirectoryName}".ToU8String();
|
||||
isEncrypted = true;
|
||||
break;
|
||||
default:
|
||||
rc = ResultFs.InvalidArgument.Log();
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = baseFileSystem.EnsureDirectoryExists(contentDirPath.ToString());
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = FsCreators.SubDirectoryFileSystemCreator.Create(out IFileSystem subDirFileSystem,
|
||||
baseFileSystem, contentDirPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (!isEncrypted)
|
||||
{
|
||||
fileSystem = subDirFileSystem;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
return FsCreators.EncryptedFileSystemCreator.Create(out fileSystem, subDirFileSystem,
|
||||
EncryptedFsKeyId.Content, SdEncryptionSeed);
|
||||
}
|
||||
|
||||
public Result OpenCustomStorageFileSystem(out IFileSystem fileSystem, CustomStorageId storageId)
|
||||
{
|
||||
fileSystem = default;
|
||||
@ -743,25 +109,6 @@ namespace LibHac.FsSrv
|
||||
}
|
||||
}
|
||||
|
||||
public Result OpenGameCardFileSystem(out IFileSystem fileSystem, GameCardHandle handle,
|
||||
GameCardPartition partitionId)
|
||||
{
|
||||
Result rc;
|
||||
int tries = 0;
|
||||
|
||||
do
|
||||
{
|
||||
rc = FsCreators.GameCardFileSystemCreator.Create(out fileSystem, handle, partitionId);
|
||||
|
||||
if (!ResultFs.DataCorrupted.Includes(rc))
|
||||
break;
|
||||
|
||||
tries++;
|
||||
} while (tries < 2);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
public Result OpenHostFileSystem(out IFileSystem fileSystem, U8Span path, bool openCaseSensitive)
|
||||
{
|
||||
fileSystem = default;
|
||||
@ -796,26 +143,7 @@ namespace LibHac.FsSrv
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result RegisterExternalKey(ref RightsId rightsId, ref AccessKey externalKey)
|
||||
{
|
||||
return ExternalKeys.Add(rightsId, externalKey);
|
||||
}
|
||||
|
||||
public Result UnregisterExternalKey(ref RightsId rightsId)
|
||||
{
|
||||
ExternalKeys.Remove(rightsId);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result UnregisterAllExternalKey()
|
||||
{
|
||||
ExternalKeys.Clear();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result SetSdCardEncryptionSeed(ref EncryptionSeed seed)
|
||||
public Result SetSdCardEncryptionSeed(in EncryptionSeed seed)
|
||||
{
|
||||
seed.Value.CopyTo(SdEncryptionSeed);
|
||||
// todo: FsCreators.SaveDataFileSystemCreator.SetSdCardEncryptionSeed(seed);
|
||||
|
@ -18,6 +18,7 @@ namespace LibHac.FsSrv
|
||||
public class FileSystemProxyImpl : IFileSystemProxy, IFileSystemProxyForLoader
|
||||
{
|
||||
private FileSystemProxyCoreImpl FsProxyCore { get; }
|
||||
private ReferenceCountedDisposable<NcaFileSystemService> NcaFileSystemService { get; set; }
|
||||
internal HorizonClient Hos { get; }
|
||||
|
||||
public ulong CurrentProcess { get; private set; }
|
||||
@ -40,98 +41,70 @@ namespace LibHac.FsSrv
|
||||
|
||||
private ProgramRegistryService GetProgramRegistryService()
|
||||
{
|
||||
return new ProgramRegistryService(FsProxyCore.Config.ProgramRegistryServiceImpl, CurrentProcess);
|
||||
return new ProgramRegistryService(FsProxyCore.Config.ProgramRegistryService, CurrentProcess);
|
||||
}
|
||||
|
||||
public Result OpenFileSystemWithId(out IFileSystem fileSystem, ref FsPath path, ulong id, FileSystemProxyType type)
|
||||
public Result OpenFileSystemWithId(out ReferenceCountedDisposable<IFileSystem> fileSystem, in FspPath path,
|
||||
ulong id, FileSystemProxyType fsType)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
AccessControl ac = programInfo.AccessControl;
|
||||
|
||||
switch (type)
|
||||
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
|
||||
if (rc.IsFailure())
|
||||
{
|
||||
case FileSystemProxyType.Logo:
|
||||
if (!ac.GetAccessibilityFor(AccessibilityType.MountLogo).CanRead)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
break;
|
||||
case FileSystemProxyType.Control:
|
||||
if (!ac.GetAccessibilityFor(AccessibilityType.MountContentControl).CanRead)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
break;
|
||||
case FileSystemProxyType.Manual:
|
||||
if (!ac.GetAccessibilityFor(AccessibilityType.MountContentManual).CanRead)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
break;
|
||||
case FileSystemProxyType.Meta:
|
||||
if (!ac.GetAccessibilityFor(AccessibilityType.MountContentMeta).CanRead)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
break;
|
||||
case FileSystemProxyType.Data:
|
||||
if (!ac.GetAccessibilityFor(AccessibilityType.MountContentData).CanRead)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
break;
|
||||
case FileSystemProxyType.Package:
|
||||
if (!ac.GetAccessibilityFor(AccessibilityType.MountApplicationPackage).CanRead)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
break;
|
||||
default:
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
fileSystem = default;
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (type == FileSystemProxyType.Meta)
|
||||
return ncaFsService.OpenFileSystemWithId(out fileSystem, in path, id, fsType);
|
||||
}
|
||||
|
||||
public Result OpenFileSystemWithPatch(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
ProgramId programId, FileSystemProxyType fsType)
|
||||
{
|
||||
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
|
||||
if (rc.IsFailure())
|
||||
{
|
||||
id = ulong.MaxValue;
|
||||
}
|
||||
else if (id == ulong.MaxValue)
|
||||
{
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
fileSystem = default;
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool canMountSystemDataPrivate = ac.GetAccessibilityFor(AccessibilityType.MountSystemDataPrivate).CanRead;
|
||||
|
||||
var normalizer = new PathNormalizer(path, GetPathNormalizerOptions(path));
|
||||
if (normalizer.Result.IsFailure()) return normalizer.Result;
|
||||
|
||||
return FsProxyCore.OpenFileSystem(out fileSystem, normalizer.Path, type, canMountSystemDataPrivate, id);
|
||||
|
||||
// Missing speed emulation storage type wrapper, async wrapper, and FileSystemInterfaceAdapter
|
||||
return ncaFsService.OpenFileSystemWithPatch(out fileSystem, programId, fsType);
|
||||
}
|
||||
|
||||
private PathNormalizer.Option GetPathNormalizerOptions(U8Span path)
|
||||
public Result OpenCodeFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
out CodeVerificationData verificationData, in FspPath path, ProgramId programId)
|
||||
{
|
||||
int hostMountLength = StringUtils.GetLength(CommonMountNames.HostRootFileSystemMountName,
|
||||
PathTools.MountNameLengthMax);
|
||||
Unsafe.SkipInit(out verificationData);
|
||||
|
||||
bool isHostPath = StringUtils.Compare(path, CommonMountNames.HostRootFileSystemMountName, hostMountLength) == 0;
|
||||
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
|
||||
if (rc.IsFailure())
|
||||
{
|
||||
fileSystem = default;
|
||||
return rc;
|
||||
}
|
||||
|
||||
PathNormalizer.Option hostOption = isHostPath ? PathNormalizer.Option.PreserveUnc : PathNormalizer.Option.None;
|
||||
return PathNormalizer.Option.HasMountName | PathNormalizer.Option.PreserveTailSeparator | hostOption;
|
||||
}
|
||||
|
||||
public Result OpenFileSystemWithPatch(out IFileSystem fileSystem, ProgramId programId, FileSystemProxyType type)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenCodeFileSystem(out IFileSystem fileSystem, out CodeVerificationData verificationData, in FspPath path,
|
||||
ProgramId programId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return ncaFsService.OpenCodeFileSystem(out fileSystem, out verificationData, in path, programId);
|
||||
}
|
||||
|
||||
public Result IsArchivedProgram(out bool isArchived, ulong processId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
Unsafe.SkipInit(out isArchived);
|
||||
|
||||
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return ncaFsService.IsArchivedProgram(out isArchived, processId);
|
||||
}
|
||||
|
||||
public Result SetCurrentProcess(ulong processId)
|
||||
{
|
||||
CurrentProcess = processId;
|
||||
|
||||
// Initialize the NCA file system service
|
||||
var ncaService = new NcaFileSystemService(FsProxyCore.Config.NcaFileSystemService, processId);
|
||||
NcaFileSystemService = new ReferenceCountedDisposable<NcaFileSystemService>(ncaService);
|
||||
NcaFileSystemService.Target.SetSelfReference(NcaFileSystemService);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
@ -140,44 +113,96 @@ namespace LibHac.FsSrv
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenDataFileSystemByCurrentProcess(out IFileSystem fileSystem)
|
||||
public Result OpenDataFileSystemByCurrentProcess(out ReferenceCountedDisposable<IFileSystem> fileSystem)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
|
||||
if (rc.IsFailure())
|
||||
{
|
||||
fileSystem = default;
|
||||
return rc;
|
||||
}
|
||||
|
||||
return ncaFsService.OpenDataFileSystemByCurrentProcess(out fileSystem);
|
||||
}
|
||||
|
||||
public Result OpenDataFileSystemByProgramId(out IFileSystem fileSystem, ProgramId programId)
|
||||
public Result OpenDataFileSystemByProgramId(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
ProgramId programId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
|
||||
if (rc.IsFailure())
|
||||
{
|
||||
fileSystem = default;
|
||||
return rc;
|
||||
}
|
||||
|
||||
return ncaFsService.OpenDataFileSystemByProgramId(out fileSystem, programId);
|
||||
}
|
||||
|
||||
public Result OpenDataStorageByCurrentProcess(out IStorage storage)
|
||||
public Result OpenDataStorageByCurrentProcess(out ReferenceCountedDisposable<IStorage> storage)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
|
||||
if (rc.IsFailure())
|
||||
{
|
||||
storage = default;
|
||||
return rc;
|
||||
}
|
||||
|
||||
return ncaFsService.OpenDataStorageByCurrentProcess(out storage);
|
||||
}
|
||||
|
||||
public Result OpenDataStorageByProgramId(out IStorage storage, ProgramId programId)
|
||||
public Result OpenDataStorageByProgramId(out ReferenceCountedDisposable<IStorage> storage, ProgramId programId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
|
||||
if (rc.IsFailure())
|
||||
{
|
||||
storage = default;
|
||||
return rc;
|
||||
}
|
||||
|
||||
return ncaFsService.OpenDataStorageByProgramId(out storage, programId);
|
||||
}
|
||||
|
||||
public Result OpenDataStorageByDataId(out IStorage storage, DataId dataId, StorageId storageId)
|
||||
public Result OpenDataStorageByDataId(out ReferenceCountedDisposable<IStorage> storage, DataId dataId, StorageId storageId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
|
||||
if (rc.IsFailure())
|
||||
{
|
||||
storage = default;
|
||||
return rc;
|
||||
}
|
||||
|
||||
return ncaFsService.OpenDataStorageByDataId(out storage, dataId, storageId);
|
||||
}
|
||||
|
||||
public Result OpenPatchDataStorageByCurrentProcess(out IStorage storage)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
storage = default;
|
||||
return ResultFs.TargetNotFound.Log();
|
||||
}
|
||||
|
||||
public Result OpenDataFileSystemWithProgramIndex(out IFileSystem fileSystem, byte programIndex)
|
||||
public Result OpenDataFileSystemWithProgramIndex(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
byte programIndex)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
|
||||
if (rc.IsFailure())
|
||||
{
|
||||
fileSystem = default;
|
||||
return rc;
|
||||
}
|
||||
|
||||
return ncaFsService.OpenDataFileSystemWithProgramIndex(out fileSystem, programIndex);
|
||||
}
|
||||
|
||||
public Result OpenDataStorageWithProgramIndex(out IStorage storage, byte programIndex)
|
||||
public Result OpenDataStorageWithProgramIndex(out ReferenceCountedDisposable<IStorage> storage, byte programIndex)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
|
||||
if (rc.IsFailure())
|
||||
{
|
||||
storage = default;
|
||||
return rc;
|
||||
}
|
||||
|
||||
return ncaFsService.OpenDataStorageWithProgramIndex(out storage, programIndex);
|
||||
}
|
||||
|
||||
public Result RegisterSaveDataFileSystemAtomicDeletion(ReadOnlySpan<ulong> saveDataIds)
|
||||
@ -719,7 +744,8 @@ namespace LibHac.FsSrv
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenImageDirectoryFileSystem(out IFileSystem fileSystem, ImageDirectoryId directoryId)
|
||||
public Result OpenImageDirectoryFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
ImageDirectoryId directoryId)
|
||||
{
|
||||
return GetBaseFileSystemService().OpenImageDirectoryFileSystem(out fileSystem, directoryId);
|
||||
}
|
||||
@ -734,7 +760,8 @@ namespace LibHac.FsSrv
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenBisFileSystem(out IFileSystem fileSystem, in FspPath rootPath, BisPartitionId partitionId)
|
||||
public Result OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, in FspPath rootPath,
|
||||
BisPartitionId partitionId)
|
||||
{
|
||||
return GetBaseFileSystemService().OpenBisFileSystem(out fileSystem, in rootPath, partitionId);
|
||||
}
|
||||
@ -761,7 +788,7 @@ namespace LibHac.FsSrv
|
||||
return OpenHostFileSystemWithOption(out fileSystem, ref path, MountHostOption.None);
|
||||
}
|
||||
|
||||
public Result OpenSdCardFileSystem(out IFileSystem fileSystem)
|
||||
public Result OpenSdCardFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem)
|
||||
{
|
||||
return GetBaseFileSystemService().OpenSdCardFileSystem(out fileSystem);
|
||||
}
|
||||
@ -795,6 +822,26 @@ namespace LibHac.FsSrv
|
||||
return FsProxyCore.OpenDeviceOperator(out deviceOperator);
|
||||
}
|
||||
|
||||
public Result OpenSystemDataUpdateEventNotifier(out ReferenceCountedDisposable<IEventNotifier> eventNotifier)
|
||||
{
|
||||
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
|
||||
if (rc.IsFailure())
|
||||
{
|
||||
eventNotifier = null;
|
||||
return rc;
|
||||
}
|
||||
|
||||
return ncaFsService.OpenSystemDataUpdateEventNotifier(out eventNotifier);
|
||||
}
|
||||
|
||||
public Result NotifySystemDataUpdateEvent()
|
||||
{
|
||||
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return ncaFsService.NotifySystemDataUpdateEvent();
|
||||
}
|
||||
|
||||
public Result OpenSaveDataInfoReader(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader)
|
||||
{
|
||||
infoReader = default;
|
||||
@ -1015,11 +1062,16 @@ namespace LibHac.FsSrv
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result OpenContentStorageFileSystem(out IFileSystem fileSystem, ContentStorageId storageId)
|
||||
public Result OpenContentStorageFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, ContentStorageId storageId)
|
||||
{
|
||||
// Missing permission check, speed emulation storage type wrapper, and FileSystemInterfaceAdapter
|
||||
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
|
||||
if (rc.IsFailure())
|
||||
{
|
||||
fileSystem = null;
|
||||
return rc;
|
||||
}
|
||||
|
||||
return FsProxyCore.OpenContentStorageFileSystem(out fileSystem, storageId);
|
||||
return ncaFsService.OpenContentStorageFileSystem(out fileSystem, storageId);
|
||||
}
|
||||
|
||||
public Result OpenCloudBackupWorkStorageFileSystem(out IFileSystem fileSystem, CloudBackupWorkStorageId storageId)
|
||||
@ -1034,8 +1086,8 @@ namespace LibHac.FsSrv
|
||||
return FsProxyCore.OpenCustomStorageFileSystem(out fileSystem, storageId);
|
||||
}
|
||||
|
||||
public Result OpenGameCardFileSystem(out IFileSystem fileSystem, GameCardHandle handle,
|
||||
GameCardPartition partitionId)
|
||||
public Result OpenGameCardFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
GameCardHandle handle, GameCardPartition partitionId)
|
||||
{
|
||||
return GetBaseFileSystemService().OpenGameCardFileSystem(out fileSystem, handle, partitionId);
|
||||
}
|
||||
@ -1056,48 +1108,69 @@ namespace LibHac.FsSrv
|
||||
|
||||
public Result GetRightsId(out RightsId rightsId, ProgramId programId, StorageId storageId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
Unsafe.SkipInit(out rightsId);
|
||||
|
||||
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return ncaFsService.GetRightsId(out rightsId, programId, storageId);
|
||||
}
|
||||
|
||||
public Result GetRightsIdByPath(out RightsId rightsId, ref FsPath path)
|
||||
public Result GetRightsIdByPath(out RightsId rightsId, in FspPath path)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return GetRightsIdAndKeyGenerationByPath(out rightsId, out _, in path);
|
||||
}
|
||||
|
||||
public Result GetRightsIdAndKeyGenerationByPath(out RightsId rightsId, out byte keyGeneration, ref FsPath path)
|
||||
public Result GetRightsIdAndKeyGenerationByPath(out RightsId rightsId, out byte keyGeneration, in FspPath path)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
Unsafe.SkipInit(out rightsId);
|
||||
Unsafe.SkipInit(out keyGeneration);
|
||||
|
||||
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return ncaFsService.GetRightsIdAndKeyGenerationByPath(out rightsId, out keyGeneration, in path);
|
||||
}
|
||||
|
||||
public Result RegisterExternalKey(ref RightsId rightsId, ref AccessKey externalKey)
|
||||
public Result RegisterExternalKey(in RightsId rightsId, in AccessKey externalKey)
|
||||
{
|
||||
// Missing permission check
|
||||
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return FsProxyCore.RegisterExternalKey(ref rightsId, ref externalKey);
|
||||
return ncaFsService.RegisterExternalKey(in rightsId, in externalKey);
|
||||
}
|
||||
|
||||
public Result UnregisterExternalKey(ref RightsId rightsId)
|
||||
public Result UnregisterExternalKey(in RightsId rightsId)
|
||||
{
|
||||
// Missing permission check
|
||||
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return FsProxyCore.UnregisterExternalKey(ref rightsId);
|
||||
return ncaFsService.UnregisterExternalKey(in rightsId);
|
||||
}
|
||||
|
||||
public Result UnregisterAllExternalKey()
|
||||
{
|
||||
// Missing permission check
|
||||
|
||||
return FsProxyCore.UnregisterAllExternalKey();
|
||||
}
|
||||
|
||||
public Result SetSdCardEncryptionSeed(ref EncryptionSeed seed)
|
||||
{
|
||||
// Missing permission check
|
||||
|
||||
Result rc = FsProxyCore.SetSdCardEncryptionSeed(ref seed);
|
||||
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return Result.Success;
|
||||
return ncaFsService.UnregisterAllExternalKey();
|
||||
}
|
||||
|
||||
public Result SetSdCardEncryptionSeed(in EncryptionSeed seed)
|
||||
{
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (!programInfo.AccessControl.CanCall(OperationType.SetEncryptionSeed))
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
rc = FsProxyCore.SetSdCardEncryptionSeed(in seed);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return ncaFsService.SetSdCardEncryptionSeed(in seed);
|
||||
}
|
||||
|
||||
public Result VerifySaveDataFileSystemBySaveDataSpaceId(SaveDataSpaceId spaceId, ulong saveDataId, Span<byte> readBuffer)
|
||||
@ -1166,12 +1239,22 @@ namespace LibHac.FsSrv
|
||||
|
||||
public Result RegisterUpdatePartition()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return ncaFsService.RegisterUpdatePartition();
|
||||
}
|
||||
|
||||
public Result OpenRegisteredUpdatePartition(out IFileSystem fileSystem)
|
||||
public Result OpenRegisteredUpdatePartition(out ReferenceCountedDisposable<IFileSystem> fileSystem)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
Result rc = GetNcaFileSystemService(out NcaFileSystemService ncaFsService);
|
||||
if (rc.IsFailure())
|
||||
{
|
||||
fileSystem = default;
|
||||
return rc;
|
||||
}
|
||||
|
||||
return ncaFsService.OpenRegisteredUpdatePartition(out fileSystem);
|
||||
}
|
||||
|
||||
public Result OverrideSaveDataTransferTokenSignVerificationKey(ReadOnlySpan<byte> key)
|
||||
@ -1269,7 +1352,19 @@ namespace LibHac.FsSrv
|
||||
|
||||
private BaseFileSystemService GetBaseFileSystemService()
|
||||
{
|
||||
return new BaseFileSystemService(FsProxyCore.Config.BaseFileSystemServiceImpl, CurrentProcess);
|
||||
return new BaseFileSystemService(FsProxyCore.Config.BaseFileSystemService, CurrentProcess);
|
||||
}
|
||||
|
||||
private Result GetNcaFileSystemService(out NcaFileSystemService ncaFsService)
|
||||
{
|
||||
if (NcaFileSystemService is null)
|
||||
{
|
||||
ncaFsService = null;
|
||||
return ResultFs.PreconditionViolation.Log();
|
||||
}
|
||||
|
||||
ncaFsService = NcaFileSystemService.Target;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
internal bool IsCurrentProcess(ulong processId)
|
||||
|
@ -12,6 +12,9 @@ namespace LibHac.FsSrv
|
||||
{
|
||||
internal const ulong SaveIndexerId = 0x8000000000000000;
|
||||
|
||||
private const ulong SpeedEmulationProgramIdMinimum = 0x100000000000000;
|
||||
private const ulong SpeedEmulationProgramIdMaximum = 0x100000000001FFF;
|
||||
|
||||
private FileSystemProxyCoreImpl FsProxyCore { get; }
|
||||
|
||||
/// <summary>The client instance to be used for internal operations like save indexer access.</summary>
|
||||
@ -91,11 +94,30 @@ namespace LibHac.FsSrv
|
||||
baseFsServiceConfig.ProgramRegistry = programRegistry;
|
||||
var baseFsService = new BaseFileSystemServiceImpl(in baseFsServiceConfig);
|
||||
|
||||
var ncaFsServiceConfig = new NcaFileSystemServiceImpl.Configuration();
|
||||
ncaFsServiceConfig.BaseFsService = baseFsService;
|
||||
ncaFsServiceConfig.HostFsCreator = config.FsCreators.HostFileSystemCreator;
|
||||
ncaFsServiceConfig.TargetManagerFsCreator = config.FsCreators.TargetManagerFileSystemCreator;
|
||||
ncaFsServiceConfig.PartitionFsCreator = config.FsCreators.PartitionFileSystemCreator;
|
||||
ncaFsServiceConfig.RomFsCreator = config.FsCreators.RomFileSystemCreator;
|
||||
ncaFsServiceConfig.StorageOnNcaCreator = config.FsCreators.StorageOnNcaCreator;
|
||||
ncaFsServiceConfig.SubDirectoryFsCreator = config.FsCreators.SubDirectoryFileSystemCreator;
|
||||
ncaFsServiceConfig.EncryptedFsCreator = config.FsCreators.EncryptedFileSystemCreator;
|
||||
ncaFsServiceConfig.ProgramRegistryService = programRegistryService;
|
||||
ncaFsServiceConfig.HorizonClient = Hos;
|
||||
ncaFsServiceConfig.ProgramRegistry = programRegistry;
|
||||
ncaFsServiceConfig.SpeedEmulationRange =
|
||||
new InternalProgramIdRangeForSpeedEmulation(SpeedEmulationProgramIdMinimum,
|
||||
SpeedEmulationProgramIdMaximum);
|
||||
|
||||
var ncaFsService = new NcaFileSystemServiceImpl(in ncaFsServiceConfig, config.ExternalKeySet);
|
||||
|
||||
var fspConfig = new FileSystemProxyConfiguration
|
||||
{
|
||||
FsCreatorInterfaces = config.FsCreators,
|
||||
BaseFileSystemServiceImpl = baseFsService,
|
||||
ProgramRegistryServiceImpl = programRegistryService
|
||||
BaseFileSystemService = baseFsService,
|
||||
NcaFileSystemService = ncaFsService,
|
||||
ProgramRegistryService = programRegistryService
|
||||
};
|
||||
|
||||
return fspConfig;
|
||||
@ -113,7 +135,7 @@ namespace LibHac.FsSrv
|
||||
|
||||
private ProgramRegistryImpl GetProgramRegistryServiceObject()
|
||||
{
|
||||
return new ProgramRegistryImpl(FsProxyCore.Config.ProgramRegistryServiceImpl);
|
||||
return new ProgramRegistryImpl(FsProxyCore.Config.ProgramRegistryService);
|
||||
}
|
||||
|
||||
private class FileSystemProxyService : IServiceObject
|
||||
|
@ -13,16 +13,16 @@ namespace LibHac.FsSrv
|
||||
public interface IFileSystemProxy
|
||||
{
|
||||
Result SetCurrentProcess(ulong processId);
|
||||
Result OpenDataFileSystemByCurrentProcess(out IFileSystem fileSystem);
|
||||
Result OpenFileSystemWithPatch(out IFileSystem fileSystem, ProgramId programId, FileSystemProxyType type);
|
||||
Result OpenFileSystemWithId(out IFileSystem fileSystem, ref FsPath path, ulong id, FileSystemProxyType type);
|
||||
Result OpenDataFileSystemByProgramId(out IFileSystem fileSystem, ProgramId programId);
|
||||
Result OpenBisFileSystem(out IFileSystem fileSystem, in FspPath rootPath, BisPartitionId partitionId);
|
||||
Result OpenDataFileSystemByCurrentProcess(out ReferenceCountedDisposable<IFileSystem> fileSystem);
|
||||
Result OpenFileSystemWithPatch(out ReferenceCountedDisposable<IFileSystem> fileSystem, ProgramId programId, FileSystemProxyType fsType);
|
||||
Result OpenFileSystemWithId(out ReferenceCountedDisposable<IFileSystem> fileSystem, in FspPath path, ulong id, FileSystemProxyType fsType);
|
||||
Result OpenDataFileSystemByProgramId(out ReferenceCountedDisposable<IFileSystem> fileSystem, ProgramId programId);
|
||||
Result OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, in FspPath rootPath, BisPartitionId partitionId);
|
||||
Result OpenBisStorage(out IStorage storage, BisPartitionId partitionId);
|
||||
Result InvalidateBisCache();
|
||||
Result OpenHostFileSystemWithOption(out IFileSystem fileSystem, ref FsPath path, MountHostOption option);
|
||||
Result OpenHostFileSystem(out IFileSystem fileSystem, ref FsPath path);
|
||||
Result OpenSdCardFileSystem(out IFileSystem fileSystem);
|
||||
Result OpenSdCardFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem);
|
||||
Result FormatSdCardFileSystem();
|
||||
Result DeleteSaveDataFileSystem(ulong saveDataId);
|
||||
Result CreateSaveDataFileSystem(ref SaveDataAttribute attribute, ref SaveDataCreationInfo creationInfo, ref SaveMetaCreateInfo metaCreateInfo);
|
||||
@ -33,7 +33,7 @@ namespace LibHac.FsSrv
|
||||
Result IsExFatSupported(out bool isSupported);
|
||||
Result DeleteSaveDataFileSystemBySaveDataAttribute(SaveDataSpaceId spaceId, ref SaveDataAttribute attribute);
|
||||
Result OpenGameCardStorage(out IStorage storage, GameCardHandle handle, GameCardPartitionRaw partitionId);
|
||||
Result OpenGameCardFileSystem(out IFileSystem fileSystem, GameCardHandle handle, GameCardPartition partitionId);
|
||||
Result OpenGameCardFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, GameCardHandle handle, GameCardPartition partitionId);
|
||||
Result ExtendSaveDataFileSystem(SaveDataSpaceId spaceId, ulong saveDataId, long dataSize, long journalSize);
|
||||
Result DeleteCacheStorage(short index);
|
||||
Result GetCacheStorageSize(out long dataSize, out long journalSize, short index);
|
||||
@ -57,36 +57,39 @@ namespace LibHac.FsSrv
|
||||
Result OpenSaveDataMetaFile(out IFile file, SaveDataSpaceId spaceId, ref SaveDataAttribute attribute, SaveDataMetaType type);
|
||||
|
||||
Result ListAccessibleSaveDataOwnerId(out int readCount, Span<Ncm.ApplicationId> idBuffer, ProgramId programId, int startIndex, int bufferIdCount);
|
||||
Result OpenImageDirectoryFileSystem(out IFileSystem fileSystem, ImageDirectoryId directoryId);
|
||||
Result OpenContentStorageFileSystem(out IFileSystem fileSystem, ContentStorageId storageId);
|
||||
Result OpenImageDirectoryFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, ImageDirectoryId directoryId);
|
||||
Result OpenContentStorageFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, ContentStorageId storageId);
|
||||
Result OpenCloudBackupWorkStorageFileSystem(out IFileSystem fileSystem, CloudBackupWorkStorageId storageId);
|
||||
Result OpenCustomStorageFileSystem(out IFileSystem fileSystem, CustomStorageId storageId);
|
||||
Result OpenDataStorageByCurrentProcess(out IStorage storage);
|
||||
Result OpenDataStorageByProgramId(out IStorage storage, ProgramId programId);
|
||||
Result OpenDataStorageByDataId(out IStorage storage, DataId dataId, StorageId storageId);
|
||||
Result OpenDataStorageByCurrentProcess(out ReferenceCountedDisposable<IStorage> storage);
|
||||
Result OpenDataStorageByProgramId(out ReferenceCountedDisposable<IStorage> storage, ProgramId programId);
|
||||
Result OpenDataStorageByDataId(out ReferenceCountedDisposable<IStorage> storage, DataId dataId, StorageId storageId);
|
||||
Result OpenPatchDataStorageByCurrentProcess(out IStorage storage);
|
||||
Result OpenDataFileSystemWithProgramIndex(out IFileSystem fileSystem, byte programIndex);
|
||||
Result OpenDataStorageWithProgramIndex(out IStorage storage, byte programIndex);
|
||||
Result OpenDataFileSystemWithProgramIndex(out ReferenceCountedDisposable<IFileSystem> fileSystem, byte programIndex);
|
||||
Result OpenDataStorageWithProgramIndex(out ReferenceCountedDisposable<IStorage> storage, byte programIndex);
|
||||
Result OpenDeviceOperator(out IDeviceOperator deviceOperator);
|
||||
|
||||
Result OpenSystemDataUpdateEventNotifier(out ReferenceCountedDisposable<IEventNotifier> eventNotifier);
|
||||
Result NotifySystemDataUpdateEvent();
|
||||
|
||||
Result QuerySaveDataTotalSize(out long totalSize, long dataSize, long journalSize);
|
||||
Result VerifySaveDataFileSystem(ulong saveDataId, Span<byte> readBuffer);
|
||||
Result CorruptSaveDataFileSystem(ulong saveDataId);
|
||||
Result CreatePaddingFile(long size);
|
||||
Result DeleteAllPaddingFiles();
|
||||
Result GetRightsId(out RightsId rightsId, ProgramId programId, StorageId storageId);
|
||||
Result RegisterExternalKey(ref RightsId rightsId, ref AccessKey externalKey);
|
||||
Result RegisterExternalKey(in RightsId rightsId, in AccessKey externalKey);
|
||||
Result UnregisterAllExternalKey();
|
||||
Result GetRightsIdByPath(out RightsId rightsId, ref FsPath path);
|
||||
Result GetRightsIdAndKeyGenerationByPath(out RightsId rightsId, out byte keyGeneration, ref FsPath path);
|
||||
Result GetRightsIdByPath(out RightsId rightsId, in FspPath path);
|
||||
Result GetRightsIdAndKeyGenerationByPath(out RightsId rightsId, out byte keyGeneration, in FspPath path);
|
||||
Result SetCurrentPosixTimeWithTimeDifference(long time, int difference);
|
||||
Result GetFreeSpaceSizeForSaveData(out long freeSpaceSize, SaveDataSpaceId spaceId);
|
||||
Result VerifySaveDataFileSystemBySaveDataSpaceId(SaveDataSpaceId spaceId, ulong saveDataId, Span<byte> readBuffer);
|
||||
Result CorruptSaveDataFileSystemBySaveDataSpaceId(SaveDataSpaceId spaceId, ulong saveDataId);
|
||||
Result QuerySaveDataInternalStorageTotalSize(out long size, SaveDataSpaceId spaceId, ulong saveDataId);
|
||||
Result GetSaveDataCommitId(out long commitId, SaveDataSpaceId spaceId, ulong saveDataId);
|
||||
Result UnregisterExternalKey(ref RightsId rightsId);
|
||||
Result SetSdCardEncryptionSeed(ref EncryptionSeed seed);
|
||||
Result UnregisterExternalKey(in RightsId rightsId);
|
||||
Result SetSdCardEncryptionSeed(in EncryptionSeed seed);
|
||||
Result SetSdCardAccessibility(bool isAccessible);
|
||||
Result IsSdCardAccessible(out bool isAccessible);
|
||||
|
||||
@ -99,7 +102,7 @@ namespace LibHac.FsSrv
|
||||
Result GetGlobalAccessLogMode(out GlobalAccessLogMode mode);
|
||||
Result OutputAccessLogToSdCard(U8Span logString);
|
||||
Result RegisterUpdatePartition();
|
||||
Result OpenRegisteredUpdatePartition(out IFileSystem fileSystem);
|
||||
Result OpenRegisteredUpdatePartition(out ReferenceCountedDisposable<IFileSystem> fileSystem);
|
||||
|
||||
Result GetProgramIndexForAccessLog(out int programIndex, out int programCount);
|
||||
Result OverrideSaveDataTransferTokenSignVerificationKey(ReadOnlySpan<byte> key);
|
||||
|
17
src/LibHac/FsSrv/IRomFileSystemAccessFailureManager.cs
Normal file
17
src/LibHac/FsSrv/IRomFileSystemAccessFailureManager.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using LibHac.Fs;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.Ncm;
|
||||
|
||||
namespace LibHac.FsSrv
|
||||
{
|
||||
public interface IRomFileSystemAccessFailureManager
|
||||
{
|
||||
Result OpenDataStorageCore(out ReferenceCountedDisposable<IStorage> storage, out Hash ncaHeaderDigest, ulong id,
|
||||
StorageId storageId);
|
||||
|
||||
Result HandleResolubleAccessFailure(out bool wasDeferred, in Result nonDeferredResult);
|
||||
void IncrementRomFsRemountForDataCorruptionCount();
|
||||
void IncrementRomFsUnrecoverableDataCorruptionByRemountCount();
|
||||
void IncrementRomFsRecoveredByInvalidateCacheCount();
|
||||
}
|
||||
}
|
222
src/LibHac/FsSrv/Impl/LocationResolverSet.cs
Normal file
222
src/LibHac/FsSrv/Impl/LocationResolverSet.cs
Normal file
@ -0,0 +1,222 @@
|
||||
using System;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Lr;
|
||||
using LibHac.Ncm;
|
||||
|
||||
namespace LibHac.FsSrv.Impl
|
||||
{
|
||||
internal class LocationResolverSet : IDisposable
|
||||
{
|
||||
private const int LocationResolverCount = 5;
|
||||
|
||||
private LocationResolver[] _resolvers;
|
||||
private AddOnContentLocationResolver _aocResolver;
|
||||
private object _locker;
|
||||
|
||||
private HorizonClient _hos;
|
||||
|
||||
public LocationResolverSet(HorizonClient horizonClient)
|
||||
{
|
||||
_resolvers = new LocationResolver[LocationResolverCount];
|
||||
_locker = new object();
|
||||
_hos = horizonClient;
|
||||
}
|
||||
|
||||
private Result GetLocationResolver(out LocationResolver resolver, StorageId storageId)
|
||||
{
|
||||
resolver = default;
|
||||
|
||||
if (!IsValidStorageId(storageId))
|
||||
return ResultLr.LocationResolverNotFound.Log();
|
||||
|
||||
lock (_locker)
|
||||
{
|
||||
int index = GetResolverIndexFromStorageId(storageId);
|
||||
ref LocationResolver lr = ref _resolvers[index];
|
||||
|
||||
// Open the location resolver if it hasn't been already
|
||||
if (lr is null && _hos.Lr.OpenLocationResolver(out lr, storageId).IsFailure())
|
||||
return ResultLr.LocationResolverNotFound.Log();
|
||||
|
||||
resolver = lr;
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
private Result GetRegisteredLocationResolver(out RegisteredLocationResolver resolver)
|
||||
{
|
||||
Result rc = _hos.Lr.OpenRegisteredLocationResolver(out RegisteredLocationResolver lr);
|
||||
|
||||
if (rc.IsFailure())
|
||||
{
|
||||
lr?.Dispose();
|
||||
resolver = default;
|
||||
return rc;
|
||||
}
|
||||
|
||||
resolver = lr;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private Result GetAddOnContentLocationResolver(out AddOnContentLocationResolver resolver)
|
||||
{
|
||||
lock (_locker)
|
||||
{
|
||||
if (_aocResolver is null)
|
||||
{
|
||||
Result rc = _hos.Lr.OpenAddOnContentLocationResolver(out AddOnContentLocationResolver lr);
|
||||
if (rc.IsFailure())
|
||||
{
|
||||
resolver = default;
|
||||
return rc;
|
||||
}
|
||||
|
||||
_aocResolver = lr;
|
||||
}
|
||||
|
||||
resolver = _aocResolver;
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
public Result ResolveApplicationControlPath(out Path path, Ncm.ApplicationId applicationId, StorageId storageId)
|
||||
{
|
||||
Path.InitEmpty(out path);
|
||||
|
||||
Result rc = GetLocationResolver(out LocationResolver resolver, storageId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = resolver.ResolveApplicationControlPath(out path, applicationId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
PathUtility.Replace(path.StrMutable, (byte)'\\', (byte)'/');
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result ResolveApplicationHtmlDocumentPath(out Path path, Ncm.ApplicationId applicationId, StorageId storageId)
|
||||
{
|
||||
Path.InitEmpty(out path);
|
||||
|
||||
Result rc = GetLocationResolver(out LocationResolver resolver, storageId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = resolver.ResolveApplicationHtmlDocumentPath(out path, applicationId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
PathUtility.Replace(path.StrMutable, (byte)'\\', (byte)'/');
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result ResolveProgramPath(out Path path, ProgramId programId, StorageId storageId)
|
||||
{
|
||||
Path.InitEmpty(out path);
|
||||
|
||||
Result rc = GetLocationResolver(out LocationResolver resolver, storageId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = resolver.ResolveProgramPath(out path, programId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
PathUtility.Replace(path.StrMutable, (byte)'\\', (byte)'/');
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result ResolveRomPath(out Path path, ProgramId programId, StorageId storageId)
|
||||
{
|
||||
Path.InitEmpty(out path);
|
||||
|
||||
Result rc = GetLocationResolver(out LocationResolver resolver, storageId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = resolver.ResolveProgramPath(out path, programId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
PathUtility.Replace(path.StrMutable, (byte)'\\', (byte)'/');
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result ResolveAddOnContentPath(out Path path, DataId dataId)
|
||||
{
|
||||
Path.InitEmpty(out path);
|
||||
|
||||
Result rc = GetAddOnContentLocationResolver(out AddOnContentLocationResolver resolver);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return resolver.ResolveAddOnContentPath(out path, dataId);
|
||||
}
|
||||
|
||||
public Result ResolveDataPath(out Path path, DataId dataId, StorageId storageId)
|
||||
{
|
||||
Path.InitEmpty(out path);
|
||||
|
||||
if (storageId == StorageId.None)
|
||||
return ResultFs.InvalidAlignment.Log();
|
||||
|
||||
Result rc = GetLocationResolver(out LocationResolver resolver, storageId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return resolver.ResolveDataPath(out path, dataId);
|
||||
}
|
||||
|
||||
public Result ResolveRegisteredProgramPath(out Path path, ProgramId programId)
|
||||
{
|
||||
Path.InitEmpty(out path);
|
||||
|
||||
Result rc = GetRegisteredLocationResolver(out RegisteredLocationResolver resolver);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
using (resolver)
|
||||
{
|
||||
return resolver.ResolveProgramPath(out path, programId);
|
||||
}
|
||||
}
|
||||
|
||||
public Result ResolveRegisteredHtmlDocumentPath(out Path path, ProgramId programId)
|
||||
{
|
||||
Path.InitEmpty(out path);
|
||||
|
||||
Result rc = GetRegisteredLocationResolver(out RegisteredLocationResolver resolver);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
using (resolver)
|
||||
{
|
||||
return resolver.ResolveHtmlDocumentPath(out path, programId);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsValidStorageId(StorageId id)
|
||||
{
|
||||
return id == StorageId.Host ||
|
||||
id == StorageId.GameCard ||
|
||||
id == StorageId.BuiltInSystem ||
|
||||
id == StorageId.BuiltInUser ||
|
||||
id == StorageId.SdCard;
|
||||
}
|
||||
|
||||
private static int GetResolverIndexFromStorageId(StorageId id)
|
||||
{
|
||||
Assert.AssertTrue(IsValidStorageId(id));
|
||||
|
||||
return id switch
|
||||
{
|
||||
StorageId.Host => 2,
|
||||
StorageId.GameCard => 4,
|
||||
StorageId.BuiltInSystem => 0,
|
||||
StorageId.BuiltInUser => 1,
|
||||
StorageId.SdCard => 3,
|
||||
_ => -1
|
||||
};
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (LocationResolver resolver in _resolvers)
|
||||
{
|
||||
resolver?.Dispose();
|
||||
}
|
||||
|
||||
_aocResolver?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
174
src/LibHac/FsSrv/Impl/Utility.cs
Normal file
174
src/LibHac/FsSrv/Impl/Utility.cs
Normal file
@ -0,0 +1,174 @@
|
||||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.Util;
|
||||
|
||||
namespace LibHac.FsSrv.Impl
|
||||
{
|
||||
internal static class Utility
|
||||
{
|
||||
public static Result EnsureDirectory(IFileSystem fileSystem, U8Span path)
|
||||
{
|
||||
FsPath.FromSpan(out FsPath pathBuffer, ReadOnlySpan<byte>.Empty).IgnoreResult();
|
||||
|
||||
int pathLength = StringUtils.GetLength(path);
|
||||
|
||||
if (pathLength > 0)
|
||||
{
|
||||
// Remove any trailing directory separators
|
||||
while (pathLength > 0 && path[pathLength - 1] == StringTraits.DirectorySeparator)
|
||||
{
|
||||
pathLength--;
|
||||
}
|
||||
|
||||
// Copy the path to a mutable buffer
|
||||
path.Value.Slice(0, pathLength).CopyTo(pathBuffer.Str);
|
||||
}
|
||||
|
||||
pathBuffer.Str[pathLength] = StringTraits.NullTerminator;
|
||||
|
||||
return EnsureDirectoryImpl(fileSystem, pathBuffer.Str.Slice(0, pathLength));
|
||||
}
|
||||
|
||||
private static Result EnsureDirectoryImpl(IFileSystem fileSystem, Span<byte> path)
|
||||
{
|
||||
// Double check the trailing separators have been trimmed
|
||||
Assert.AssertTrue(path.Length == 0 || path[path.Length - 1] != StringTraits.DirectorySeparator);
|
||||
|
||||
// Use the root path if the input path is empty
|
||||
var pathToCheck = new U8Span(path.IsEmpty ? FileSystemRootPath : path);
|
||||
|
||||
// Check if the path exists
|
||||
Result rc = fileSystem.GetEntryType(out DirectoryEntryType entryType, pathToCheck);
|
||||
|
||||
if (rc.IsFailure())
|
||||
{
|
||||
// Something went wrong if we get a result other than PathNotFound
|
||||
if (!ResultFs.PathNotFound.Includes(rc))
|
||||
return rc;
|
||||
|
||||
if (path.Length <= 0)
|
||||
{
|
||||
// The file system either reported that the root directory doesn't exist,
|
||||
// or the input path had a negative length
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
// The path does not exist. Ensure its parent directory exists
|
||||
rc = EnsureParentDirectoryImpl(fileSystem, path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// The parent directory exists, we can now create a directory at the input path
|
||||
rc = fileSystem.CreateDirectory(new U8Span(path));
|
||||
|
||||
if (rc.IsSuccess())
|
||||
{
|
||||
// The directory was successfully created
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
if (!ResultFs.PathAlreadyExists.Includes(rc))
|
||||
return rc;
|
||||
|
||||
// Someone else created a file system entry at the input path after we checked
|
||||
// if the path existed. Get the entry type to check if it's a directory.
|
||||
rc = fileSystem.GetEntryType(out entryType, new U8Span(path));
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
// We want the entry that exists at the input path to be a directory
|
||||
// Return PathAlreadyExists if it's a file
|
||||
if (entryType == DirectoryEntryType.File)
|
||||
return ResultFs.PathAlreadyExists.Log();
|
||||
|
||||
// A directory exists at the input path. Success
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private static Result EnsureParentDirectoryImpl(IFileSystem fileSystem, Span<byte> path)
|
||||
{
|
||||
// The path should not be empty or have a trailing directory separator
|
||||
Assert.AssertTrue(path.Length > 0);
|
||||
Assert.NotEqual(StringTraits.DirectorySeparator, path[path.Length - 1]);
|
||||
|
||||
// Make sure the path's not too long
|
||||
if (path.Length > PathTool.EntryNameLengthMax)
|
||||
return ResultFs.TooLongPath.Log();
|
||||
|
||||
// Iterate until we run out of path or find the next separator
|
||||
int length = path.Length;
|
||||
while (length > 0 && path[length - 1] != StringTraits.DirectorySeparator)
|
||||
{
|
||||
length--;
|
||||
}
|
||||
|
||||
if (length == 0)
|
||||
{
|
||||
// We hit the beginning of the path. Ensure the root directory exists and return
|
||||
return EnsureDirectoryImpl(fileSystem, Span<byte>.Empty);
|
||||
}
|
||||
|
||||
// We found the length of the parent directory. Ensure it exists
|
||||
path[length - 1] = StringTraits.NullTerminator;
|
||||
Result rc = EnsureDirectoryImpl(fileSystem, path.Slice(0, length));
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Restore the separator
|
||||
path[length - 1] = StringTraits.DirectorySeparator;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result CreateSubDirectoryFileSystem(out ReferenceCountedDisposable<IFileSystem> subDirFileSystem,
|
||||
ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path, bool preserveUnc = false)
|
||||
{
|
||||
subDirFileSystem = default;
|
||||
|
||||
if (path.IsNull())
|
||||
return ResultFs.NullptrArgument.Log();
|
||||
|
||||
// Check if the directory exists
|
||||
Result rc = baseFileSystem.Target.OpenDirectory(out IDirectory dir, path, OpenDirectoryMode.Directory);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
dir.Dispose();
|
||||
|
||||
var fs = new SubdirectoryFileSystem(baseFileSystem, preserveUnc);
|
||||
using (var subDirFs = new ReferenceCountedDisposable<SubdirectoryFileSystem>(fs))
|
||||
{
|
||||
rc = subDirFs.Target.Initialize(path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
subDirFileSystem = subDirFs.AddReference<IFileSystem>();
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
public static Result WrapSubDirectory(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path, bool createIfMissing)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
// The path must already exist if we're not automatically creating it
|
||||
if (!createIfMissing)
|
||||
{
|
||||
Result result = baseFileSystem.Target.GetEntryType(out _, path);
|
||||
if (result.IsFailure()) return result;
|
||||
}
|
||||
|
||||
// Ensure the path exists or check if it's a directory
|
||||
Result rc = EnsureDirectory(baseFileSystem.Target, path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return CreateSubDirectoryFileSystem(out fileSystem, baseFileSystem, path);
|
||||
}
|
||||
|
||||
private static ReadOnlySpan<byte> FileSystemRootPath => // /
|
||||
new[]
|
||||
{
|
||||
(byte) '/'
|
||||
};
|
||||
}
|
||||
}
|
326
src/LibHac/FsSrv/NcaFileSystemService.cs
Normal file
326
src/LibHac/FsSrv/NcaFileSystemService.cs
Normal file
@ -0,0 +1,326 @@
|
||||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSrv.Impl;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Spl;
|
||||
using LibHac.Util;
|
||||
|
||||
namespace LibHac.FsSrv
|
||||
{
|
||||
internal class NcaFileSystemService : IRomFileSystemAccessFailureManager, IDisposable
|
||||
{
|
||||
private const int AocSemaphoreCount = 128;
|
||||
private const int RomSemaphoreCount = 10;
|
||||
|
||||
private ReferenceCountedDisposable<NcaFileSystemService>.WeakReference SelfReference { get; set; }
|
||||
private NcaFileSystemServiceImpl ServiceImpl { get; }
|
||||
private ulong ProcessId { get; }
|
||||
private SemaphoreAdaptor AocMountCountSemaphore { get; }
|
||||
private SemaphoreAdaptor RomMountCountSemaphore { get; }
|
||||
|
||||
public NcaFileSystemService(NcaFileSystemServiceImpl serviceImpl, ulong processId)
|
||||
{
|
||||
ServiceImpl = serviceImpl;
|
||||
ProcessId = processId;
|
||||
AocMountCountSemaphore = new SemaphoreAdaptor(AocSemaphoreCount, AocSemaphoreCount);
|
||||
RomMountCountSemaphore = new SemaphoreAdaptor(RomSemaphoreCount, RomSemaphoreCount);
|
||||
}
|
||||
|
||||
public Result OpenFileSystemWithPatch(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
ProgramId programId, FileSystemProxyType fsType)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenCodeFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
out CodeVerificationData verificationData, in FspPath path, ProgramId programId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenDataFileSystemByCurrentProcess(out ReferenceCountedDisposable<IFileSystem> fileSystem)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private Result OpenDataStorageCore(out ReferenceCountedDisposable<IStorage> storage, out Hash ncaHeaderDigest,
|
||||
ulong id, StorageId storageId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenDataStorageByCurrentProcess(out ReferenceCountedDisposable<IStorage> storage)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenDataStorageByProgramId(out ReferenceCountedDisposable<IStorage> storage, ProgramId programId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenFileSystemWithId(out ReferenceCountedDisposable<IFileSystem> fileSystem, in FspPath path,
|
||||
ulong id, FileSystemProxyType fsType)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
AccessControl ac = programInfo.AccessControl;
|
||||
|
||||
switch (fsType)
|
||||
{
|
||||
case FileSystemProxyType.Logo:
|
||||
if (!ac.GetAccessibilityFor(AccessibilityType.MountLogo).CanRead)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
break;
|
||||
case FileSystemProxyType.Control:
|
||||
if (!ac.GetAccessibilityFor(AccessibilityType.MountContentControl).CanRead)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
break;
|
||||
case FileSystemProxyType.Manual:
|
||||
if (!ac.GetAccessibilityFor(AccessibilityType.MountContentManual).CanRead)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
break;
|
||||
case FileSystemProxyType.Meta:
|
||||
if (!ac.GetAccessibilityFor(AccessibilityType.MountContentMeta).CanRead)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
break;
|
||||
case FileSystemProxyType.Data:
|
||||
if (!ac.GetAccessibilityFor(AccessibilityType.MountContentData).CanRead)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
break;
|
||||
case FileSystemProxyType.Package:
|
||||
if (!ac.GetAccessibilityFor(AccessibilityType.MountApplicationPackage).CanRead)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
break;
|
||||
default:
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
}
|
||||
|
||||
if (fsType == FileSystemProxyType.Meta)
|
||||
{
|
||||
id = ulong.MaxValue;
|
||||
}
|
||||
else if (id == ulong.MaxValue)
|
||||
{
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
}
|
||||
|
||||
bool canMountSystemDataPrivate = ac.GetAccessibilityFor(AccessibilityType.MountSystemDataPrivate).CanRead;
|
||||
|
||||
var normalizer = new PathNormalizer(path, GetPathNormalizerOptions(path));
|
||||
if (normalizer.Result.IsFailure()) return normalizer.Result;
|
||||
|
||||
rc = ServiceImpl.OpenFileSystem(out ReferenceCountedDisposable<IFileSystem> baseFs, out _, path, fsType,
|
||||
canMountSystemDataPrivate, id);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
fileSystem = baseFs;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result OpenDataFileSystemByProgramId(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
ProgramId programId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenDataStorageByDataId(out ReferenceCountedDisposable<IStorage> storage, DataId dataId,
|
||||
StorageId storageId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenDataFileSystemWithProgramIndex(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
byte programIndex)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenDataStorageWithProgramIndex(out ReferenceCountedDisposable<IStorage> storage,
|
||||
byte programIndex)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result GetRightsId(out RightsId rightsId, ProgramId programId, StorageId storageId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result GetRightsIdAndKeyGenerationByPath(out RightsId rightsId, out byte keyGeneration, in FspPath path)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private Result OpenDataFileSystemCore(out ReferenceCountedDisposable<IFileSystem> fileSystem, out bool isHostFs,
|
||||
ulong id, StorageId storageId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenContentStorageFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
ContentStorageId contentStorageId)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
Accessibility accessibility =
|
||||
programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountContentStorage);
|
||||
|
||||
if (!accessibility.CanRead || !accessibility.CanWrite)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
rc = ServiceImpl.OpenContentStorageFileSystem(out ReferenceCountedDisposable<IFileSystem> contentFs,
|
||||
contentStorageId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
fileSystem = contentFs;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result RegisterExternalKey(in RightsId rightsId, in AccessKey accessKey)
|
||||
{
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (!programInfo.AccessControl.CanCall(OperationType.RegisterExternalKey))
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
return ServiceImpl.RegisterExternalKey(in rightsId, in accessKey);
|
||||
}
|
||||
|
||||
public Result UnregisterExternalKey(in RightsId rightsId)
|
||||
{
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (!programInfo.AccessControl.CanCall(OperationType.RegisterExternalKey))
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
return ServiceImpl.UnregisterExternalKey(in rightsId);
|
||||
}
|
||||
|
||||
public Result UnregisterAllExternalKey()
|
||||
{
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (!programInfo.AccessControl.CanCall(OperationType.RegisterExternalKey))
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
return ServiceImpl.UnregisterAllExternalKey();
|
||||
}
|
||||
|
||||
public Result RegisterUpdatePartition()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenRegisteredUpdatePartition(out ReferenceCountedDisposable<IFileSystem> fileSystem)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result IsArchivedProgram(out bool isArchived, ulong processId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result SetSdCardEncryptionSeed(in EncryptionSeed encryptionSeed)
|
||||
{
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (!programInfo.AccessControl.CanCall(OperationType.SetEncryptionSeed))
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
return ServiceImpl.SetSdCardEncryptionSeed(in encryptionSeed);
|
||||
}
|
||||
|
||||
public Result OpenSystemDataUpdateEventNotifier(out ReferenceCountedDisposable<IEventNotifier> eventNotifier)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result NotifySystemDataUpdateEvent()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result HandleResolubleAccessFailure(out bool wasDeferred, in Result nonDeferredResult)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void IncrementRomFsRemountForDataCorruptionCount()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void IncrementRomFsUnrecoverableDataCorruptionByRemountCount()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void IncrementRomFsRecoveredByInvalidateCacheCount()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Result IRomFileSystemAccessFailureManager.OpenDataStorageCore(out ReferenceCountedDisposable<IStorage> storage,
|
||||
out Hash ncaHeaderDigest, ulong id, StorageId storageId)
|
||||
{
|
||||
return OpenDataStorageCore(out storage, out ncaHeaderDigest, id, storageId);
|
||||
}
|
||||
|
||||
internal void SetSelfReference(ReferenceCountedDisposable<NcaFileSystemService> reference)
|
||||
{
|
||||
SelfReference = new ReferenceCountedDisposable<NcaFileSystemService>.WeakReference(reference);
|
||||
}
|
||||
|
||||
private Result TryAcquireAddOnContentOpenCountSemaphore(out IUniqueLock semaphoreLock)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private Result TryAcquireRomMountCountSemaphore(out IUniqueLock semaphoreLock)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private Result GetProgramInfo(out ProgramInfo programInfo)
|
||||
{
|
||||
return ServiceImpl.GetProgramInfo(out programInfo, ProcessId);
|
||||
}
|
||||
|
||||
private PathNormalizer.Option GetPathNormalizerOptions(U8Span path)
|
||||
{
|
||||
// Set the PreserveUnc flag if the path is on the host file system
|
||||
PathNormalizer.Option hostOption = IsHostFs(path) ? PathNormalizer.Option.PreserveUnc : PathNormalizer.Option.None;
|
||||
return PathNormalizer.Option.HasMountName | PathNormalizer.Option.PreserveTailSeparator | hostOption;
|
||||
}
|
||||
|
||||
private bool IsHostFs(U8Span path)
|
||||
{
|
||||
int hostMountLength = StringUtils.GetLength(CommonMountNames.HostRootFileSystemMountName,
|
||||
PathTools.MountNameLengthMax);
|
||||
|
||||
return StringUtils.Compare(path, CommonMountNames.HostRootFileSystemMountName, hostMountLength) == 0;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
AocMountCountSemaphore?.Dispose();
|
||||
RomMountCountSemaphore?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
836
src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs
Normal file
836
src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs
Normal file
@ -0,0 +1,836 @@
|
||||
using System;
|
||||
using System.Buffers.Text;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSrv.Creators;
|
||||
using LibHac.FsSrv.Impl;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.FsSystem.NcaUtils;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Spl;
|
||||
using LibHac.Util;
|
||||
using RightsId = LibHac.Fs.RightsId;
|
||||
using Utility = LibHac.FsSrv.Impl.Utility;
|
||||
|
||||
namespace LibHac.FsSrv
|
||||
{
|
||||
public class NcaFileSystemServiceImpl
|
||||
{
|
||||
private Configuration _config;
|
||||
// UpdatePartitionPath
|
||||
private ExternalKeySet _externalKeyManager;
|
||||
private LocationResolverSet _locationResolverSet;
|
||||
// SystemDataUpdateEventManager
|
||||
private EncryptionSeed _encryptionSeed;
|
||||
private int _romFsRemountForDataCorruptionCount;
|
||||
private int _romfsUnrecoverableDataCorruptionByRemountCount;
|
||||
private int _romFsRecoveredByInvalidateCacheCount;
|
||||
private object _romfsCountLocker;
|
||||
|
||||
public NcaFileSystemServiceImpl(in Configuration configuration, ExternalKeySet externalKeySet)
|
||||
{
|
||||
_config = configuration;
|
||||
_externalKeyManager = externalKeySet;
|
||||
_locationResolverSet = new LocationResolverSet(_config.HorizonClient);
|
||||
_romfsCountLocker = new object();
|
||||
}
|
||||
|
||||
public struct Configuration
|
||||
{
|
||||
public BaseFileSystemServiceImpl BaseFsService;
|
||||
public IHostFileSystemCreator HostFsCreator;
|
||||
public ITargetManagerFileSystemCreator TargetManagerFsCreator;
|
||||
public IPartitionFileSystemCreator PartitionFsCreator;
|
||||
public IRomFileSystemCreator RomFsCreator;
|
||||
public IStorageOnNcaCreator StorageOnNcaCreator;
|
||||
public ISubDirectoryFileSystemCreator SubDirectoryFsCreator;
|
||||
public IEncryptedFileSystemCreator EncryptedFsCreator;
|
||||
public ProgramRegistryServiceImpl ProgramRegistryService;
|
||||
// AccessFailureService
|
||||
public InternalProgramIdRangeForSpeedEmulation SpeedEmulationRange;
|
||||
|
||||
// LibHac additions
|
||||
public HorizonClient HorizonClient;
|
||||
public ProgramRegistryImpl ProgramRegistry;
|
||||
}
|
||||
|
||||
public Result OpenFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
out CodeVerificationData verificationData, U8Span path, FileSystemProxyType type,
|
||||
bool canMountSystemDataPrivate, ulong id)
|
||||
{
|
||||
fileSystem = default;
|
||||
Unsafe.SkipInit(out verificationData);
|
||||
verificationData.IsValid = false;
|
||||
|
||||
// Get a reference to the path that will be advanced as each part of the path is parsed
|
||||
U8Span currentPath = path.Slice(0, StringUtils.GetLength(path));
|
||||
|
||||
// Open the root filesystem based on the path's mount name
|
||||
Result rc = OpenFileSystemFromMountName(ref currentPath,
|
||||
out ReferenceCountedDisposable<IFileSystem> baseFileSystem, out bool shouldContinue,
|
||||
out MountNameInfo mountNameInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Don't continue if the rest of the path is empty
|
||||
if (!shouldContinue)
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
|
||||
if (type == FileSystemProxyType.Logo && mountNameInfo.IsGameCard)
|
||||
{
|
||||
rc = _config.BaseFsService.OpenGameCardFileSystem(out fileSystem,
|
||||
new GameCardHandle(mountNameInfo.GcHandle),
|
||||
GameCardPartition.Logo);
|
||||
|
||||
if (rc.IsSuccess())
|
||||
return Result.Success;
|
||||
|
||||
if (!ResultFs.PartitionNotFound.Includes(rc))
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = IsContentPathDir(ref currentPath, out bool isDirectory);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (isDirectory)
|
||||
{
|
||||
if (!mountNameInfo.IsHostFs)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
if (type == FileSystemProxyType.Manual)
|
||||
{
|
||||
rc = TryOpenCaseSensitiveContentDirectory(
|
||||
out ReferenceCountedDisposable<IFileSystem> manualFileSystem, baseFileSystem, currentPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var readOnlyFs = new ReadOnlyFileSystem(manualFileSystem);
|
||||
fileSystem = new ReferenceCountedDisposable<IFileSystem>(readOnlyFs);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
return TryOpenContentDirectory(currentPath, out fileSystem, baseFileSystem, type, true);
|
||||
}
|
||||
|
||||
rc = TryOpenNsp(ref currentPath, out ReferenceCountedDisposable<IFileSystem> nspFileSystem, baseFileSystem);
|
||||
|
||||
if (rc.IsSuccess())
|
||||
{
|
||||
// Must be the end of the path to open Application Package FS type
|
||||
if (currentPath.Length == 0 || currentPath[0] == 0)
|
||||
{
|
||||
if (type == FileSystemProxyType.Package)
|
||||
{
|
||||
fileSystem = nspFileSystem;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
}
|
||||
|
||||
baseFileSystem = nspFileSystem;
|
||||
}
|
||||
|
||||
if (!mountNameInfo.CanMountNca)
|
||||
{
|
||||
return ResultFs.InvalidNcaMountPoint.Log();
|
||||
}
|
||||
|
||||
ulong openProgramId = mountNameInfo.IsHostFs ? ulong.MaxValue : id;
|
||||
|
||||
rc = TryOpenNca(ref currentPath, out Nca nca, baseFileSystem, openProgramId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = OpenNcaStorage(out ReferenceCountedDisposable<IStorage> ncaSectionStorage, nca,
|
||||
out NcaFormatType fsType, type, mountNameInfo.IsGameCard, canMountSystemDataPrivate);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
switch (fsType)
|
||||
{
|
||||
case NcaFormatType.Romfs:
|
||||
return _config.RomFsCreator.Create(out fileSystem, ncaSectionStorage);
|
||||
case NcaFormatType.Pfs0:
|
||||
return _config.PartitionFsCreator.Create(out fileSystem, ncaSectionStorage);
|
||||
default:
|
||||
return ResultFs.InvalidNcaFileSystemType.Log();
|
||||
}
|
||||
}
|
||||
|
||||
private struct MountNameInfo
|
||||
{
|
||||
public bool IsGameCard;
|
||||
public int GcHandle;
|
||||
public bool IsHostFs;
|
||||
public bool CanMountNca;
|
||||
}
|
||||
|
||||
private Result OpenFileSystemFromMountName(ref U8Span path,
|
||||
out ReferenceCountedDisposable<IFileSystem> fileSystem, out bool shouldContinue, out MountNameInfo info)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
info = new MountNameInfo();
|
||||
shouldContinue = true;
|
||||
|
||||
if (StringUtils.Compare(path, CommonMountNames.GameCardFileSystemMountName,
|
||||
CommonMountNames.GameCardFileSystemMountName.Length) == 0)
|
||||
{
|
||||
path = path.Slice(CommonMountNames.GameCardFileSystemMountName.Length);
|
||||
|
||||
if (StringUtils.GetLength(path.Value, 9) < 9)
|
||||
return ResultFs.InvalidPath.Log();
|
||||
|
||||
GameCardPartition partition;
|
||||
switch ((char)path[0])
|
||||
{
|
||||
case CommonMountNames.GameCardFileSystemMountNameUpdateSuffix:
|
||||
partition = GameCardPartition.Update;
|
||||
break;
|
||||
case CommonMountNames.GameCardFileSystemMountNameNormalSuffix:
|
||||
partition = GameCardPartition.Normal;
|
||||
break;
|
||||
case CommonMountNames.GameCardFileSystemMountNameSecureSuffix:
|
||||
partition = GameCardPartition.Secure;
|
||||
break;
|
||||
default:
|
||||
return ResultFs.InvalidPath.Log();
|
||||
}
|
||||
|
||||
path = path.Slice(1);
|
||||
bool handleParsed = Utf8Parser.TryParse(path, out int handle, out int bytesConsumed);
|
||||
|
||||
if (!handleParsed || handle == -1 || bytesConsumed != 8)
|
||||
return ResultFs.InvalidPath.Log();
|
||||
|
||||
path = path.Slice(8);
|
||||
|
||||
Result rc = _config.BaseFsService.OpenGameCardFileSystem(out fileSystem, new GameCardHandle(handle),
|
||||
partition);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
info.GcHandle = handle;
|
||||
info.IsGameCard = true;
|
||||
info.CanMountNca = true;
|
||||
}
|
||||
|
||||
else if (StringUtils.Compare(path, CommonMountNames.ContentStorageSystemMountName,
|
||||
CommonMountNames.ContentStorageSystemMountName.Length) == 0)
|
||||
{
|
||||
path = path.Slice(CommonMountNames.ContentStorageSystemMountName.Length);
|
||||
|
||||
Result rc = OpenContentStorageFileSystem(out fileSystem, ContentStorageId.System);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
info.CanMountNca = true;
|
||||
}
|
||||
|
||||
else if (StringUtils.Compare(path, CommonMountNames.ContentStorageUserMountName,
|
||||
CommonMountNames.ContentStorageUserMountName.Length) == 0)
|
||||
{
|
||||
path = path.Slice(CommonMountNames.ContentStorageUserMountName.Length);
|
||||
|
||||
Result rc = OpenContentStorageFileSystem(out fileSystem, ContentStorageId.User);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
info.CanMountNca = true;
|
||||
}
|
||||
|
||||
else if (StringUtils.Compare(path, CommonMountNames.ContentStorageSdCardMountName,
|
||||
CommonMountNames.ContentStorageSdCardMountName.Length) == 0)
|
||||
{
|
||||
path = path.Slice(CommonMountNames.ContentStorageSdCardMountName.Length);
|
||||
|
||||
Result rc = OpenContentStorageFileSystem(out fileSystem, ContentStorageId.SdCard);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
info.CanMountNca = true;
|
||||
}
|
||||
|
||||
else if (StringUtils.Compare(path, CommonMountNames.BisCalibrationFilePartitionMountName,
|
||||
CommonMountNames.BisCalibrationFilePartitionMountName.Length) == 0)
|
||||
{
|
||||
path = path.Slice(CommonMountNames.BisCalibrationFilePartitionMountName.Length);
|
||||
|
||||
Result rc = _config.BaseFsService.OpenBisFileSystem(out fileSystem, U8Span.Empty,
|
||||
BisPartitionId.CalibrationFile);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
else if (StringUtils.Compare(path, CommonMountNames.BisSafeModePartitionMountName,
|
||||
CommonMountNames.BisSafeModePartitionMountName.Length) == 0)
|
||||
{
|
||||
path = path.Slice(CommonMountNames.BisSafeModePartitionMountName.Length);
|
||||
|
||||
Result rc = _config.BaseFsService.OpenBisFileSystem(out fileSystem, U8Span.Empty,
|
||||
BisPartitionId.SafeMode);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
else if (StringUtils.Compare(path, CommonMountNames.BisUserPartitionMountName,
|
||||
CommonMountNames.BisUserPartitionMountName.Length) == 0)
|
||||
{
|
||||
path = path.Slice(CommonMountNames.BisUserPartitionMountName.Length);
|
||||
|
||||
Result rc = _config.BaseFsService.OpenBisFileSystem(out fileSystem, U8Span.Empty, BisPartitionId.User);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
else if (StringUtils.Compare(path, CommonMountNames.BisSystemPartitionMountName,
|
||||
CommonMountNames.BisSystemPartitionMountName.Length) == 0)
|
||||
{
|
||||
path = path.Slice(CommonMountNames.BisSystemPartitionMountName.Length);
|
||||
|
||||
Result rc = _config.BaseFsService.OpenBisFileSystem(out fileSystem, U8Span.Empty,
|
||||
BisPartitionId.System);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
else if (StringUtils.Compare(path, CommonMountNames.SdCardFileSystemMountName,
|
||||
CommonMountNames.SdCardFileSystemMountName.Length) == 0)
|
||||
{
|
||||
path = path.Slice(CommonMountNames.SdCardFileSystemMountName.Length);
|
||||
|
||||
Result rc = _config.BaseFsService.OpenSdCardProxyFileSystem(out fileSystem);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
else if (StringUtils.Compare(path, CommonMountNames.HostRootFileSystemMountName,
|
||||
CommonMountNames.HostRootFileSystemMountName.Length) == 0)
|
||||
{
|
||||
path = path.Slice(CommonMountNames.HostRootFileSystemMountName.Length);
|
||||
|
||||
info.IsHostFs = true;
|
||||
info.CanMountNca = true;
|
||||
|
||||
Result rc = OpenHostFileSystem(out fileSystem, U8Span.Empty, openCaseSensitive: false);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
else if (StringUtils.Compare(path, CommonMountNames.RegisteredUpdatePartitionMountName,
|
||||
CommonMountNames.RegisteredUpdatePartitionMountName.Length) == 0)
|
||||
{
|
||||
path = path.Slice(CommonMountNames.RegisteredUpdatePartitionMountName.Length);
|
||||
|
||||
info.CanMountNca = true;
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
if (StringUtils.GetLength(path, FsPath.MaxLength) == 0)
|
||||
{
|
||||
shouldContinue = false;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private Result IsContentPathDir(ref U8Span path, out bool isDirectory)
|
||||
{
|
||||
isDirectory = default;
|
||||
|
||||
ReadOnlySpan<byte> mountSeparator = new[] { (byte)':', (byte)'/' };
|
||||
|
||||
if (StringUtils.Compare(mountSeparator, path, mountSeparator.Length) != 0)
|
||||
{
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
path = path.Slice(1);
|
||||
int pathLen = StringUtils.GetLength(path);
|
||||
|
||||
if (path[pathLen - 1] == '/')
|
||||
{
|
||||
isDirectory = true;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
// Now make sure the path has a content file extension
|
||||
if (pathLen < 5)
|
||||
return ResultFs.PathNotFound.Log();
|
||||
|
||||
ReadOnlySpan<byte> fileExtension = path.Value.Slice(pathLen - 4);
|
||||
|
||||
ReadOnlySpan<byte> ncaExtension = new[] { (byte)'.', (byte)'n', (byte)'c', (byte)'a' };
|
||||
ReadOnlySpan<byte> nspExtension = new[] { (byte)'.', (byte)'n', (byte)'s', (byte)'p' };
|
||||
|
||||
if (StringUtils.CompareCaseInsensitive(fileExtension, ncaExtension) == 0 ||
|
||||
StringUtils.CompareCaseInsensitive(fileExtension, nspExtension) == 0)
|
||||
{
|
||||
isDirectory = false;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
private Result TryOpenContentDirectory(U8Span path,
|
||||
out ReferenceCountedDisposable<IFileSystem> contentFileSystem,
|
||||
ReferenceCountedDisposable<IFileSystem> baseFileSystem, FileSystemProxyType fsType, bool preserveUnc)
|
||||
{
|
||||
contentFileSystem = default;
|
||||
|
||||
Result rc = _config.SubDirectoryFsCreator.Create(out ReferenceCountedDisposable<IFileSystem> subDirFs,
|
||||
baseFileSystem, path, preserveUnc);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return OpenSubDirectoryForFsType(out contentFileSystem, subDirFs, fsType);
|
||||
}
|
||||
|
||||
private Result TryOpenCaseSensitiveContentDirectory(
|
||||
out ReferenceCountedDisposable<IFileSystem> contentFileSystem,
|
||||
ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path)
|
||||
{
|
||||
contentFileSystem = default;
|
||||
Unsafe.SkipInit(out FsPath fullPath);
|
||||
|
||||
var sb = new U8StringBuilder(fullPath.Str);
|
||||
sb.Append(path)
|
||||
.Append(new[] { (byte)'d', (byte)'a', (byte)'t', (byte)'a', (byte)'/' });
|
||||
|
||||
if (sb.Overflowed)
|
||||
return ResultFs.TooLongPath.Log();
|
||||
|
||||
Result rc = _config.TargetManagerFsCreator.GetCaseSensitivePath(out bool success, fullPath.Str);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Reopen the host filesystem as case sensitive
|
||||
if (success)
|
||||
{
|
||||
baseFileSystem.Dispose();
|
||||
|
||||
rc = OpenHostFileSystem(out baseFileSystem, U8Span.Empty, openCaseSensitive: true);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
return _config.SubDirectoryFsCreator.Create(out contentFileSystem, baseFileSystem, fullPath);
|
||||
}
|
||||
|
||||
private Result OpenSubDirectoryForFsType(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
ReferenceCountedDisposable<IFileSystem> baseFileSystem, FileSystemProxyType fsType)
|
||||
{
|
||||
fileSystem = default;
|
||||
ReadOnlySpan<byte> dirName;
|
||||
|
||||
// Get the name of the subdirectory for the filesystem type
|
||||
switch (fsType)
|
||||
{
|
||||
case FileSystemProxyType.Package:
|
||||
fileSystem = baseFileSystem;
|
||||
return Result.Success;
|
||||
|
||||
case FileSystemProxyType.Code:
|
||||
dirName = new[] { (byte)'/', (byte)'c', (byte)'o', (byte)'d', (byte)'e', (byte)'/' };
|
||||
break;
|
||||
case FileSystemProxyType.Rom:
|
||||
case FileSystemProxyType.Control:
|
||||
case FileSystemProxyType.Manual:
|
||||
case FileSystemProxyType.Meta:
|
||||
case FileSystemProxyType.RegisteredUpdate:
|
||||
dirName = new[] { (byte)'/', (byte)'d', (byte)'a', (byte)'t', (byte)'a', (byte)'/' };
|
||||
break;
|
||||
case FileSystemProxyType.Logo:
|
||||
dirName = new[] { (byte)'/', (byte)'l', (byte)'o', (byte)'g', (byte)'o', (byte)'/' };
|
||||
break;
|
||||
|
||||
default:
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
}
|
||||
|
||||
// Open the subdirectory filesystem
|
||||
Result rc = _config.SubDirectoryFsCreator.Create(out ReferenceCountedDisposable<IFileSystem> subDirFs,
|
||||
baseFileSystem, new U8Span(dirName));
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (fsType == FileSystemProxyType.Code)
|
||||
{
|
||||
rc = _config.StorageOnNcaCreator.VerifyAcidSignature(subDirFs.Target, null);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
fileSystem = subDirFs;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private Result TryOpenNsp(ref U8Span path, out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
ReferenceCountedDisposable<IFileSystem> baseFileSystem)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
ReadOnlySpan<byte> nspExtension = new[] { (byte)'.', (byte)'n', (byte)'s', (byte)'p' };
|
||||
|
||||
// Search for the end of the nsp part of the path
|
||||
int nspPathLen = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
U8Span currentSpan;
|
||||
|
||||
while (true)
|
||||
{
|
||||
currentSpan = path.Slice(nspPathLen);
|
||||
if (StringUtils.CompareCaseInsensitive(nspExtension, currentSpan, 4) == 0)
|
||||
break;
|
||||
|
||||
if (currentSpan.Length == 0 || currentSpan[0] == 0)
|
||||
{
|
||||
return ResultFs.PathNotFound.Log();
|
||||
}
|
||||
|
||||
nspPathLen++;
|
||||
}
|
||||
|
||||
// The nsp filename must be the end of the entire path or the end of a path segment
|
||||
if (currentSpan.Length <= 4 || currentSpan[4] == 0 || currentSpan[4] == (byte)'/')
|
||||
break;
|
||||
|
||||
nspPathLen += 4;
|
||||
}
|
||||
|
||||
nspPathLen += 4;
|
||||
|
||||
if (nspPathLen > FsPath.MaxLength + 1)
|
||||
return ResultFs.TooLongPath.Log();
|
||||
|
||||
Result rc = FsPath.FromSpan(out FsPath nspPath, path.Slice(0, nspPathLen));
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var storage = new FileStorageBasedFileSystem();
|
||||
using var nspFileStorage = new ReferenceCountedDisposable<FileStorageBasedFileSystem>(storage);
|
||||
|
||||
rc = nspFileStorage.Target.Initialize(baseFileSystem, new U8Span(nspPath.Str), OpenMode.Read);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = _config.PartitionFsCreator.Create(out fileSystem, nspFileStorage.AddReference<IStorage>());
|
||||
|
||||
if (rc.IsSuccess())
|
||||
{
|
||||
path = path.Slice(nspPathLen);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
private Result TryOpenNca(ref U8Span path, out Nca nca, ReferenceCountedDisposable<IFileSystem> baseFileSystem,
|
||||
ulong ncaId)
|
||||
{
|
||||
nca = default;
|
||||
|
||||
// Todo: Create ref-counted storage
|
||||
var ncaFileStorage = new FileStorageBasedFileSystem();
|
||||
|
||||
Result rc = ncaFileStorage.Initialize(baseFileSystem, path, OpenMode.Read);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = _config.StorageOnNcaCreator.OpenNca(out Nca ncaTemp, ncaFileStorage);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (ncaId == ulong.MaxValue)
|
||||
{
|
||||
ulong ncaProgramId = ncaTemp.Header.TitleId;
|
||||
|
||||
if (ncaProgramId != ulong.MaxValue && ncaId != ncaProgramId)
|
||||
{
|
||||
return ResultFs.InvalidNcaId.Log();
|
||||
}
|
||||
}
|
||||
|
||||
nca = ncaTemp;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private Result OpenNcaStorage(out ReferenceCountedDisposable<IStorage> ncaStorage, Nca nca,
|
||||
out NcaFormatType fsType, FileSystemProxyType fsProxyType, bool isGameCard, bool canMountSystemDataPrivate)
|
||||
{
|
||||
ncaStorage = default;
|
||||
fsType = default;
|
||||
|
||||
NcaContentType contentType = nca.Header.ContentType;
|
||||
|
||||
switch (fsProxyType)
|
||||
{
|
||||
case FileSystemProxyType.Code:
|
||||
case FileSystemProxyType.Rom:
|
||||
case FileSystemProxyType.Logo:
|
||||
case FileSystemProxyType.RegisteredUpdate:
|
||||
if (contentType != NcaContentType.Program)
|
||||
return ResultFs.PreconditionViolation.Log();
|
||||
|
||||
break;
|
||||
|
||||
case FileSystemProxyType.Control:
|
||||
if (contentType != NcaContentType.Control)
|
||||
return ResultFs.PreconditionViolation.Log();
|
||||
|
||||
break;
|
||||
case FileSystemProxyType.Manual:
|
||||
if (contentType != NcaContentType.Manual)
|
||||
return ResultFs.PreconditionViolation.Log();
|
||||
|
||||
break;
|
||||
case FileSystemProxyType.Meta:
|
||||
if (contentType != NcaContentType.Meta)
|
||||
return ResultFs.PreconditionViolation.Log();
|
||||
|
||||
break;
|
||||
case FileSystemProxyType.Data:
|
||||
if (contentType != NcaContentType.Data && contentType != NcaContentType.PublicData)
|
||||
return ResultFs.PreconditionViolation.Log();
|
||||
|
||||
if (contentType == NcaContentType.Data && !canMountSystemDataPrivate)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
break;
|
||||
default:
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
}
|
||||
|
||||
if (nca.Header.DistributionType == DistributionType.GameCard && !isGameCard)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
Result rc = SetNcaExternalKey(nca);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = GetNcaSectionIndex(out int sectionIndex, fsProxyType);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = _config.StorageOnNcaCreator.Create(out ncaStorage, out NcaFsHeader fsHeader, nca,
|
||||
sectionIndex, fsProxyType == FileSystemProxyType.Code);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
fsType = fsHeader.FormatType;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private Result SetNcaExternalKey(Nca nca)
|
||||
{
|
||||
var rightsId = new RightsId(nca.Header.RightsId);
|
||||
var zero = new RightsId(0, 0);
|
||||
|
||||
if (Crypto.CryptoUtil.IsSameBytes(rightsId.AsBytes(), zero.AsBytes(), Unsafe.SizeOf<RightsId>()))
|
||||
return Result.Success;
|
||||
|
||||
// ReSharper disable once UnusedVariable
|
||||
Result rc = _externalKeyManager.Get(rightsId, out AccessKey accessKey);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// todo: Set key in nca reader
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private Result GetNcaSectionIndex(out int index, FileSystemProxyType fspType)
|
||||
{
|
||||
switch (fspType)
|
||||
{
|
||||
case FileSystemProxyType.Code:
|
||||
case FileSystemProxyType.Control:
|
||||
case FileSystemProxyType.Manual:
|
||||
case FileSystemProxyType.Meta:
|
||||
case FileSystemProxyType.Data:
|
||||
index = 0;
|
||||
return Result.Success;
|
||||
case FileSystemProxyType.Rom:
|
||||
case FileSystemProxyType.RegisteredUpdate:
|
||||
index = 1;
|
||||
return Result.Success;
|
||||
case FileSystemProxyType.Logo:
|
||||
index = 2;
|
||||
return Result.Success;
|
||||
default:
|
||||
index = default;
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
}
|
||||
}
|
||||
|
||||
public Result OpenHostFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, U8Span path, bool openCaseSensitive)
|
||||
{
|
||||
fileSystem = default;
|
||||
Result rc;
|
||||
|
||||
if (!path.IsEmpty())
|
||||
{
|
||||
rc = Util.VerifyHostPath(path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
rc = _config.TargetManagerFsCreator.Create(out ReferenceCountedDisposable<IFileSystem> hostFs,
|
||||
openCaseSensitive);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (path.IsEmpty())
|
||||
{
|
||||
ReadOnlySpan<byte> rootHostPath = new[] { (byte)'C', (byte)':', (byte)'/' };
|
||||
rc = hostFs.Target.GetEntryType(out _, new U8Span(rootHostPath));
|
||||
|
||||
// Nintendo ignores all results other than this one
|
||||
if (ResultFs.TargetNotFound.Includes(rc))
|
||||
return rc;
|
||||
|
||||
fileSystem = hostFs;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
rc = _config.SubDirectoryFsCreator.Create(out ReferenceCountedDisposable<IFileSystem> subDirFs, hostFs,
|
||||
path, preserveUnc: true);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
fileSystem = subDirFs;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result OpenFileSystemWithPatch(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
ProgramId programId, FileSystemProxyType fsType)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenContentStorageFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
ContentStorageId contentStorageId)
|
||||
{
|
||||
const int storagePathMaxLen = 0x40;
|
||||
|
||||
fileSystem = default;
|
||||
|
||||
ReferenceCountedDisposable<IFileSystem> baseFileSystem = null;
|
||||
ReferenceCountedDisposable<IFileSystem> subDirFileSystem = null;
|
||||
ReferenceCountedDisposable<IFileSystem> encryptedFileSystem = null;
|
||||
|
||||
try
|
||||
{
|
||||
Result rc;
|
||||
|
||||
// Open the appropriate base file system for the content storage ID
|
||||
switch (contentStorageId)
|
||||
{
|
||||
case ContentStorageId.System:
|
||||
rc = _config.BaseFsService.OpenBisFileSystem(out baseFileSystem, U8Span.Empty,
|
||||
BisPartitionId.System);
|
||||
break;
|
||||
case ContentStorageId.User:
|
||||
rc = _config.BaseFsService.OpenBisFileSystem(out baseFileSystem, U8Span.Empty,
|
||||
BisPartitionId.User);
|
||||
break;
|
||||
case ContentStorageId.SdCard:
|
||||
rc = _config.BaseFsService.OpenSdCardProxyFileSystem(out baseFileSystem);
|
||||
break;
|
||||
default:
|
||||
rc = ResultFs.InvalidArgument.Log();
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Build the appropriate path for the content storage ID
|
||||
Span<byte> contentStoragePath = stackalloc byte[storagePathMaxLen];
|
||||
|
||||
if (contentStorageId == ContentStorageId.SdCard)
|
||||
{
|
||||
var sb = new U8StringBuilder(contentStoragePath);
|
||||
sb.Append(StringTraits.DirectorySeparator).Append(SdCardNintendoRootDirectoryName);
|
||||
sb.Append(StringTraits.DirectorySeparator).Append(ContentStorageDirectoryName);
|
||||
}
|
||||
else
|
||||
{
|
||||
var sb = new U8StringBuilder(contentStoragePath);
|
||||
sb.Append(StringTraits.DirectorySeparator).Append(ContentStorageDirectoryName);
|
||||
}
|
||||
|
||||
// Make sure the content storage path exists
|
||||
// ReSharper disable once PossibleNullReferenceException
|
||||
rc = Utility.EnsureDirectory(baseFileSystem.Target, new U8Span(contentStoragePath));
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = _config.SubDirectoryFsCreator.Create(out subDirFileSystem, baseFileSystem,
|
||||
new U8Span(contentStoragePath));
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Only content on the SD card is encrypted
|
||||
if (contentStorageId != ContentStorageId.SdCard)
|
||||
{
|
||||
// Move the shared reference to the out variable
|
||||
fileSystem = subDirFileSystem;
|
||||
subDirFileSystem = null;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
// Create an encrypted file system for SD card content storage
|
||||
rc = _config.EncryptedFsCreator.Create(out encryptedFileSystem, subDirFileSystem,
|
||||
EncryptedFsKeyId.Content, in _encryptionSeed);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
fileSystem = encryptedFileSystem;
|
||||
encryptedFileSystem = null;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
baseFileSystem?.Dispose();
|
||||
subDirFileSystem?.Dispose();
|
||||
encryptedFileSystem?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public Result RegisterExternalKey(in RightsId rightsId, in AccessKey accessKey)
|
||||
{
|
||||
return _externalKeyManager.Add(rightsId, accessKey);
|
||||
}
|
||||
|
||||
public Result UnregisterExternalKey(in RightsId rightsId)
|
||||
{
|
||||
_externalKeyManager.Remove(rightsId);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result UnregisterAllExternalKey()
|
||||
{
|
||||
_externalKeyManager.Clear();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result SetSdCardEncryptionSeed(in EncryptionSeed encryptionSeed)
|
||||
{
|
||||
_encryptionSeed = encryptionSeed;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
internal Result GetProgramInfo(out ProgramInfo programInfo, ulong processId)
|
||||
{
|
||||
return _config.ProgramRegistry.GetProgramInfo(out programInfo, processId);
|
||||
}
|
||||
|
||||
private static ReadOnlySpan<byte> SdCardNintendoRootDirectoryName => // Nintendo
|
||||
new[]
|
||||
{
|
||||
(byte) 'N', (byte) 'i', (byte) 'n', (byte) 't', (byte) 'e', (byte) 'n', (byte) 'd', (byte) 'o'
|
||||
};
|
||||
|
||||
private static ReadOnlySpan<byte> ContentStorageDirectoryName => // Contents
|
||||
new[]
|
||||
{
|
||||
(byte) 'C', (byte) 'o', (byte) 'n', (byte) 't', (byte) 'e', (byte) 'n', (byte) 't', (byte) 's'
|
||||
};
|
||||
}
|
||||
|
||||
public readonly struct InternalProgramIdRangeForSpeedEmulation
|
||||
{
|
||||
public readonly ulong ProgramIdMin;
|
||||
public readonly ulong ProgramIdMax;
|
||||
|
||||
public InternalProgramIdRangeForSpeedEmulation(ulong min, ulong max)
|
||||
{
|
||||
ProgramIdMin = min;
|
||||
ProgramIdMax = max;
|
||||
}
|
||||
}
|
||||
}
|
10
src/LibHac/FsSrv/Sf/IEventNotifier.cs
Normal file
10
src/LibHac/FsSrv/Sf/IEventNotifier.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System;
|
||||
using LibHac.Sf;
|
||||
|
||||
namespace LibHac.FsSrv.Sf
|
||||
{
|
||||
public interface IEventNotifier : IDisposable
|
||||
{
|
||||
Result GetEventHandle(out NativeHandle handle);
|
||||
}
|
||||
}
|
@ -6,8 +6,8 @@ namespace LibHac.FsSrv.Sf
|
||||
{
|
||||
public interface IFileSystemProxyForLoader
|
||||
{
|
||||
Result OpenCodeFileSystem(out IFileSystem fileSystem, out CodeVerificationData verificationData,
|
||||
in FspPath path, ProgramId programId);
|
||||
Result OpenCodeFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
out CodeVerificationData verificationData, in FspPath path, ProgramId programId);
|
||||
|
||||
Result IsArchivedProgram(out bool isArchived, ulong processId);
|
||||
Result SetCurrentProcess(ulong processId);
|
||||
|
17
src/LibHac/FsSystem/Hash.cs
Normal file
17
src/LibHac/FsSystem/Hash.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common;
|
||||
|
||||
namespace LibHac.FsSystem
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x20)]
|
||||
public struct Hash
|
||||
{
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0;
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1;
|
||||
|
||||
public readonly ReadOnlySpan<byte> Bytes => SpanHelpers.AsReadOnlyByteSpan(in this);
|
||||
public Span<byte> BytesMutable => SpanHelpers.AsByteSpan(ref this);
|
||||
}
|
||||
}
|
8
src/LibHac/FsSystem/IUniqueLock.cs
Normal file
8
src/LibHac/FsSystem/IUniqueLock.cs
Normal file
@ -0,0 +1,8 @@
|
||||
using System;
|
||||
|
||||
namespace LibHac.FsSystem
|
||||
{
|
||||
public interface IUniqueLock : IDisposable
|
||||
{
|
||||
}
|
||||
}
|
@ -39,7 +39,7 @@
|
||||
Code,
|
||||
Data,
|
||||
Logo
|
||||
};
|
||||
}
|
||||
|
||||
public enum NcaContentType
|
||||
{
|
||||
|
@ -15,6 +15,16 @@ namespace LibHac.FsSystem
|
||||
private PartitionFileSystemMetaCore<T> MetaData { get; set; }
|
||||
private bool IsInitialized { get; set; }
|
||||
private int DataOffset { get; set; }
|
||||
private ReferenceCountedDisposable<IStorage> BaseStorageShared { get; set; }
|
||||
|
||||
public Result Initialize(ReferenceCountedDisposable<IStorage> baseStorage)
|
||||
{
|
||||
Result rc = Initialize(baseStorage.Target);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
BaseStorageShared = baseStorage.AddReference();
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result Initialize(IStorage baseStorage)
|
||||
{
|
||||
@ -33,6 +43,16 @@ namespace LibHac.FsSystem
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
BaseStorageShared?.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
protected override Result DoOpenDirectory(out IDirectory directory, U8Span path, OpenDirectoryMode mode)
|
||||
{
|
||||
directory = default;
|
||||
|
@ -7,12 +7,20 @@ namespace LibHac.FsSystem
|
||||
public class ReadOnlyFileSystem : IFileSystem
|
||||
{
|
||||
private IFileSystem BaseFs { get; }
|
||||
private ReferenceCountedDisposable<IFileSystem> BaseFsShared { get; }
|
||||
|
||||
// Todo: Remove non-shared constructor
|
||||
public ReadOnlyFileSystem(IFileSystem baseFileSystem)
|
||||
{
|
||||
BaseFs = baseFileSystem;
|
||||
}
|
||||
|
||||
public ReadOnlyFileSystem(ReferenceCountedDisposable<IFileSystem> baseFileSystem)
|
||||
{
|
||||
BaseFsShared = baseFileSystem;
|
||||
BaseFs = BaseFsShared.Target;
|
||||
}
|
||||
|
||||
protected override Result DoOpenDirectory(out IDirectory directory, U8Span path, OpenDirectoryMode mode)
|
||||
{
|
||||
return BaseFs.OpenDirectory(out directory, path, mode);
|
||||
@ -36,11 +44,7 @@ namespace LibHac.FsSystem
|
||||
|
||||
protected override Result DoGetFreeSpaceSize(out long freeSpace, U8Span path)
|
||||
{
|
||||
freeSpace = 0;
|
||||
return Result.Success;
|
||||
|
||||
// FS does:
|
||||
// return ResultFs.UnsupportedOperationReadOnlyFileSystemGetSpace.Log();
|
||||
return BaseFs.GetFreeSpaceSize(out freeSpace, path);
|
||||
}
|
||||
|
||||
protected override Result DoGetTotalSpaceSize(out long totalSpace, U8Span path)
|
||||
@ -79,5 +83,15 @@ namespace LibHac.FsSystem
|
||||
protected override Result DoRenameDirectory(U8Span oldPath, U8Span newPath) => ResultFs.UnsupportedOperationModifyReadOnlyFileSystem.Log();
|
||||
|
||||
protected override Result DoRenameFile(U8Span oldPath, U8Span newPath) => ResultFs.UnsupportedOperationModifyReadOnlyFileSystem.Log();
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
BaseFsShared?.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
30
src/LibHac/FsSystem/SemaphoreAdaptor.cs
Normal file
30
src/LibHac/FsSystem/SemaphoreAdaptor.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace LibHac.FsSystem
|
||||
{
|
||||
public class SemaphoreAdaptor : IDisposable
|
||||
{
|
||||
private SemaphoreSlim _semaphore;
|
||||
|
||||
public SemaphoreAdaptor(int initialCount, int maxCount)
|
||||
{
|
||||
_semaphore = new SemaphoreSlim(initialCount, maxCount);
|
||||
}
|
||||
|
||||
public bool TryLock()
|
||||
{
|
||||
return _semaphore.Wait(TimeSpan.Zero);
|
||||
}
|
||||
|
||||
public void Unlock()
|
||||
{
|
||||
_semaphore.Release();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_semaphore?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ namespace LibHac.FsSystem
|
||||
public class SubdirectoryFileSystem : IFileSystem
|
||||
{
|
||||
private IFileSystem BaseFileSystem { get; }
|
||||
private ReferenceCountedDisposable<IFileSystem> BaseFileSystemShared { get; }
|
||||
private U8String RootPath { get; set; }
|
||||
private bool PreserveUnc { get; }
|
||||
|
||||
@ -35,7 +36,24 @@ namespace LibHac.FsSystem
|
||||
PreserveUnc = preserveUnc;
|
||||
}
|
||||
|
||||
private Result Initialize(U8Span rootPath)
|
||||
public SubdirectoryFileSystem(ReferenceCountedDisposable<IFileSystem> baseFileSystem, bool preserveUnc = false)
|
||||
{
|
||||
BaseFileSystemShared = baseFileSystem.AddReference();
|
||||
BaseFileSystem = BaseFileSystemShared.Target;
|
||||
PreserveUnc = preserveUnc;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
BaseFileSystemShared?.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public Result Initialize(U8Span rootPath)
|
||||
{
|
||||
if (StringUtils.GetLength(rootPath, PathTools.MaxPathLength + 1) > PathTools.MaxPathLength)
|
||||
return ResultFs.TooLongPath.Log();
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using LibHac.Arp;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Lr;
|
||||
using LibHac.Os;
|
||||
using LibHac.Sm;
|
||||
|
||||
@ -17,6 +18,7 @@ namespace LibHac
|
||||
public FileSystemClient Fs { get; }
|
||||
public ServiceManagerClient Sm { get; }
|
||||
public OsClient Os { get; }
|
||||
public LrClient Lr { get; }
|
||||
public ArpClient Arp => ArpLazy.Value;
|
||||
|
||||
public ITimeSpanGenerator Time => Horizon.Time;
|
||||
@ -29,6 +31,7 @@ namespace LibHac
|
||||
Fs = new FileSystemClient(this);
|
||||
Sm = new ServiceManagerClient(horizon.ServiceManager);
|
||||
Os = new OsClient(this);
|
||||
Lr = new LrClient(this);
|
||||
|
||||
ArpLazy = new Lazy<ArpClient>(InitArpClient, true);
|
||||
}
|
||||
|
35
src/LibHac/Lr/AddOnContentLocationResolver.cs
Normal file
35
src/LibHac/Lr/AddOnContentLocationResolver.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using LibHac.Ncm;
|
||||
|
||||
namespace LibHac.Lr
|
||||
{
|
||||
public class AddOnContentLocationResolver : IDisposable
|
||||
{
|
||||
private ReferenceCountedDisposable<IAddOnContentLocationResolver> _interface;
|
||||
|
||||
public AddOnContentLocationResolver(ReferenceCountedDisposable<IAddOnContentLocationResolver> baseInterface)
|
||||
{
|
||||
_interface = baseInterface.AddReference();
|
||||
}
|
||||
|
||||
public Result ResolveAddOnContentPath(out Path path, DataId id) =>
|
||||
_interface.Target.ResolveAddOnContentPath(out path, id);
|
||||
|
||||
public Result RegisterAddOnContentStorage(DataId id, Ncm.ApplicationId applicationId, StorageId storageId) =>
|
||||
_interface.Target.RegisterAddOnContentStorage(id, applicationId, storageId);
|
||||
|
||||
public Result UnregisterAllAddOnContentPath() =>
|
||||
_interface.Target.UnregisterAllAddOnContentPath();
|
||||
|
||||
public Result RefreshApplicationAddOnContent(ReadOnlySpan<Ncm.ApplicationId> ids) =>
|
||||
_interface.Target.RefreshApplicationAddOnContent(ids);
|
||||
|
||||
public Result UnregisterApplicationAddOnContent(Ncm.ApplicationId id) =>
|
||||
_interface.Target.UnregisterApplicationAddOnContent(id);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_interface?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
14
src/LibHac/Lr/IAddOnContentLocationResolver.cs
Normal file
14
src/LibHac/Lr/IAddOnContentLocationResolver.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using LibHac.Ncm;
|
||||
|
||||
namespace LibHac.Lr
|
||||
{
|
||||
public interface IAddOnContentLocationResolver : IDisposable
|
||||
{
|
||||
Result ResolveAddOnContentPath(out Path path, DataId id);
|
||||
Result RegisterAddOnContentStorage(DataId id, Ncm.ApplicationId applicationId, StorageId storageId);
|
||||
Result UnregisterAllAddOnContentPath();
|
||||
Result RefreshApplicationAddOnContent(ReadOnlySpan<Ncm.ApplicationId> ids);
|
||||
Result UnregisterApplicationAddOnContent(Ncm.ApplicationId id);
|
||||
}
|
||||
}
|
29
src/LibHac/Lr/ILocationResolver.cs
Normal file
29
src/LibHac/Lr/ILocationResolver.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using LibHac.Ncm;
|
||||
|
||||
namespace LibHac.Lr
|
||||
{
|
||||
public interface ILocationResolver : IDisposable
|
||||
{
|
||||
Result ResolveProgramPath(out Path path, ProgramId id);
|
||||
Result RedirectProgramPath(in Path path, ProgramId id);
|
||||
Result ResolveApplicationControlPath(out Path path, ProgramId id);
|
||||
Result ResolveApplicationHtmlDocumentPath(out Path path, ProgramId id);
|
||||
Result ResolveDataPath(out Path path, DataId id);
|
||||
Result RedirectApplicationControlPath(in Path path, ProgramId id, ProgramId ownerId);
|
||||
Result RedirectApplicationHtmlDocumentPath(in Path path, ProgramId id, ProgramId ownerId);
|
||||
Result ResolveApplicationLegalInformationPath(out Path path, ProgramId id);
|
||||
Result RedirectApplicationLegalInformationPath(in Path path, ProgramId id, ProgramId ownerId);
|
||||
Result Refresh();
|
||||
Result RedirectApplicationProgramPath(in Path path, ProgramId id, ProgramId ownerId);
|
||||
Result ClearApplicationRedirection(ReadOnlySpan<ProgramId> excludingIds);
|
||||
Result EraseProgramRedirection(ProgramId id);
|
||||
Result EraseApplicationControlRedirection(ProgramId id);
|
||||
Result EraseApplicationHtmlDocumentRedirection(ProgramId id);
|
||||
Result EraseApplicationLegalInformationRedirection(ProgramId id);
|
||||
Result ResolveProgramPathForDebug(out Path path, ProgramId id);
|
||||
Result RedirectProgramPathForDebug(in Path path, ProgramId id);
|
||||
Result RedirectApplicationProgramPathForDebug(in Path path, ProgramId id);
|
||||
Result EraseProgramRedirectionForDebug(ProgramId id);
|
||||
}
|
||||
}
|
13
src/LibHac/Lr/ILocationResolverManager.cs
Normal file
13
src/LibHac/Lr/ILocationResolverManager.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using LibHac.Ncm;
|
||||
|
||||
namespace LibHac.Lr
|
||||
{
|
||||
public interface ILocationResolverManager : IDisposable
|
||||
{
|
||||
Result OpenLocationResolver(out ReferenceCountedDisposable<ILocationResolver> resolver, StorageId storageId);
|
||||
Result OpenRegisteredLocationResolver(out ReferenceCountedDisposable<IRegisteredLocationResolver> resolver);
|
||||
Result RefreshLocationResolver(StorageId storageId);
|
||||
Result OpenAddOnContentLocationResolver(out ReferenceCountedDisposable<IAddOnContentLocationResolver> resolver);
|
||||
}
|
||||
}
|
19
src/LibHac/Lr/IRegisteredLocationResolver.cs
Normal file
19
src/LibHac/Lr/IRegisteredLocationResolver.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using LibHac.Ncm;
|
||||
|
||||
namespace LibHac.Lr
|
||||
{
|
||||
public interface IRegisteredLocationResolver : IDisposable
|
||||
{
|
||||
Result ResolveProgramPath(out Path path, ProgramId id);
|
||||
Result RegisterProgramPath(in Path path, ProgramId id, ProgramId ownerId);
|
||||
Result UnregisterProgramPath(ProgramId id);
|
||||
Result RedirectProgramPath(in Path path, ProgramId id, ProgramId ownerId);
|
||||
Result ResolveHtmlDocumentPath(out Path path, ProgramId id);
|
||||
Result RegisterHtmlDocumentPath(in Path path, ProgramId id, ProgramId ownerId);
|
||||
Result UnregisterHtmlDocumentPath(ProgramId id);
|
||||
Result RedirectHtmlDocumentPath(in Path path, ProgramId id);
|
||||
Result Refresh();
|
||||
Result RefreshExcluding(ReadOnlySpan<ProgramId> ids);
|
||||
}
|
||||
}
|
80
src/LibHac/Lr/LocationResolver.cs
Normal file
80
src/LibHac/Lr/LocationResolver.cs
Normal file
@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using LibHac.Ncm;
|
||||
|
||||
namespace LibHac.Lr
|
||||
{
|
||||
public class LocationResolver : IDisposable
|
||||
{
|
||||
private ReferenceCountedDisposable<ILocationResolver> _interface;
|
||||
|
||||
public LocationResolver(ReferenceCountedDisposable<ILocationResolver> baseInterface)
|
||||
{
|
||||
_interface = baseInterface.AddReference();
|
||||
}
|
||||
|
||||
public Result ResolveProgramPath(out Path path, ProgramId id) =>
|
||||
_interface.Target.ResolveProgramPath(out path, id);
|
||||
|
||||
public Result RedirectProgramPath(in Path path, ProgramId id) =>
|
||||
_interface.Target.RedirectProgramPath(in path, id);
|
||||
|
||||
public Result ResolveApplicationControlPath(out Path path, ProgramId id) =>
|
||||
_interface.Target.ResolveApplicationControlPath(out path, id);
|
||||
|
||||
public Result ResolveApplicationHtmlDocumentPath(out Path path, ProgramId id) =>
|
||||
_interface.Target.ResolveApplicationHtmlDocumentPath(out path, id);
|
||||
|
||||
public Result ResolveDataPath(out Path path, DataId id) =>
|
||||
_interface.Target.ResolveDataPath(out path, id);
|
||||
|
||||
public Result RedirectApplicationControlPath(in Path path, ProgramId id, ProgramId ownerId) =>
|
||||
_interface.Target.RedirectApplicationControlPath(in path, id, ownerId);
|
||||
|
||||
public Result RedirectApplicationHtmlDocumentPath(in Path path, ProgramId id, ProgramId ownerId) =>
|
||||
_interface.Target.RedirectApplicationHtmlDocumentPath(in path, id, ownerId);
|
||||
|
||||
public Result ResolveApplicationLegalInformationPath(out Path path, ProgramId id) =>
|
||||
_interface.Target.ResolveApplicationLegalInformationPath(out path, id);
|
||||
|
||||
public Result RedirectApplicationLegalInformationPath(in Path path, ProgramId id, ProgramId ownerId) =>
|
||||
_interface.Target.RedirectApplicationLegalInformationPath(in path, id, ownerId);
|
||||
|
||||
public Result Refresh() =>
|
||||
_interface.Target.Refresh();
|
||||
|
||||
public Result RedirectApplicationProgramPath(in Path path, ProgramId id, ProgramId ownerId) =>
|
||||
_interface.Target.RedirectApplicationProgramPath(in path, id, ownerId);
|
||||
|
||||
public Result ClearApplicationRedirection(ReadOnlySpan<ProgramId> excludingIds) =>
|
||||
_interface.Target.ClearApplicationRedirection(excludingIds);
|
||||
|
||||
public Result EraseProgramRedirection(ProgramId id) =>
|
||||
_interface.Target.EraseProgramRedirection(id);
|
||||
|
||||
public Result EraseApplicationControlRedirection(ProgramId id) =>
|
||||
_interface.Target.EraseApplicationControlRedirection(id);
|
||||
|
||||
public Result EraseApplicationHtmlDocumentRedirection(ProgramId id) =>
|
||||
_interface.Target.EraseApplicationHtmlDocumentRedirection(id);
|
||||
|
||||
public Result EraseApplicationLegalInformationRedirection(ProgramId id) =>
|
||||
_interface.Target.EraseApplicationLegalInformationRedirection(id);
|
||||
|
||||
public Result ResolveProgramPathForDebug(out Path path, ProgramId id) =>
|
||||
_interface.Target.ResolveProgramPathForDebug(out path, id);
|
||||
|
||||
public Result RedirectProgramPathForDebug(in Path path, ProgramId id) =>
|
||||
_interface.Target.RedirectProgramPathForDebug(in path, id);
|
||||
|
||||
public Result RedirectApplicationProgramPathForDebug(in Path path, ProgramId id) =>
|
||||
_interface.Target.RedirectApplicationProgramPathForDebug(in path, id);
|
||||
|
||||
public Result EraseProgramRedirectionForDebug(ProgramId id) =>
|
||||
_interface.Target.EraseProgramRedirectionForDebug(id);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_interface?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
102
src/LibHac/Lr/LrClient.cs
Normal file
102
src/LibHac/Lr/LrClient.cs
Normal file
@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using LibHac.Ncm;
|
||||
|
||||
namespace LibHac.Lr
|
||||
{
|
||||
public class LrClient : IDisposable
|
||||
{
|
||||
private HorizonClient Hos { get; }
|
||||
|
||||
private ILocationResolverManager LrManager { get; set; }
|
||||
private readonly object _lrInitLocker = new object();
|
||||
|
||||
public LrClient(HorizonClient horizonClient)
|
||||
{
|
||||
Hos = horizonClient;
|
||||
}
|
||||
|
||||
public Result OpenLocationResolver(out LocationResolver resolver, StorageId storageId)
|
||||
{
|
||||
resolver = default;
|
||||
EnsureInitialized();
|
||||
|
||||
Result rc = LrManager.OpenLocationResolver(out ReferenceCountedDisposable<ILocationResolver> baseResolver,
|
||||
storageId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
using (baseResolver)
|
||||
{
|
||||
resolver = new LocationResolver(baseResolver);
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
public Result OpenRegisteredLocationResolver(out RegisteredLocationResolver resolver)
|
||||
{
|
||||
resolver = default;
|
||||
EnsureInitialized();
|
||||
|
||||
Result rc = LrManager.OpenRegisteredLocationResolver(
|
||||
out ReferenceCountedDisposable<IRegisteredLocationResolver> baseResolver);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
using (baseResolver)
|
||||
{
|
||||
resolver = new RegisteredLocationResolver(baseResolver);
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
public Result OpenAddOnContentLocationResolver(out AddOnContentLocationResolver resolver)
|
||||
{
|
||||
resolver = default;
|
||||
EnsureInitialized();
|
||||
|
||||
Result rc = LrManager.OpenAddOnContentLocationResolver(
|
||||
out ReferenceCountedDisposable<IAddOnContentLocationResolver> baseResolver);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
using (baseResolver)
|
||||
{
|
||||
resolver = new AddOnContentLocationResolver(baseResolver);
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
public Result RefreshLocationResolver(StorageId storageId)
|
||||
{
|
||||
EnsureInitialized();
|
||||
|
||||
Result rc = LrManager.RefreshLocationResolver(storageId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private void EnsureInitialized()
|
||||
{
|
||||
if (LrManager != null)
|
||||
return;
|
||||
|
||||
lock (_lrInitLocker)
|
||||
{
|
||||
if (LrManager != null)
|
||||
return;
|
||||
|
||||
Result rc = Hos.Sm.GetService(out ILocationResolverManager manager, "lr");
|
||||
|
||||
if (rc.IsFailure())
|
||||
{
|
||||
throw new HorizonResultException(rc, "Failed to initialize lr client.");
|
||||
}
|
||||
|
||||
LrManager = manager;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
LrManager?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
36
src/LibHac/Lr/Path.cs
Normal file
36
src/LibHac/Lr/Path.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Util;
|
||||
|
||||
namespace LibHac.Lr
|
||||
{
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
[StructLayout(LayoutKind.Sequential, Size = PathTool.EntryNameLengthMax)]
|
||||
public struct Path
|
||||
{
|
||||
#if DEBUG
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding000;
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding100;
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly Padding100 Padding200;
|
||||
#endif
|
||||
|
||||
public readonly ReadOnlySpan<byte> Str => SpanHelpers.AsReadOnlyByteSpan(in this);
|
||||
public Span<byte> StrMutable => SpanHelpers.AsByteSpan(ref this);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void InitEmpty(out Path path)
|
||||
{
|
||||
Unsafe.SkipInit(out path);
|
||||
SpanHelpers.AsByteSpan(ref path)[0] = 0;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator U8Span(in Path value) => new U8Span(SpanHelpers.AsReadOnlyByteSpan(in value));
|
||||
|
||||
public override readonly string ToString() => StringUtils.Utf8ZToString(Str);
|
||||
}
|
||||
}
|
50
src/LibHac/Lr/RegisteredLocationResolver.cs
Normal file
50
src/LibHac/Lr/RegisteredLocationResolver.cs
Normal file
@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using LibHac.Ncm;
|
||||
|
||||
namespace LibHac.Lr
|
||||
{
|
||||
public class RegisteredLocationResolver : IDisposable
|
||||
{
|
||||
private ReferenceCountedDisposable<IRegisteredLocationResolver> _interface;
|
||||
|
||||
public RegisteredLocationResolver(ReferenceCountedDisposable<IRegisteredLocationResolver> baseInterface)
|
||||
{
|
||||
_interface = baseInterface.AddReference();
|
||||
}
|
||||
|
||||
public Result ResolveProgramPath(out Path path, ProgramId id) =>
|
||||
_interface.Target.ResolveProgramPath(out path, id);
|
||||
|
||||
public Result RegisterProgramPath(in Path path, ProgramId id, ProgramId ownerId) =>
|
||||
_interface.Target.RegisterProgramPath(in path, id, ownerId);
|
||||
|
||||
public Result UnregisterProgramPath(ProgramId id) =>
|
||||
_interface.Target.UnregisterProgramPath(id);
|
||||
|
||||
public Result RedirectProgramPath(in Path path, ProgramId id, ProgramId ownerId) =>
|
||||
_interface.Target.RedirectProgramPath(in path, id, ownerId);
|
||||
|
||||
public Result ResolveHtmlDocumentPath(out Path path, ProgramId id) =>
|
||||
_interface.Target.ResolveHtmlDocumentPath(out path, id);
|
||||
|
||||
public Result RegisterHtmlDocumentPath(in Path path, ProgramId id, ProgramId ownerId) =>
|
||||
_interface.Target.RegisterHtmlDocumentPath(in path, id, ownerId);
|
||||
|
||||
public Result UnregisterHtmlDocumentPath(ProgramId id) =>
|
||||
_interface.Target.UnregisterHtmlDocumentPath(id);
|
||||
|
||||
public Result RedirectHtmlDocumentPath(in Path path, ProgramId id) =>
|
||||
_interface.Target.RedirectHtmlDocumentPath(in path, id);
|
||||
|
||||
public Result Refresh() =>
|
||||
_interface.Target.Refresh();
|
||||
|
||||
public Result RefreshExcluding(ReadOnlySpan<ProgramId> ids) =>
|
||||
_interface.Target.RefreshExcluding(ids);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_interface?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
39
src/LibHac/Lr/ResultLr.cs
Normal file
39
src/LibHac/Lr/ResultLr.cs
Normal file
@ -0,0 +1,39 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// This file was automatically generated.
|
||||
// Changes to this file will be lost when the file is regenerated.
|
||||
//
|
||||
// To change this file, modify /build/CodeGen/results.csv at the root of this
|
||||
// repo and run the build script.
|
||||
//
|
||||
// The script can be run with the "codegen" option to run only the
|
||||
// code generation portion of the build.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
namespace LibHac.Lr
|
||||
{
|
||||
public static class ResultLr
|
||||
{
|
||||
public const int ModuleLr = 8;
|
||||
|
||||
/// <summary>Error code: 2008-0002; Inner value: 0x408</summary>
|
||||
public static Result.Base ProgramNotFound => new Result.Base(ModuleLr, 2);
|
||||
/// <summary>Error code: 2008-0003; Inner value: 0x608</summary>
|
||||
public static Result.Base DataNotFound => new Result.Base(ModuleLr, 3);
|
||||
/// <summary>Error code: 2008-0004; Inner value: 0x808</summary>
|
||||
public static Result.Base UnknownStorageId => new Result.Base(ModuleLr, 4);
|
||||
/// <summary>Error code: 2008-0005; Inner value: 0xa08</summary>
|
||||
public static Result.Base LocationResolverNotFound => new Result.Base(ModuleLr, 5);
|
||||
/// <summary>Error code: 2008-0006; Inner value: 0xc08</summary>
|
||||
public static Result.Base HtmlDocumentNotFound => new Result.Base(ModuleLr, 6);
|
||||
/// <summary>Error code: 2008-0007; Inner value: 0xe08</summary>
|
||||
public static Result.Base AddOnContentNotFound => new Result.Base(ModuleLr, 7);
|
||||
/// <summary>Error code: 2008-0008; Inner value: 0x1008</summary>
|
||||
public static Result.Base ControlNotFound => new Result.Base(ModuleLr, 8);
|
||||
/// <summary>Error code: 2008-0009; Inner value: 0x1208</summary>
|
||||
public static Result.Base LegalInformationNotFound => new Result.Base(ModuleLr, 9);
|
||||
/// <summary>Error code: 2008-0010; Inner value: 0x1408</summary>
|
||||
public static Result.Base DebugProgramNotFound => new Result.Base(ModuleLr, 10);
|
||||
/// <summary>Error code: 2008-0090; Inner value: 0xb408</summary>
|
||||
public static Result.Base TooManyRegisteredPaths => new Result.Base(ModuleLr, 90);
|
||||
}
|
||||
}
|
@ -43,12 +43,16 @@ namespace LibHac
|
||||
public static SwitchFs OpenSdCard(KeySet keySet, IAttributeFileSystem fileSystem)
|
||||
{
|
||||
var concatFs = new ConcatenationFileSystem(fileSystem);
|
||||
SubdirectoryFileSystem.CreateNew(out SubdirectoryFileSystem contentDirFs, concatFs, "/Nintendo/Contents".ToU8String()).ThrowIfFailure();
|
||||
|
||||
var contentDirFs = new SubdirectoryFileSystem(concatFs);
|
||||
contentDirFs.Initialize("/Nintendo/Contents".ToU8String()).ThrowIfFailure();
|
||||
|
||||
AesXtsFileSystem encSaveFs = null;
|
||||
if (fileSystem.DirectoryExists("/Nintendo/save"))
|
||||
{
|
||||
SubdirectoryFileSystem.CreateNew(out SubdirectoryFileSystem saveDirFs, concatFs, "/Nintendo/save".ToU8String()).ThrowIfFailure();
|
||||
var saveDirFs = new SubdirectoryFileSystem(concatFs);
|
||||
saveDirFs.Initialize("/Nintendo/save".ToU8String()).ThrowIfFailure();
|
||||
|
||||
encSaveFs = new AesXtsFileSystem(saveDirFs, keySet.SdCardEncryptionKeys[0].DataRo.ToArray(), 0x4000);
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,9 @@ namespace LibHac.Tests.Fs
|
||||
baseFs.CreateDirectory("/sub".ToU8Span());
|
||||
baseFs.CreateDirectory("/sub/path".ToU8Span());
|
||||
|
||||
SubdirectoryFileSystem.CreateNew(out SubdirectoryFileSystem subFs, baseFs, "/sub/path".ToU8String()).ThrowIfFailure();
|
||||
var subFs = new SubdirectoryFileSystem(baseFs);
|
||||
subFs.Initialize("/sub/path".ToU8String()).ThrowIfFailure();
|
||||
|
||||
return (baseFs, subFs);
|
||||
}
|
||||
|
||||
@ -53,7 +55,8 @@ namespace LibHac.Tests.Fs
|
||||
{
|
||||
var baseFs = new InMemoryFileSystem();
|
||||
|
||||
SubdirectoryFileSystem.CreateNew(out SubdirectoryFileSystem subFs, baseFs, "/".ToU8String()).ThrowIfFailure();
|
||||
var subFs = new SubdirectoryFileSystem(baseFs);
|
||||
subFs.Initialize("/".ToU8String()).ThrowIfFailure();
|
||||
return subFs;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user