mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2025-02-09 13:14:46 +01:00
Merge pull request #167 from Thealexbarney/fsp-split
- Split `FileSystemProxyImpl` into ~8 different classes. - Add `sf` IPC interfaces to `fssrv` and use them where appropriate - Make `IFileSystemProxy` method signatures more accurate - Implement LR client - Flesh out and fully implement many `fssrv` methods - In general, make new and previously-existing code in `fssrv` more accurate to FS 10.0.0
This commit is contained in:
commit
46be7259d0
@ -1 +1 @@
|
||||
3.1.401
|
||||
3.1.402
|
@ -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,29 @@ 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,3407,,AllocationFailureInFileSystemInterfaceAdapter, In OpenFile or OpenDirectory
|
||||
2,3420,,AllocationFailureInNew,
|
||||
2,3421,,AllocationFailureInCreateShared,
|
||||
2,3422,,AllocationFailureInMakeUnique,
|
||||
2,3423,,AllocationFailureInAllocateShared,
|
||||
2,3424,,AllocationFailurePooledBufferNotEnoughSize,
|
||||
|
||||
2,3500,3999,MmcAccessFailed,
|
||||
|
||||
@ -148,8 +165,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,
|
||||
@ -205,6 +222,7 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
|
||||
2,4812,,IncompleteBlockInZeroBitmapHashStorageFile,
|
||||
|
||||
2,5000,5999,Unexpected,
|
||||
2,5121,,UnexpectedFatFileSystemSectorCount,
|
||||
2,5307,,UnexpectedErrorInHostFileFlush,
|
||||
2,5308,,UnexpectedErrorInHostFileGetSize,
|
||||
2,5309,,UnknownHostFileSystemError,
|
||||
@ -221,7 +239,7 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
|
||||
|
||||
2,6030,6059,InvalidPathForOperation,
|
||||
2,6031,,DirectoryNotDeletable,
|
||||
2,6032,,DestinationIsSubPathOfSource,
|
||||
2,6032,,DirectoryNotRenamable,
|
||||
2,6033,,PathNotFoundInSaveDataFileTable,
|
||||
2,6034,,DifferentDestFileSystem,
|
||||
|
||||
@ -249,6 +267,7 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
|
||||
2,6203,,InvalidOpenModeForWrite,
|
||||
|
||||
2,6300,6399,UnsupportedOperation,
|
||||
2,6301,,UnsupportedCommitTarget,
|
||||
2,6302,,UnsupportedOperationInSubStorageSetSize,Attempted to resize a non-resizable SubStorage.
|
||||
2,6303,,UnsupportedOperationInResizableSubStorageSetSize,Attempted to resize a SubStorage that wasn't located at the end of the base storage.
|
||||
2,6304,,UnsupportedOperationInMemoryStorageSetSize,
|
||||
@ -260,6 +279,7 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
|
||||
2,6350,,UnsupportedOperationInRoGameCardStorageWrite,
|
||||
2,6351,,UnsupportedOperationInRoGameCardStorageSetSize,
|
||||
2,6359,,UnsupportedOperationInConcatFsQueryEntry,
|
||||
2,6362,,UnsupportedOperationInFileServiceObjectAdapterA,Called OperateRange with an invalid operation ID.
|
||||
2,6364,,UnsupportedOperationModifyRomFsFileSystem,
|
||||
2,6365,,UnsupportedOperationInRomFsFileSystem,Called RomFsFileSystem::CommitProvisionally.
|
||||
2,6366,,UnsupportedOperationRomFsFileSystemGetSpace,
|
||||
@ -278,10 +298,15 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
|
||||
|
||||
2,6452,,ExternalKeyAlreadyRegistered,
|
||||
2,6454,,WriteStateUnflushed,
|
||||
2,6456,,DirectoryNotClosed,
|
||||
2,6457,,WriteModeFileNotClosed,
|
||||
2,6458,,AllocatorAlreadyRegistered,
|
||||
2,6459,,DefaultAllocatorUsed,
|
||||
2,6461,,AllocatorAlignmentViolation,
|
||||
2,6463,,MultiCommitFileSystemAlreadyAdded,The provided file system has already been added to the multi-commit manager.
|
||||
2,6465,,UserNotExist,
|
||||
2,6466,,DefaultGlobalFileDataCacheEnabled,
|
||||
2,6467,,SaveDataRootPathUnavailable,
|
||||
|
||||
2,6600,6699,EntryNotFound,
|
||||
2,6605,,TargetProgramNotFound,Specified program is not found in the program registry.
|
||||
@ -302,6 +327,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 220.
|
@ -502,7 +502,7 @@ namespace LibHac.Boot
|
||||
Package1Section.Bootloader => 0,
|
||||
Package1Section.SecureMonitor => 1,
|
||||
Package1Section.WarmBoot => 2,
|
||||
_ => -1,
|
||||
_ => -1
|
||||
};
|
||||
}
|
||||
|
||||
@ -513,7 +513,7 @@ namespace LibHac.Boot
|
||||
Package1Section.Bootloader => 1,
|
||||
Package1Section.SecureMonitor => 2,
|
||||
Package1Section.WarmBoot => 0,
|
||||
_ => -1,
|
||||
_ => -1
|
||||
};
|
||||
}
|
||||
|
||||
@ -522,7 +522,7 @@ namespace LibHac.Boot
|
||||
Package1Section.Bootloader => 1,
|
||||
Package1Section.SecureMonitor => 0,
|
||||
Package1Section.WarmBoot => 2,
|
||||
_ => -1,
|
||||
_ => -1
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ namespace LibHac.Common.Keys
|
||||
CommonSeedDiff = Common | Seed | DifferentDev,
|
||||
CommonDrvd = Common | Derived,
|
||||
DeviceRoot = Device | Root,
|
||||
DeviceDrvd = Device | Derived,
|
||||
DeviceDrvd = Device | Derived
|
||||
}
|
||||
|
||||
public readonly string Name;
|
||||
|
22
src/LibHac/Common/SharedObjectHelpers.cs
Normal file
22
src/LibHac/Common/SharedObjectHelpers.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace LibHac.Common
|
||||
{
|
||||
public static class Shared
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T Move<T>(ref T value)
|
||||
{
|
||||
T tmp = value;
|
||||
value = default;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Move<T>(out T dest, ref T value)
|
||||
{
|
||||
dest = value;
|
||||
value = default;
|
||||
}
|
||||
}
|
||||
}
|
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));
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs.Fsa;
|
||||
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||
|
||||
namespace LibHac.Fs.Accessors
|
||||
{
|
||||
@ -20,10 +21,13 @@ namespace LibHac.Fs.Accessors
|
||||
private readonly object _locker = new object();
|
||||
|
||||
internal bool IsAccessLogEnabled { get; set; }
|
||||
public IMultiCommitTarget MultiCommitTarget { get; }
|
||||
|
||||
public FileSystemAccessor(string name, IFileSystem baseFileSystem, FileSystemClient fsClient, ICommonMountNameGenerator nameGenerator)
|
||||
public FileSystemAccessor(U8Span name, IMultiCommitTarget multiCommitTarget, IFileSystem baseFileSystem,
|
||||
FileSystemClient fsClient, ICommonMountNameGenerator nameGenerator)
|
||||
{
|
||||
Name = name;
|
||||
Name = name.ToString();
|
||||
MultiCommitTarget = multiCommitTarget;
|
||||
FileSystem = baseFileSystem;
|
||||
FsClient = fsClient;
|
||||
MountNameGenerator = nameGenerator;
|
||||
@ -147,6 +151,11 @@ namespace LibHac.Fs.Accessors
|
||||
return MountNameGenerator.GenerateCommonMountName(nameBuffer);
|
||||
}
|
||||
|
||||
public ReferenceCountedDisposable<IFileSystemSf> GetMultiCommitTarget()
|
||||
{
|
||||
return MultiCommitTarget?.GetMultiCommitTarget();
|
||||
}
|
||||
|
||||
internal void NotifyCloseFile(FileAccessor file)
|
||||
{
|
||||
lock (_locker)
|
||||
|
@ -232,7 +232,7 @@ namespace LibHac.Fs
|
||||
}
|
||||
|
||||
private static Result EnsureApplicationCacheStorageImpl(this FileSystemClient fs, out long requiredSize,
|
||||
out CacheStorageTargetMedia target, Ncm.ApplicationId applicationId, ulong saveDataOwnerId, short index,
|
||||
out CacheStorageTargetMedia target, Ncm.ApplicationId applicationId, ulong saveDataOwnerId, ushort index,
|
||||
long dataSize, long journalSize, bool allowExisting)
|
||||
{
|
||||
requiredSize = default;
|
||||
@ -299,7 +299,7 @@ namespace LibHac.Fs
|
||||
}
|
||||
|
||||
public static Result EnsureApplicationCacheStorage(this FileSystemClient fs, out long requiredSize,
|
||||
out CacheStorageTargetMedia target, Ncm.ApplicationId applicationId, ulong saveDataOwnerId, short index,
|
||||
out CacheStorageTargetMedia target, Ncm.ApplicationId applicationId, ulong saveDataOwnerId, ushort index,
|
||||
long dataSize, long journalSize, bool allowExisting)
|
||||
{
|
||||
return EnsureApplicationCacheStorageImpl(fs, out requiredSize, out target, applicationId, saveDataOwnerId,
|
||||
@ -334,7 +334,7 @@ namespace LibHac.Fs
|
||||
}
|
||||
|
||||
public static Result TryCreateCacheStorage(this FileSystemClient fs, out long requiredSize,
|
||||
SaveDataSpaceId spaceId, Ncm.ApplicationId applicationId, ulong saveDataOwnerId, short index,
|
||||
SaveDataSpaceId spaceId, Ncm.ApplicationId applicationId, ulong saveDataOwnerId, ushort index,
|
||||
long dataSize, long journalSize, bool allowExisting)
|
||||
{
|
||||
requiredSize = default;
|
||||
|
@ -1,8 +1,9 @@
|
||||
using LibHac.Common;
|
||||
using System;
|
||||
using LibHac.Common;
|
||||
|
||||
namespace LibHac.Fs
|
||||
{
|
||||
internal static class CommonMountNames
|
||||
internal static class CommonPaths
|
||||
{
|
||||
public const char ReservedMountNamePrefixCharacter = '@';
|
||||
|
||||
@ -21,5 +22,11 @@ namespace LibHac.Fs
|
||||
public const char GameCardFileSystemMountNameUpdateSuffix = 'U';
|
||||
public const char GameCardFileSystemMountNameNormalSuffix = 'N';
|
||||
public const char GameCardFileSystemMountNameSecureSuffix = 'S';
|
||||
|
||||
public static ReadOnlySpan<byte> SdCardNintendoRootDirectoryName => // Nintendo
|
||||
new[]
|
||||
{
|
||||
(byte) 'N', (byte) 'i', (byte) 'n', (byte) 't', (byte) 'e', (byte) 'n', (byte) 'd', (byte) 'o'
|
||||
};
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -3,6 +3,7 @@ using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs.Accessors;
|
||||
using LibHac.FsSrv;
|
||||
using LibHac.Sf;
|
||||
|
||||
namespace LibHac.Fs
|
||||
{
|
||||
@ -187,7 +188,7 @@ namespace LibHac.Fs
|
||||
string logString = AccessLogHelpers.BuildDefaultLogLine(result, startTime, endTime, handleId, message, caller);
|
||||
|
||||
IFileSystemProxy fsProxy = GetFileSystemProxyServiceObject();
|
||||
fsProxy.OutputAccessLogToSdCard(logString.ToU8Span());
|
||||
fsProxy.OutputAccessLogToSdCard(new InBuffer(logString.ToU8Span())).IgnoreResult();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ using LibHac.FsSrv;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.Util;
|
||||
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
|
||||
|
||||
namespace LibHac.Fs
|
||||
{
|
||||
@ -134,7 +135,13 @@ namespace LibHac.Fs
|
||||
|
||||
public Result Register(U8Span mountName, IFileSystem fileSystem, ICommonMountNameGenerator nameGenerator)
|
||||
{
|
||||
var accessor = new FileSystemAccessor(mountName.ToString(), fileSystem, this, nameGenerator);
|
||||
return Register(mountName, null, fileSystem, nameGenerator);
|
||||
}
|
||||
|
||||
public Result Register(U8Span mountName, IMultiCommitTarget multiCommitTarget, IFileSystem fileSystem,
|
||||
ICommonMountNameGenerator nameGenerator)
|
||||
{
|
||||
var accessor = new FileSystemAccessor(mountName, multiCommitTarget, fileSystem, this, nameGenerator);
|
||||
|
||||
Result rc = MountTable.Mount(accessor);
|
||||
if (rc.IsFailure()) return rc;
|
||||
@ -173,8 +180,8 @@ namespace LibHac.Fs
|
||||
if (path.IsNull())
|
||||
return ResultFs.NullptrArgument.Log();
|
||||
|
||||
int hostMountNameLen = StringUtils.GetLength(CommonMountNames.HostRootFileSystemMountName);
|
||||
if (StringUtils.Compare(path, CommonMountNames.HostRootFileSystemMountName, hostMountNameLen) == 0)
|
||||
int hostMountNameLen = StringUtils.GetLength(CommonPaths.HostRootFileSystemMountName);
|
||||
if (StringUtils.Compare(path, CommonPaths.HostRootFileSystemMountName, hostMountNameLen) == 0)
|
||||
{
|
||||
return ResultFs.NotMounted.Log();
|
||||
}
|
||||
@ -195,7 +202,7 @@ namespace LibHac.Fs
|
||||
|
||||
if (PathUtility.IsWindowsDrive(path) || PathUtility.IsUnc(path))
|
||||
{
|
||||
StringUtils.Copy(mountName.Name, CommonMountNames.HostRootFileSystemMountName);
|
||||
StringUtils.Copy(mountName.Name, CommonPaths.HostRootFileSystemMountName);
|
||||
mountName.Name[PathTools.MountNameLengthMax] = StringTraits.NullTerminator;
|
||||
|
||||
subPath = path;
|
||||
|
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;
|
||||
}
|
||||
}
|
@ -1,9 +1,13 @@
|
||||
namespace LibHac.Fs
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace LibHac.Fs
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x20)]
|
||||
public struct FileTimeStampRaw
|
||||
{
|
||||
public long Created;
|
||||
public long Accessed;
|
||||
public long Modified;
|
||||
public bool IsLocalTime;
|
||||
}
|
||||
}
|
||||
|
@ -96,10 +96,11 @@ namespace LibHac.Fs
|
||||
public enum SaveDataState : byte
|
||||
{
|
||||
Normal = 0,
|
||||
Creating = 1,
|
||||
Processing = 1,
|
||||
State2 = 2,
|
||||
MarkedForDeletion = 3,
|
||||
Extending = 4
|
||||
Extending = 4,
|
||||
ImportSuspended = 5
|
||||
}
|
||||
|
||||
public enum ImageDirectoryId
|
||||
@ -165,7 +166,8 @@ namespace LibHac.Fs
|
||||
KeepAfterResettingSystemSaveData = 1 << 0,
|
||||
KeepAfterRefurbishment = 1 << 1,
|
||||
KeepAfterResettingSystemSaveDataWithoutUserSaveData = 1 << 2,
|
||||
NeedsSecureDelete = 1 << 3
|
||||
NeedsSecureDelete = 1 << 3,
|
||||
Restore = 1 << 4
|
||||
}
|
||||
|
||||
public enum SdmmcPort
|
||||
@ -188,4 +190,11 @@ namespace LibHac.Fs
|
||||
None = 0,
|
||||
PseudoCaseSensitive = 1
|
||||
}
|
||||
|
||||
public enum SimulatingDeviceDetectionMode
|
||||
{
|
||||
None = 0,
|
||||
Inserted = 1,
|
||||
NotInserted = 2
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.FsSystem;
|
||||
|
||||
@ -36,6 +37,31 @@ namespace LibHac.Fs.Fsa
|
||||
return DoCreateFile(path, size, option);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates or overwrites a file at the specified path.
|
||||
/// </summary>
|
||||
/// <param name="path">The full path of the file to create.</param>
|
||||
/// <param name="size">The initial size of the created file.
|
||||
/// Should usually be <see cref="CreateFileOptions.None"/></param>
|
||||
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
|
||||
/// <remarks>
|
||||
/// The following <see cref="Result"/> codes may be returned under certain conditions:
|
||||
///
|
||||
/// The parent directory of the specified path does not exist: <see cref="ResultFs.PathNotFound"/>
|
||||
/// Specified path already exists as either a file or directory: <see cref="ResultFs.PathAlreadyExists"/>
|
||||
/// Insufficient free space to create the file: <see cref="ResultFs.InsufficientFreeSpace"/>
|
||||
/// </remarks>
|
||||
public Result CreateFile(U8Span path, long size)
|
||||
{
|
||||
if (path.IsNull())
|
||||
return ResultFs.NullptrArgument.Log();
|
||||
|
||||
if (size < 0)
|
||||
return ResultFs.OutOfRange.Log();
|
||||
|
||||
return DoCreateFile(path, size, CreateFileOptions.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the specified file.
|
||||
/// </summary>
|
||||
@ -167,7 +193,7 @@ namespace LibHac.Fs.Fsa
|
||||
/// <paramref name="oldPath"/> does not exist or is a file: <see cref="ResultFs.PathNotFound"/>
|
||||
/// <paramref name="newPath"/>'s parent directory does not exist: <see cref="ResultFs.PathNotFound"/>
|
||||
/// <paramref name="newPath"/> already exists as either a file or directory: <see cref="ResultFs.PathAlreadyExists"/>
|
||||
/// Either <paramref name="oldPath"/> or <paramref name="newPath"/> is a subpath of the other: <see cref="ResultFs.DestinationIsSubPathOfSource"/>
|
||||
/// Either <paramref name="oldPath"/> or <paramref name="newPath"/> is a subpath of the other: <see cref="ResultFs.DirectoryNotRenamable"/>
|
||||
/// </remarks>
|
||||
public Result RenameDirectory(U8Span oldPath, U8Span newPath)
|
||||
{
|
||||
@ -391,7 +417,7 @@ namespace LibHac.Fs.Fsa
|
||||
|
||||
protected virtual Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, U8Span path)
|
||||
{
|
||||
timeStamp = default;
|
||||
Unsafe.SkipInit(out timeStamp);
|
||||
return ResultFs.NotImplemented.Log();
|
||||
}
|
||||
|
||||
|
9
src/LibHac/Fs/Fsa/IMultiCommitTarget.cs
Normal file
9
src/LibHac/Fs/Fsa/IMultiCommitTarget.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||
|
||||
namespace LibHac.Fs.Fsa
|
||||
{
|
||||
public interface IMultiCommitTarget
|
||||
{
|
||||
ReferenceCountedDisposable<IFileSystemSf> GetMultiCommitTarget();
|
||||
}
|
||||
}
|
43
src/LibHac/Fs/Impl/DirectoryServiceObjectAdapter.cs
Normal file
43
src/LibHac/Fs/Impl/DirectoryServiceObjectAdapter.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Sf;
|
||||
using IDirectory = LibHac.Fs.Fsa.IDirectory;
|
||||
using IDirectorySf = LibHac.FsSrv.Sf.IDirectory;
|
||||
|
||||
namespace LibHac.Fs.Impl
|
||||
{
|
||||
/// <summary>
|
||||
/// An adapter for using an <see cref="IDirectorySf"/> service object as an <see cref="IDirectory"/>. Used
|
||||
/// when receiving a Horizon IPC directory object so it can be used as an <see cref="IDirectory"/> locally.
|
||||
/// </summary>
|
||||
internal class DirectoryServiceObjectAdapter : IDirectory
|
||||
{
|
||||
private ReferenceCountedDisposable<IDirectorySf> BaseDirectory { get; }
|
||||
|
||||
public DirectoryServiceObjectAdapter(ReferenceCountedDisposable<IDirectorySf> baseDirectory)
|
||||
{
|
||||
BaseDirectory = baseDirectory.AddReference();
|
||||
}
|
||||
|
||||
protected override Result DoRead(out long entriesRead, Span<DirectoryEntry> entryBuffer)
|
||||
{
|
||||
Span<byte> buffer = MemoryMarshal.Cast<DirectoryEntry, byte>(entryBuffer);
|
||||
return BaseDirectory.Target.Read(out entriesRead, new OutBuffer(buffer));
|
||||
}
|
||||
|
||||
protected override Result DoGetEntryCount(out long entryCount)
|
||||
{
|
||||
return BaseDirectory.Target.GetEntryCount(out entryCount);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
BaseDirectory?.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
75
src/LibHac/Fs/Impl/FileServiceObjectAdapter.cs
Normal file
75
src/LibHac/Fs/Impl/FileServiceObjectAdapter.cs
Normal file
@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using IFile = LibHac.Fs.Fsa.IFile;
|
||||
using IFileSf = LibHac.FsSrv.Sf.IFile;
|
||||
|
||||
namespace LibHac.Fs.Impl
|
||||
{
|
||||
/// <summary>
|
||||
/// An adapter for using an <see cref="IFileSf"/> service object as an <see cref="IFile"/>. Used
|
||||
/// when receiving a Horizon IPC file object so it can be used as an <see cref="IFile"/> locally.
|
||||
/// </summary>
|
||||
internal class FileServiceObjectAdapter : IFile
|
||||
{
|
||||
private ReferenceCountedDisposable<IFileSf> BaseFile { get; }
|
||||
|
||||
public FileServiceObjectAdapter(ReferenceCountedDisposable<IFileSf> baseFile)
|
||||
{
|
||||
BaseFile = baseFile.AddReference();
|
||||
}
|
||||
|
||||
protected override Result DoRead(out long bytesRead, long offset, Span<byte> destination, in ReadOption option)
|
||||
{
|
||||
return BaseFile.Target.Read(out bytesRead, offset, destination, option);
|
||||
}
|
||||
|
||||
protected override Result DoWrite(long offset, ReadOnlySpan<byte> source, in WriteOption option)
|
||||
{
|
||||
return BaseFile.Target.Write(offset, source, option);
|
||||
}
|
||||
|
||||
protected override Result DoFlush()
|
||||
{
|
||||
return BaseFile.Target.Flush();
|
||||
}
|
||||
|
||||
protected override Result DoSetSize(long size)
|
||||
{
|
||||
return BaseFile.Target.SetSize(size);
|
||||
}
|
||||
|
||||
protected override Result DoGetSize(out long size)
|
||||
{
|
||||
return BaseFile.Target.GetSize(out size);
|
||||
}
|
||||
|
||||
protected override Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan<byte> inBuffer)
|
||||
{
|
||||
switch (operationId)
|
||||
{
|
||||
case OperationId.InvalidateCache:
|
||||
return BaseFile.Target.OperateRange(out _, (int)OperationId.InvalidateCache, offset, size);
|
||||
case OperationId.QueryRange:
|
||||
if (outBuffer.Length != Unsafe.SizeOf<QueryRangeInfo>())
|
||||
return ResultFs.InvalidSize.Log();
|
||||
|
||||
ref QueryRangeInfo info = ref SpanHelpers.AsStruct<QueryRangeInfo>(outBuffer);
|
||||
|
||||
return BaseFile.Target.OperateRange(out info, (int)OperationId.QueryRange, offset, size);
|
||||
default:
|
||||
return ResultFs.UnsupportedOperationInFileServiceObjectAdapterA.Log();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
BaseFile?.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
231
src/LibHac/Fs/Impl/FileSystemServiceObjectAdapter.cs
Normal file
231
src/LibHac/Fs/Impl/FileSystemServiceObjectAdapter.cs
Normal file
@ -0,0 +1,231 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.Util;
|
||||
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||
using IFileSf = LibHac.FsSrv.Sf.IFile;
|
||||
using IDirectorySf = LibHac.FsSrv.Sf.IDirectory;
|
||||
|
||||
namespace LibHac.Fs.Impl
|
||||
{
|
||||
/// <summary>
|
||||
/// An adapter for using an <see cref="IFileSystemSf"/> service object as an <see cref="Fsa.IFileSystem"/>. Used
|
||||
/// when receiving a Horizon IPC file system object so it can be used as an <see cref="Fsa.IFileSystem"/> locally.
|
||||
/// </summary>
|
||||
internal class FileSystemServiceObjectAdapter : Fsa.IFileSystem, IMultiCommitTarget
|
||||
{
|
||||
private ReferenceCountedDisposable<IFileSystemSf> BaseFs { get; }
|
||||
|
||||
public FileSystemServiceObjectAdapter(ReferenceCountedDisposable<IFileSystemSf> baseFileSystem)
|
||||
{
|
||||
BaseFs = baseFileSystem.AddReference();
|
||||
}
|
||||
|
||||
protected override Result DoCreateFile(U8Span path, long size, CreateFileOptions option)
|
||||
{
|
||||
Result rc = GetPathForServiceObject(out Path sfPath, path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return BaseFs.Target.CreateFile(in sfPath, size, (int)option);
|
||||
}
|
||||
|
||||
protected override Result DoDeleteFile(U8Span path)
|
||||
{
|
||||
Result rc = GetPathForServiceObject(out Path sfPath, path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return BaseFs.Target.DeleteFile(in sfPath);
|
||||
}
|
||||
|
||||
protected override Result DoCreateDirectory(U8Span path)
|
||||
{
|
||||
Result rc = GetPathForServiceObject(out Path sfPath, path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return BaseFs.Target.DeleteFile(in sfPath);
|
||||
}
|
||||
|
||||
protected override Result DoDeleteDirectory(U8Span path)
|
||||
{
|
||||
Result rc = GetPathForServiceObject(out Path sfPath, path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return BaseFs.Target.DeleteDirectory(in sfPath);
|
||||
}
|
||||
|
||||
protected override Result DoDeleteDirectoryRecursively(U8Span path)
|
||||
{
|
||||
Result rc = GetPathForServiceObject(out Path sfPath, path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return BaseFs.Target.DeleteDirectoryRecursively(in sfPath);
|
||||
}
|
||||
|
||||
protected override Result DoCleanDirectoryRecursively(U8Span path)
|
||||
{
|
||||
Result rc = GetPathForServiceObject(out Path sfPath, path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return BaseFs.Target.CleanDirectoryRecursively(in sfPath);
|
||||
}
|
||||
|
||||
protected override Result DoRenameFile(U8Span oldPath, U8Span newPath)
|
||||
{
|
||||
Result rc = GetPathForServiceObject(out Path oldSfPath, oldPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = GetPathForServiceObject(out Path newSfPath, newPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return BaseFs.Target.RenameFile(in oldSfPath, in newSfPath);
|
||||
}
|
||||
|
||||
protected override Result DoRenameDirectory(U8Span oldPath, U8Span newPath)
|
||||
{
|
||||
Result rc = GetPathForServiceObject(out Path oldSfPath, oldPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = GetPathForServiceObject(out Path newSfPath, newPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return BaseFs.Target.RenameDirectory(in oldSfPath, in newSfPath);
|
||||
}
|
||||
|
||||
protected override Result DoGetEntryType(out DirectoryEntryType entryType, U8Span path)
|
||||
{
|
||||
Unsafe.SkipInit(out entryType);
|
||||
|
||||
Result rc = GetPathForServiceObject(out Path sfPath, path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
ref uint sfEntryType = ref Unsafe.As<DirectoryEntryType, uint>(ref entryType);
|
||||
|
||||
return BaseFs.Target.GetEntryType(out sfEntryType, in sfPath);
|
||||
}
|
||||
|
||||
protected override Result DoGetFreeSpaceSize(out long freeSpace, U8Span path)
|
||||
{
|
||||
Unsafe.SkipInit(out freeSpace);
|
||||
|
||||
Result rc = GetPathForServiceObject(out Path sfPath, path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return BaseFs.Target.GetFreeSpaceSize(out freeSpace, in sfPath);
|
||||
}
|
||||
|
||||
protected override Result DoGetTotalSpaceSize(out long totalSpace, U8Span path)
|
||||
{
|
||||
Unsafe.SkipInit(out totalSpace);
|
||||
|
||||
Result rc = GetPathForServiceObject(out Path sfPath, path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return BaseFs.Target.GetTotalSpaceSize(out totalSpace, in sfPath);
|
||||
}
|
||||
|
||||
protected override Result DoOpenFile(out Fsa.IFile file, U8Span path, OpenMode mode)
|
||||
{
|
||||
file = default;
|
||||
|
||||
Result rc = GetPathForServiceObject(out Path sfPath, path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
ReferenceCountedDisposable<IFileSf> sfFile = null;
|
||||
try
|
||||
{
|
||||
rc = BaseFs.Target.OpenFile(out sfFile, in sfPath, (uint)mode);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
file = new FileServiceObjectAdapter(sfFile);
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
sfFile?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
protected override Result DoOpenDirectory(out Fsa.IDirectory directory, U8Span path, OpenDirectoryMode mode)
|
||||
{
|
||||
directory = default;
|
||||
|
||||
Result rc = GetPathForServiceObject(out Path sfPath, path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
ReferenceCountedDisposable<IDirectorySf> sfDir = null;
|
||||
try
|
||||
{
|
||||
rc = BaseFs.Target.OpenDirectory(out sfDir, in sfPath, (uint)mode);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
directory = new DirectoryServiceObjectAdapter(sfDir);
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
sfDir?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
protected override Result DoCommit()
|
||||
{
|
||||
return BaseFs.Target.Commit();
|
||||
}
|
||||
|
||||
protected override Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, U8Span path)
|
||||
{
|
||||
Unsafe.SkipInit(out timeStamp);
|
||||
|
||||
Result rc = GetPathForServiceObject(out Path sfPath, path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return BaseFs.Target.GetFileTimeStampRaw(out timeStamp, in sfPath);
|
||||
}
|
||||
|
||||
protected override Result DoQueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId,
|
||||
U8Span path)
|
||||
{
|
||||
Result rc = GetPathForServiceObject(out Path sfPath, path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return BaseFs.Target.QueryEntry(outBuffer, inBuffer, (int)queryId, in sfPath);
|
||||
}
|
||||
|
||||
public ReferenceCountedDisposable<IFileSystemSf> GetMultiCommitTarget()
|
||||
{
|
||||
return BaseFs.AddReference();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
BaseFs?.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private Result GetPathForServiceObject(out Path sfPath, U8Span path)
|
||||
{
|
||||
// This is the function used to create Sf.Path structs. Get an unsafe byte span for init only.
|
||||
Unsafe.SkipInit(out sfPath);
|
||||
Span<byte> outPath = SpanHelpers.AsByteSpan(ref sfPath);
|
||||
|
||||
// Copy and null terminate
|
||||
StringUtils.Copy(outPath, path);
|
||||
outPath[Unsafe.SizeOf<Path>() - 1] = StringTraits.NullTerminator;
|
||||
|
||||
// Replace directory separators
|
||||
PathUtility.Replace(outPath, StringTraits.AltDirectorySeparator, StringTraits.DirectorySeparator);
|
||||
|
||||
// Get lengths
|
||||
int windowsSkipLength = PathUtility.GetWindowsPathSkipLength(path);
|
||||
var nonWindowsPath = new U8Span(sfPath.Str.Slice(windowsSkipLength));
|
||||
int maxLength = PathTool.EntryNameLengthMax - windowsSkipLength;
|
||||
return PathUtility.VerifyPath(nonWindowsPath, maxLength, maxLength);
|
||||
}
|
||||
}
|
||||
}
|
53
src/LibHac/Fs/Impl/ReaderWriterLockHandlers.cs
Normal file
53
src/LibHac/Fs/Impl/ReaderWriterLockHandlers.cs
Normal file
@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace LibHac.Fs.Impl
|
||||
{
|
||||
/// <summary>
|
||||
/// A wrapper for handling write access to a reader-writer lock.
|
||||
/// </summary>
|
||||
public class UniqueLock : IDisposable
|
||||
{
|
||||
private ReaderWriterLockSlim _lock;
|
||||
private bool _hasLock;
|
||||
|
||||
public UniqueLock(ReaderWriterLockSlim readerWriterLock)
|
||||
{
|
||||
_lock = readerWriterLock;
|
||||
readerWriterLock.EnterWriteLock();
|
||||
_hasLock = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_hasLock)
|
||||
{
|
||||
_lock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A wrapper for handling read access to a reader-writer lock.
|
||||
/// </summary>
|
||||
public class SharedLock : IDisposable
|
||||
{
|
||||
private ReaderWriterLockSlim _lock;
|
||||
private bool _hasLock;
|
||||
|
||||
public SharedLock(ReaderWriterLockSlim readerWriterLock)
|
||||
{
|
||||
_lock = readerWriterLock;
|
||||
readerWriterLock.EnterReadLock();
|
||||
_hasLock = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_hasLock)
|
||||
{
|
||||
_lock.EnterReadLock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
73
src/LibHac/Fs/Impl/StorageServiceObjectAdapter.cs
Normal file
73
src/LibHac/Fs/Impl/StorageServiceObjectAdapter.cs
Normal file
@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using IStorageSf = LibHac.FsSrv.Sf.IStorage;
|
||||
|
||||
namespace LibHac.Fs.Impl
|
||||
{
|
||||
/// <summary>
|
||||
/// An adapter for using an <see cref="IStorageSf"/> service object as an <see cref="IStorage"/>. Used
|
||||
/// when receiving a Horizon IPC storage object so it can be used as an <see cref="IStorage"/> locally.
|
||||
/// </summary>
|
||||
internal class StorageServiceObjectAdapter : IStorage
|
||||
{
|
||||
private ReferenceCountedDisposable<IStorageSf> BaseStorage { get; }
|
||||
|
||||
public StorageServiceObjectAdapter(ReferenceCountedDisposable<IStorageSf> baseStorage)
|
||||
{
|
||||
BaseStorage = baseStorage.AddReference();
|
||||
}
|
||||
protected override Result DoRead(long offset, Span<byte> destination)
|
||||
{
|
||||
return BaseStorage.Target.Read(offset, destination);
|
||||
}
|
||||
|
||||
protected override Result DoWrite(long offset, ReadOnlySpan<byte> source)
|
||||
{
|
||||
return BaseStorage.Target.Write(offset, source);
|
||||
}
|
||||
|
||||
protected override Result DoFlush()
|
||||
{
|
||||
return BaseStorage.Target.Flush();
|
||||
}
|
||||
|
||||
protected override Result DoSetSize(long size)
|
||||
{
|
||||
return BaseStorage.Target.SetSize(size);
|
||||
}
|
||||
|
||||
protected override Result DoGetSize(out long size)
|
||||
{
|
||||
return BaseStorage.Target.GetSize(out size);
|
||||
}
|
||||
|
||||
protected override Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan<byte> inBuffer)
|
||||
{
|
||||
switch (operationId)
|
||||
{
|
||||
case OperationId.InvalidateCache:
|
||||
return BaseStorage.Target.OperateRange(out _, (int)OperationId.InvalidateCache, offset, size);
|
||||
case OperationId.QueryRange:
|
||||
if (outBuffer.Length != Unsafe.SizeOf<QueryRangeInfo>())
|
||||
return ResultFs.InvalidSize.Log();
|
||||
|
||||
ref QueryRangeInfo info = ref SpanHelpers.AsStruct<QueryRangeInfo>(outBuffer);
|
||||
|
||||
return BaseStorage.Target.OperateRange(out info, (int)OperationId.QueryRange, offset, size);
|
||||
default:
|
||||
return ResultFs.UnsupportedOperationInFileServiceObjectAdapterA.Log();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
BaseStorage?.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
using LibHac.Common;
|
||||
using static LibHac.Fs.CommonMountNames;
|
||||
using static LibHac.Fs.CommonPaths;
|
||||
|
||||
namespace LibHac.Fs
|
||||
{
|
||||
|
@ -19,6 +19,7 @@ namespace LibHac.Fs
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsWindowsDriveCharacter(byte c)
|
||||
{
|
||||
// Mask lowercase letters to uppercase and check if it's in range
|
||||
return (0b1101_1111 & c) - 'A' <= 'Z' - 'A';
|
||||
//return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z';
|
||||
}
|
||||
|
@ -1,11 +1,31 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace LibHac.Fs
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x40)]
|
||||
public struct QueryRangeInfo
|
||||
{
|
||||
public uint AesCtrKeyType;
|
||||
public uint SpeedEmulationType;
|
||||
public int AesCtrKeyType;
|
||||
public int SpeedEmulationType;
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
this = default;
|
||||
}
|
||||
|
||||
public void Merge(in QueryRangeInfo other)
|
||||
{
|
||||
AesCtrKeyType |= other.AesCtrKeyType;
|
||||
SpeedEmulationType |= other.SpeedEmulationType;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum AesCtrKeyTypeFlag
|
||||
{
|
||||
InternalKeyForSoftwareAes = 1 << 0,
|
||||
InternalKeyForHardwareAes = 1 << 1,
|
||||
ExternalKeyForHardwareAes = 1 << 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,34 @@ 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> In OpenFile or OpenDirectory<br/>Error code: 2002-3407; Inner value: 0x1a9e02</summary>
|
||||
public static Result.Base AllocationFailureInFileSystemInterfaceAdapter => new Result.Base(ModuleFs, 3407);
|
||||
/// <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 +239,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); }
|
||||
@ -305,6 +339,8 @@ namespace LibHac.Fs
|
||||
|
||||
/// <summary>Error code: 2002-5000; Range: 5000-5999; Inner value: 0x271002</summary>
|
||||
public static Result.Base Unexpected { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 5000, 5999); }
|
||||
/// <summary>Error code: 2002-5121; Inner value: 0x280202</summary>
|
||||
public static Result.Base UnexpectedFatFileSystemSectorCount => new Result.Base(ModuleFs, 5121);
|
||||
/// <summary>Error code: 2002-5307; Inner value: 0x297602</summary>
|
||||
public static Result.Base UnexpectedErrorInHostFileFlush => new Result.Base(ModuleFs, 5307);
|
||||
/// <summary>Error code: 2002-5308; Inner value: 0x297802</summary>
|
||||
@ -336,7 +372,7 @@ namespace LibHac.Fs
|
||||
/// <summary>Error code: 2002-6031; Inner value: 0x2f1e02</summary>
|
||||
public static Result.Base DirectoryNotDeletable => new Result.Base(ModuleFs, 6031);
|
||||
/// <summary>Error code: 2002-6032; Inner value: 0x2f2002</summary>
|
||||
public static Result.Base DestinationIsSubPathOfSource => new Result.Base(ModuleFs, 6032);
|
||||
public static Result.Base DirectoryNotRenamable => new Result.Base(ModuleFs, 6032);
|
||||
/// <summary>Error code: 2002-6033; Inner value: 0x2f2202</summary>
|
||||
public static Result.Base PathNotFoundInSaveDataFileTable => new Result.Base(ModuleFs, 6033);
|
||||
/// <summary>Error code: 2002-6034; Inner value: 0x2f2402</summary>
|
||||
@ -387,6 +423,8 @@ namespace LibHac.Fs
|
||||
|
||||
/// <summary>Error code: 2002-6300; Range: 6300-6399; Inner value: 0x313802</summary>
|
||||
public static Result.Base UnsupportedOperation { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6300, 6399); }
|
||||
/// <summary>Error code: 2002-6301; Inner value: 0x313a02</summary>
|
||||
public static Result.Base UnsupportedCommitTarget => new Result.Base(ModuleFs, 6301);
|
||||
/// <summary>Attempted to resize a non-resizable SubStorage.<br/>Error code: 2002-6302; Inner value: 0x313c02</summary>
|
||||
public static Result.Base UnsupportedOperationInSubStorageSetSize => new Result.Base(ModuleFs, 6302);
|
||||
/// <summary>Attempted to resize a SubStorage that wasn't located at the end of the base storage.<br/>Error code: 2002-6303; Inner value: 0x313e02</summary>
|
||||
@ -409,6 +447,8 @@ namespace LibHac.Fs
|
||||
public static Result.Base UnsupportedOperationInRoGameCardStorageSetSize => new Result.Base(ModuleFs, 6351);
|
||||
/// <summary>Error code: 2002-6359; Inner value: 0x31ae02</summary>
|
||||
public static Result.Base UnsupportedOperationInConcatFsQueryEntry => new Result.Base(ModuleFs, 6359);
|
||||
/// <summary>Called OperateRange with an invalid operation ID.<br/>Error code: 2002-6362; Inner value: 0x31b402</summary>
|
||||
public static Result.Base UnsupportedOperationInFileServiceObjectAdapterA => new Result.Base(ModuleFs, 6362);
|
||||
/// <summary>Error code: 2002-6364; Inner value: 0x31b802</summary>
|
||||
public static Result.Base UnsupportedOperationModifyRomFsFileSystem => new Result.Base(ModuleFs, 6364);
|
||||
/// <summary>Called RomFsFileSystem::CommitProvisionally.<br/>Error code: 2002-6365; Inner value: 0x31ba02</summary>
|
||||
@ -443,14 +483,24 @@ namespace LibHac.Fs
|
||||
public static Result.Base ExternalKeyAlreadyRegistered => new Result.Base(ModuleFs, 6452);
|
||||
/// <summary>Error code: 2002-6454; Inner value: 0x326c02</summary>
|
||||
public static Result.Base WriteStateUnflushed => new Result.Base(ModuleFs, 6454);
|
||||
/// <summary>Error code: 2002-6456; Inner value: 0x327002</summary>
|
||||
public static Result.Base DirectoryNotClosed => new Result.Base(ModuleFs, 6456);
|
||||
/// <summary>Error code: 2002-6457; Inner value: 0x327202</summary>
|
||||
public static Result.Base WriteModeFileNotClosed => new Result.Base(ModuleFs, 6457);
|
||||
/// <summary>Error code: 2002-6458; Inner value: 0x327402</summary>
|
||||
public static Result.Base AllocatorAlreadyRegistered => new Result.Base(ModuleFs, 6458);
|
||||
/// <summary>Error code: 2002-6459; Inner value: 0x327602</summary>
|
||||
public static Result.Base DefaultAllocatorUsed => new Result.Base(ModuleFs, 6459);
|
||||
/// <summary>Error code: 2002-6461; Inner value: 0x327a02</summary>
|
||||
public static Result.Base AllocatorAlignmentViolation => new Result.Base(ModuleFs, 6461);
|
||||
/// <summary>The provided file system has already been added to the multi-commit manager.<br/>Error code: 2002-6463; Inner value: 0x327e02</summary>
|
||||
public static Result.Base MultiCommitFileSystemAlreadyAdded => new Result.Base(ModuleFs, 6463);
|
||||
/// <summary>Error code: 2002-6465; Inner value: 0x328202</summary>
|
||||
public static Result.Base UserNotExist => new Result.Base(ModuleFs, 6465);
|
||||
/// <summary>Error code: 2002-6466; Inner value: 0x328402</summary>
|
||||
public static Result.Base DefaultGlobalFileDataCacheEnabled => new Result.Base(ModuleFs, 6466);
|
||||
/// <summary>Error code: 2002-6467; Inner value: 0x328602</summary>
|
||||
public static Result.Base SaveDataRootPathUnavailable => new Result.Base(ModuleFs, 6467);
|
||||
|
||||
/// <summary>Error code: 2002-6600; Range: 6600-6699; Inner value: 0x339002</summary>
|
||||
public static Result.Base EntryNotFound { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6600, 6699); }
|
||||
|
@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Util;
|
||||
|
||||
namespace LibHac.Fs
|
||||
{
|
||||
@ -13,17 +15,17 @@ namespace LibHac.Fs
|
||||
[FieldOffset(0x18)] public ulong StaticSaveDataId;
|
||||
[FieldOffset(0x20)] public SaveDataType Type;
|
||||
[FieldOffset(0x21)] public SaveDataRank Rank;
|
||||
[FieldOffset(0x22)] public short Index;
|
||||
[FieldOffset(0x22)] public ushort Index;
|
||||
|
||||
public SaveDataAttribute(ProgramId programId, SaveDataType type, UserId userId, ulong saveDataId) : this(
|
||||
programId, type, userId, saveDataId, 0, SaveDataRank.Primary)
|
||||
{ }
|
||||
|
||||
public SaveDataAttribute(ProgramId programId, SaveDataType type, UserId userId, ulong saveDataId,
|
||||
short index) : this(programId, type, userId, saveDataId, index, SaveDataRank.Primary)
|
||||
ushort index) : this(programId, type, userId, saveDataId, index, SaveDataRank.Primary)
|
||||
{ }
|
||||
|
||||
public SaveDataAttribute(ProgramId programId, SaveDataType type, UserId userId, ulong saveDataId, short index,
|
||||
public SaveDataAttribute(ProgramId programId, SaveDataType type, UserId userId, ulong saveDataId, ushort index,
|
||||
SaveDataRank rank)
|
||||
{
|
||||
ProgramId = programId;
|
||||
@ -34,6 +36,38 @@ namespace LibHac.Fs
|
||||
Rank = rank;
|
||||
}
|
||||
|
||||
public static Result Make(out SaveDataAttribute attribute, ProgramId programId, SaveDataType type,
|
||||
UserId userId, ulong staticSaveDataId)
|
||||
{
|
||||
return Make(out attribute, programId, type, userId, staticSaveDataId, 0, SaveDataRank.Primary);
|
||||
}
|
||||
|
||||
public static Result Make(out SaveDataAttribute attribute, ProgramId programId, SaveDataType type,
|
||||
UserId userId, ulong staticSaveDataId, ushort index)
|
||||
{
|
||||
return Make(out attribute, programId, type, userId, staticSaveDataId, index, SaveDataRank.Primary);
|
||||
}
|
||||
|
||||
public static Result Make(out SaveDataAttribute attribute, ProgramId programId, SaveDataType type,
|
||||
UserId userId, ulong staticSaveDataId, ushort index, SaveDataRank rank)
|
||||
{
|
||||
Unsafe.SkipInit(out attribute);
|
||||
SaveDataAttribute tempAttribute = default;
|
||||
|
||||
tempAttribute.ProgramId = programId;
|
||||
tempAttribute.Type = type;
|
||||
tempAttribute.UserId = userId;
|
||||
tempAttribute.StaticSaveDataId = staticSaveDataId;
|
||||
tempAttribute.Index = index;
|
||||
tempAttribute.Rank = rank;
|
||||
|
||||
if (!SaveDataTypesValidity.IsValid(in tempAttribute))
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
|
||||
attribute = tempAttribute;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public override readonly bool Equals(object obj)
|
||||
{
|
||||
return obj is SaveDataAttribute attribute && Equals(attribute);
|
||||
@ -85,40 +119,96 @@ namespace LibHac.Fs
|
||||
[FieldOffset(0x04)] public bool FilterByIndex;
|
||||
[FieldOffset(0x05)] public SaveDataRank Rank;
|
||||
|
||||
[FieldOffset(0x08)] public ProgramId ProgramId;
|
||||
[FieldOffset(0x10)] public UserId UserId;
|
||||
[FieldOffset(0x20)] public ulong SaveDataId;
|
||||
[FieldOffset(0x28)] public SaveDataType SaveDataType;
|
||||
[FieldOffset(0x2A)] public short Index;
|
||||
[FieldOffset(0x08)] public SaveDataAttribute Attribute;
|
||||
|
||||
public void SetProgramId(ProgramId value)
|
||||
{
|
||||
FilterByProgramId = true;
|
||||
ProgramId = value;
|
||||
Attribute.ProgramId = value;
|
||||
}
|
||||
|
||||
public void SetSaveDataType(SaveDataType value)
|
||||
{
|
||||
FilterBySaveDataType = true;
|
||||
SaveDataType = value;
|
||||
Attribute.Type = value;
|
||||
}
|
||||
|
||||
public void SetUserId(UserId value)
|
||||
{
|
||||
FilterByUserId = true;
|
||||
UserId = value;
|
||||
Attribute.UserId = value;
|
||||
}
|
||||
|
||||
public void SetSaveDataId(ulong value)
|
||||
{
|
||||
FilterBySaveDataId = true;
|
||||
SaveDataId = value;
|
||||
Attribute.StaticSaveDataId = value;
|
||||
}
|
||||
|
||||
public void SetIndex(short value)
|
||||
public void SetIndex(ushort value)
|
||||
{
|
||||
FilterByIndex = true;
|
||||
Index = value;
|
||||
Attribute.Index = value;
|
||||
}
|
||||
|
||||
public static Result Make(out SaveDataFilter filter, Optional<ulong> programId, Optional<SaveDataType> saveType,
|
||||
Optional<UserId> userId, Optional<ulong> saveDataId, Optional<ushort> index)
|
||||
{
|
||||
return Make(out filter, programId, saveType, userId, saveDataId, index, SaveDataRank.Primary);
|
||||
}
|
||||
|
||||
public static Result Make(out SaveDataFilter filter, Optional<ulong> programId, Optional<SaveDataType> saveType,
|
||||
Optional<UserId> userId, Optional<ulong> saveDataId, Optional<ushort> index, SaveDataRank rank)
|
||||
{
|
||||
Unsafe.SkipInit(out filter);
|
||||
|
||||
SaveDataFilter tempFilter = Make(programId, saveType, userId, saveDataId, index, rank);
|
||||
|
||||
if (!SaveDataTypesValidity.IsValid(in tempFilter))
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
|
||||
filter = tempFilter;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static SaveDataFilter Make(Optional<ulong> programId, Optional<SaveDataType> saveType,
|
||||
Optional<UserId> userId, Optional<ulong> saveDataId, Optional<ushort> index, SaveDataRank rank)
|
||||
{
|
||||
var filter = new SaveDataFilter();
|
||||
|
||||
if (programId.HasValue)
|
||||
{
|
||||
filter.FilterByProgramId = true;
|
||||
filter.Attribute.ProgramId = new ProgramId(programId.Value);
|
||||
}
|
||||
|
||||
if (saveType.HasValue)
|
||||
{
|
||||
filter.FilterBySaveDataType = true;
|
||||
filter.Attribute.Type = saveType.Value;
|
||||
}
|
||||
|
||||
if (userId.HasValue)
|
||||
{
|
||||
filter.FilterByUserId = true;
|
||||
filter.Attribute.UserId = userId.Value;
|
||||
}
|
||||
|
||||
if (saveDataId.HasValue)
|
||||
{
|
||||
filter.FilterBySaveDataId = true;
|
||||
filter.Attribute.StaticSaveDataId = saveDataId.Value;
|
||||
}
|
||||
|
||||
if (index.HasValue)
|
||||
{
|
||||
filter.FilterByIndex = true;
|
||||
filter.Attribute.Index = index.Value;
|
||||
}
|
||||
|
||||
filter.Rank = rank;
|
||||
|
||||
return filter;
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,6 +220,7 @@ namespace LibHac.Fs
|
||||
[FieldOffset(0x00)] private byte _hashStart;
|
||||
|
||||
public Span<byte> Hash => SpanHelpers.CreateSpan(ref _hashStart, HashLength);
|
||||
public ReadOnlySpan<byte> HashRo => SpanHelpers.CreateReadOnlySpan(in _hashStart, HashLength);
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
@ -140,7 +231,7 @@ namespace LibHac.Fs
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x10)]
|
||||
public struct SaveMetaCreateInfo
|
||||
public struct SaveDataMetaInfo
|
||||
{
|
||||
[FieldOffset(0)] public int Size;
|
||||
[FieldOffset(4)] public SaveDataMetaType Type;
|
||||
@ -151,7 +242,7 @@ namespace LibHac.Fs
|
||||
{
|
||||
[FieldOffset(0x00)] public long Size;
|
||||
[FieldOffset(0x08)] public long JournalSize;
|
||||
[FieldOffset(0x10)] public ulong BlockSize;
|
||||
[FieldOffset(0x10)] public long BlockSize;
|
||||
[FieldOffset(0x18)] public ulong OwnerId;
|
||||
[FieldOffset(0x20)] public SaveDataFlags Flags;
|
||||
[FieldOffset(0x24)] public SaveDataSpaceId SpaceId;
|
||||
@ -168,8 +259,66 @@ namespace LibHac.Fs
|
||||
[FieldOffset(0x20)] public ulong StaticSaveDataId;
|
||||
[FieldOffset(0x28)] public ProgramId ProgramId;
|
||||
[FieldOffset(0x30)] public long Size;
|
||||
[FieldOffset(0x38)] public short Index;
|
||||
[FieldOffset(0x38)] public ushort Index;
|
||||
[FieldOffset(0x3A)] public SaveDataRank Rank;
|
||||
[FieldOffset(0x3B)] public SaveDataState State;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Size = 0x200)]
|
||||
public struct SaveDataExtraData
|
||||
{
|
||||
[FieldOffset(0x00)] public SaveDataAttribute Attribute;
|
||||
[FieldOffset(0x40)] public ulong OwnerId;
|
||||
[FieldOffset(0x48)] public ulong TimeStamp;
|
||||
[FieldOffset(0x50)] public SaveDataFlags Flags;
|
||||
[FieldOffset(0x58)] public long DataSize;
|
||||
[FieldOffset(0x60)] public long JournalSize;
|
||||
[FieldOffset(0x68)] public long CommitId;
|
||||
}
|
||||
|
||||
internal static class SaveDataTypesValidity
|
||||
{
|
||||
public static bool IsValid(in SaveDataAttribute attribute)
|
||||
{
|
||||
return IsValid(in attribute.Type) && IsValid(in attribute.Rank);
|
||||
}
|
||||
|
||||
public static bool IsValid(in SaveDataCreationInfo creationInfo)
|
||||
{
|
||||
return creationInfo.Size >= 0 && creationInfo.JournalSize >= 0 && creationInfo.BlockSize >= 0 &&
|
||||
IsValid(in creationInfo.SpaceId);
|
||||
}
|
||||
|
||||
public static bool IsValid(in SaveDataMetaInfo metaInfo)
|
||||
{
|
||||
return IsValid(in metaInfo.Type);
|
||||
}
|
||||
|
||||
public static bool IsValid(in SaveDataFilter filter)
|
||||
{
|
||||
return IsValid(in filter.Attribute);
|
||||
}
|
||||
|
||||
public static bool IsValid(in SaveDataType type)
|
||||
{
|
||||
// SaveDataType.SystemBcat is excluded in this check
|
||||
return (uint)type <= (uint)SaveDataType.Cache;
|
||||
}
|
||||
|
||||
public static bool IsValid(in SaveDataRank rank)
|
||||
{
|
||||
return (uint)rank <= (uint)SaveDataRank.Secondary;
|
||||
}
|
||||
|
||||
public static bool IsValid(in SaveDataSpaceId spaceId)
|
||||
{
|
||||
return (uint)spaceId <= (uint)SaveDataSpaceId.SdCache || spaceId == SaveDataSpaceId.ProperSystem ||
|
||||
spaceId == SaveDataSpaceId.SafeMode;
|
||||
}
|
||||
|
||||
public static bool IsValid(in SaveDataMetaType metaType)
|
||||
{
|
||||
return (uint)metaType <= (uint)SaveDataMetaType.ExtensionContext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ using LibHac.FsSrv;
|
||||
namespace LibHac.Fs
|
||||
{
|
||||
/// <summary>
|
||||
/// The default access logger that will output to the SD card via <see cref="FileSystemProxy"/>.
|
||||
/// The default access logger that will output to the SD card via <see cref="FileSystemProxyImpl"/>.
|
||||
/// </summary>
|
||||
public class SdCardAccessLog : IAccessLog
|
||||
{
|
||||
|
@ -1,8 +1,9 @@
|
||||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Fs.Impl;
|
||||
using LibHac.FsSrv;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||
|
||||
namespace LibHac.Fs.Shim
|
||||
{
|
||||
@ -40,14 +41,20 @@ 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<IFileSystemSf> fileSystem, in sfPath,
|
||||
default, FileSystemProxyType.Package);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return fs.Register(mountName, fileSystem);
|
||||
using (fileSystem)
|
||||
{
|
||||
var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem);
|
||||
|
||||
return fs.Register(mountName, fileSystemAdapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Fs.Impl;
|
||||
using LibHac.FsSrv;
|
||||
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||
|
||||
namespace LibHac.Fs.Shim
|
||||
{
|
||||
@ -43,12 +44,23 @@ namespace LibHac.Fs.Shim
|
||||
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
var attribute = new SaveDataAttribute(applicationId, SaveDataType.Bcat, UserId.Zero, 0);
|
||||
var attribute = new SaveDataAttribute(applicationId, SaveDataType.Bcat, UserId.InvalidId, 0);
|
||||
|
||||
rc = fsProxy.OpenSaveDataFileSystem(out IFileSystem fileSystem, SaveDataSpaceId.User, ref attribute);
|
||||
if (rc.IsFailure()) return rc;
|
||||
ReferenceCountedDisposable<IFileSystemSf> saveFs = null;
|
||||
|
||||
return fs.Register(mountName, fileSystem);
|
||||
try
|
||||
{
|
||||
rc = fsProxy.OpenSaveDataFileSystem(out saveFs, SaveDataSpaceId.User, in attribute);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var fileSystemAdapter = new FileSystemServiceObjectAdapter(saveFs);
|
||||
|
||||
return fs.Register(mountName, fileSystemAdapter);
|
||||
}
|
||||
finally
|
||||
{
|
||||
saveFs?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,15 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Fs.Impl;
|
||||
using LibHac.FsSrv;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.Util;
|
||||
using static LibHac.Fs.CommonMountNames;
|
||||
using static LibHac.Fs.CommonPaths;
|
||||
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||
using IStorageSf = LibHac.FsSrv.Sf.IStorage;
|
||||
|
||||
namespace LibHac.Fs.Shim
|
||||
{
|
||||
@ -80,25 +84,28 @@ namespace LibHac.Fs.Shim
|
||||
}
|
||||
|
||||
// ReSharper disable once UnusedParameter.Local
|
||||
private static Result MountBisImpl(FileSystemClient fs, U8Span mountName, BisPartitionId partitionId, U8Span rootPath)
|
||||
private static Result MountBisImpl(FileSystemClient fs, U8Span mountName, BisPartitionId partitionId,
|
||||
U8Span rootPath)
|
||||
{
|
||||
Result rc = MountHelpers.CheckMountNameAcceptingReservedMountName(mountName);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
FsPath sfPath;
|
||||
unsafe { _ = &sfPath; } // workaround for CS0165
|
||||
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
// Nintendo doesn't use the provided rootPath
|
||||
sfPath.Str[0] = 0;
|
||||
FspPath.CreateEmpty(out FspPath sfPath);
|
||||
|
||||
rc = fsProxy.OpenBisFileSystem(out IFileSystem fileSystem, ref sfPath, partitionId);
|
||||
rc = fsProxy.OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, in sfPath,
|
||||
partitionId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var nameGenerator = new BisCommonMountNameGenerator(partitionId);
|
||||
using (fileSystem)
|
||||
{
|
||||
var nameGenerator = new BisCommonMountNameGenerator(partitionId);
|
||||
var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem);
|
||||
|
||||
return fs.Register(mountName, fileSystem, nameGenerator);
|
||||
return fs.Register(mountName, fileSystemAdapter, nameGenerator);
|
||||
}
|
||||
}
|
||||
|
||||
public static U8Span GetBisMountName(BisPartitionId partitionId)
|
||||
@ -134,8 +141,7 @@ namespace LibHac.Fs.Shim
|
||||
// todo: Decide how to handle SetBisRootForHost since it allows mounting any directory on the user's computer
|
||||
public static Result SetBisRootForHost(this FileSystemClient fs, BisPartitionId partitionId, U8Span rootPath)
|
||||
{
|
||||
FsPath sfPath;
|
||||
unsafe { _ = &sfPath; } // workaround for CS0165
|
||||
Unsafe.SkipInit(out FsPath path);
|
||||
|
||||
int pathLen = StringUtils.GetLength(rootPath, PathTools.MaxPathLength + 1);
|
||||
if (pathLen > PathTools.MaxPathLength)
|
||||
@ -147,30 +153,38 @@ namespace LibHac.Fs.Shim
|
||||
? StringTraits.NullTerminator
|
||||
: StringTraits.DirectorySeparator;
|
||||
|
||||
var sb = new U8StringBuilder(sfPath.Str);
|
||||
var sb = new U8StringBuilder(path.Str);
|
||||
Result rc = sb.Append(rootPath).Append(endingSeparator).ToSfPath();
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
else
|
||||
{
|
||||
sfPath.Str[0] = StringTraits.NullTerminator;
|
||||
path.Str[0] = StringTraits.NullTerminator;
|
||||
}
|
||||
|
||||
FspPath.FromSpan(out FspPath sfPath, path.Str);
|
||||
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
return fsProxy.SetBisRootForHost(partitionId, ref sfPath);
|
||||
return fsProxy.SetBisRootForHost(partitionId, in sfPath);
|
||||
}
|
||||
|
||||
public static Result OpenBisPartition(this FileSystemClient fs, out IStorage partitionStorage, BisPartitionId partitionId)
|
||||
public static Result OpenBisPartition(this FileSystemClient fs, out IStorage partitionStorage,
|
||||
BisPartitionId partitionId)
|
||||
{
|
||||
partitionStorage = default;
|
||||
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
Result rc = fsProxy.OpenBisStorage(out IStorage storage, partitionId);
|
||||
Result rc = fsProxy.OpenBisStorage(out ReferenceCountedDisposable<IStorageSf> storage, partitionId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
partitionStorage = storage;
|
||||
return Result.Success;
|
||||
using (storage)
|
||||
{
|
||||
var storageAdapter = new StorageServiceObjectAdapter(storage);
|
||||
|
||||
partitionStorage = storageAdapter;
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
public static Result InvalidateBisCache(this FileSystemClient fs)
|
||||
|
@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Fs.Impl;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.Ncm;
|
||||
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||
|
||||
namespace LibHac.Fs.Shim
|
||||
{
|
||||
@ -49,10 +50,16 @@ namespace LibHac.Fs.Shim
|
||||
|
||||
IFileSystemProxyForLoader fsProxy = fs.GetFileSystemProxyForLoaderServiceObject();
|
||||
|
||||
rc = fsProxy.OpenCodeFileSystem(out IFileSystem codeFs, out verificationData, in fsPath, programId);
|
||||
rc = fsProxy.OpenCodeFileSystem(out ReferenceCountedDisposable<IFileSystemSf> codeFs, out verificationData,
|
||||
in fsPath, programId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return fs.Register(mountName, codeFs);
|
||||
using (codeFs)
|
||||
{
|
||||
var fileSystemAdapter = new FileSystemServiceObjectAdapter(codeFs);
|
||||
|
||||
return fs.Register(mountName, fileSystemAdapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Fs.Impl;
|
||||
using LibHac.FsSrv;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.Ncm;
|
||||
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||
|
||||
namespace LibHac.Fs.Shim
|
||||
{
|
||||
@ -27,10 +28,16 @@ namespace LibHac.Fs.Shim
|
||||
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
rc = fsProxy.OpenFileSystemWithPatch(out IFileSystem fileSystem, programId, fspType);
|
||||
rc = fsProxy.OpenFileSystemWithPatch(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, programId,
|
||||
fspType);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return fs.Register(mountName, fileSystem);
|
||||
using (fileSystem)
|
||||
{
|
||||
var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem);
|
||||
|
||||
return fs.Register(mountName, fileSystemAdapter);
|
||||
}
|
||||
}
|
||||
|
||||
public static Result MountContent(this FileSystemClient fs, U8Span mountName, U8Span path, ProgramId programId, ContentType type)
|
||||
@ -55,14 +62,20 @@ 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<IFileSystemSf> fileSystem, in fsPath,
|
||||
id, type);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return fs.Register(mountName, fileSystem);
|
||||
using (fileSystem)
|
||||
{
|
||||
var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem);
|
||||
|
||||
return fs.Register(mountName, fileSystemAdapter);
|
||||
}
|
||||
}
|
||||
|
||||
private static FileSystemProxyType ConvertToFileSystemProxyType(ContentType type) => type switch
|
||||
@ -71,7 +84,7 @@ namespace LibHac.Fs.Shim
|
||||
ContentType.Control => FileSystemProxyType.Control,
|
||||
ContentType.Manual => FileSystemProxyType.Manual,
|
||||
ContentType.Data => FileSystemProxyType.Data,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Fs.Impl;
|
||||
using LibHac.FsSrv;
|
||||
using LibHac.Util;
|
||||
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||
|
||||
namespace LibHac.Fs.Shim
|
||||
{
|
||||
@ -20,12 +21,17 @@ namespace LibHac.Fs.Shim
|
||||
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
rc = fsProxy.OpenContentStorageFileSystem(out IFileSystem contentFs, storageId);
|
||||
rc = fsProxy.OpenContentStorageFileSystem(out ReferenceCountedDisposable<IFileSystemSf> contentFs, storageId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var mountNameGenerator = new ContentStorageCommonMountNameGenerator(storageId);
|
||||
using (contentFs)
|
||||
{
|
||||
var mountNameGenerator = new ContentStorageCommonMountNameGenerator(storageId);
|
||||
|
||||
return fs.Register(mountName, contentFs, mountNameGenerator);
|
||||
var fileSystemAdapter = new FileSystemServiceObjectAdapter(contentFs);
|
||||
|
||||
return fs.Register(mountName, fileSystemAdapter, mountNameGenerator);
|
||||
}
|
||||
}
|
||||
|
||||
public static U8String GetContentStorageMountName(ContentStorageId storageId)
|
||||
@ -33,11 +39,11 @@ namespace LibHac.Fs.Shim
|
||||
switch (storageId)
|
||||
{
|
||||
case ContentStorageId.System:
|
||||
return CommonMountNames.ContentStorageSystemMountName;
|
||||
return CommonPaths.ContentStorageSystemMountName;
|
||||
case ContentStorageId.User:
|
||||
return CommonMountNames.ContentStorageUserMountName;
|
||||
return CommonPaths.ContentStorageUserMountName;
|
||||
case ContentStorageId.SdCard:
|
||||
return CommonMountNames.ContentStorageSdCardMountName;
|
||||
return CommonPaths.ContentStorageSdCardMountName;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(storageId), storageId, null);
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Fs.Impl;
|
||||
using LibHac.FsSrv;
|
||||
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||
|
||||
namespace LibHac.Fs.Shim
|
||||
{
|
||||
@ -12,12 +13,22 @@ namespace LibHac.Fs.Shim
|
||||
Result rc = MountHelpers.CheckMountName(mountName);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
ReferenceCountedDisposable<IFileSystemSf> customFs = null;
|
||||
try
|
||||
{
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
rc = fsProxy.OpenCustomStorageFileSystem(out IFileSystem customFs, storageId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
rc = fsProxy.OpenCustomStorageFileSystem(out customFs, storageId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return fs.Register(mountName, customFs);
|
||||
var adapter = new FileSystemServiceObjectAdapter(customFs);
|
||||
|
||||
return fs.Register(mountName, adapter);
|
||||
}
|
||||
finally
|
||||
{
|
||||
customFs?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetCustomStorageDirectoryName(CustomStorageId storageId)
|
||||
|
@ -1,7 +1,10 @@
|
||||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Fs.Impl;
|
||||
using LibHac.FsSrv;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||
using IStorageSf = LibHac.FsSrv.Sf.IStorage;
|
||||
|
||||
namespace LibHac.Fs.Shim
|
||||
{
|
||||
@ -11,33 +14,63 @@ namespace LibHac.Fs.Shim
|
||||
{
|
||||
handle = default;
|
||||
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
ReferenceCountedDisposable<IDeviceOperator> deviceOperator = null;
|
||||
try
|
||||
{
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
Result rc = fsProxy.OpenDeviceOperator(out IDeviceOperator deviceOperator);
|
||||
if (rc.IsFailure()) return rc;
|
||||
Result rc = fsProxy.OpenDeviceOperator(out deviceOperator);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return deviceOperator.GetGameCardHandle(out handle);
|
||||
return deviceOperator.Target.GetGameCardHandle(out handle);
|
||||
}
|
||||
finally
|
||||
{
|
||||
deviceOperator?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsGameCardInserted(this FileSystemClient fs)
|
||||
{
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
ReferenceCountedDisposable<IDeviceOperator> deviceOperator = null;
|
||||
try
|
||||
{
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
Result rc = fsProxy.OpenDeviceOperator(out IDeviceOperator deviceOperator);
|
||||
if (rc.IsFailure()) throw new LibHacException("Abort");
|
||||
Result rc = fsProxy.OpenDeviceOperator(out deviceOperator);
|
||||
if (rc.IsFailure()) throw new LibHacException("Abort");
|
||||
|
||||
rc = deviceOperator.IsGameCardInserted(out bool isInserted);
|
||||
if (rc.IsFailure()) throw new LibHacException("Abort");
|
||||
rc = deviceOperator.Target.IsGameCardInserted(out bool isInserted);
|
||||
if (rc.IsFailure()) throw new LibHacException("Abort");
|
||||
|
||||
return isInserted;
|
||||
return isInserted;
|
||||
}
|
||||
finally
|
||||
{
|
||||
deviceOperator?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static Result OpenGameCardPartition(this FileSystemClient fs, out IStorage storage,
|
||||
GameCardHandle handle, GameCardPartitionRaw partitionType)
|
||||
{
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
storage = default;
|
||||
|
||||
return fsProxy.OpenGameCardStorage(out storage, handle, partitionType);
|
||||
ReferenceCountedDisposable<IStorageSf> sfStorage = null;
|
||||
try
|
||||
{
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
Result rc = fsProxy.OpenGameCardStorage(out sfStorage, handle, partitionType);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
storage = new StorageServiceObjectAdapter(sfStorage);
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
sfStorage?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static Result MountGameCardPartition(this FileSystemClient fs, U8Span mountName, GameCardHandle handle,
|
||||
@ -48,12 +81,16 @@ namespace LibHac.Fs.Shim
|
||||
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
rc = fsProxy.OpenGameCardFileSystem(out IFileSystem cardFs, handle, partitionId);
|
||||
rc = fsProxy.OpenGameCardFileSystem(out ReferenceCountedDisposable<IFileSystemSf> cardFs, handle, partitionId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var mountNameGenerator = new GameCardCommonMountNameGenerator(handle, partitionId);
|
||||
using (cardFs)
|
||||
{
|
||||
var mountNameGenerator = new GameCardCommonMountNameGenerator(handle, partitionId);
|
||||
var fileSystemAdapter = new FileSystemServiceObjectAdapter(cardFs);
|
||||
|
||||
return fs.Register(mountName, cardFs, mountNameGenerator);
|
||||
return fs.Register(mountName, fileSystemAdapter, mountNameGenerator);
|
||||
}
|
||||
}
|
||||
|
||||
private class GameCardCommonMountNameGenerator : ICommonMountNameGenerator
|
||||
@ -71,7 +108,7 @@ namespace LibHac.Fs.Shim
|
||||
{
|
||||
char letter = GetGameCardMountNameSuffix(PartitionId);
|
||||
|
||||
string mountName = $"{CommonMountNames.GameCardFileSystemMountName}{letter}{Handle.Value:x8}";
|
||||
string mountName = $"{CommonPaths.GameCardFileSystemMountName}{letter}{Handle.Value:x8}";
|
||||
new U8Span(mountName).Value.CopyTo(nameBuffer);
|
||||
|
||||
return Result.Success;
|
||||
|
@ -2,11 +2,14 @@
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Fs.Impl;
|
||||
using LibHac.FsSrv;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.Util;
|
||||
using static LibHac.Fs.CommonMountNames;
|
||||
using static LibHac.Fs.CommonPaths;
|
||||
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
|
||||
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||
|
||||
namespace LibHac.Fs.Shim
|
||||
{
|
||||
@ -76,12 +79,11 @@ namespace LibHac.Fs.Shim
|
||||
public static Result MountHostRoot(this FileSystemClient fs)
|
||||
{
|
||||
IFileSystem hostFileSystem = default;
|
||||
var path = new FsPath();
|
||||
path.Str[0] = 0;
|
||||
FspPath.CreateEmpty(out FspPath path);
|
||||
|
||||
static string LogMessageGenerator() => $", name: \"{HostRootFileSystemMountName.ToString()}\"";
|
||||
|
||||
Result OpenHostFs() => OpenHostFileSystemImpl(fs, out hostFileSystem, ref path, MountHostOption.None);
|
||||
Result OpenHostFs() => OpenHostFileSystemImpl(fs, out hostFileSystem, in path, MountHostOption.None);
|
||||
|
||||
Result MountHostFs() => fs.Register(HostRootFileSystemMountName, hostFileSystem,
|
||||
new HostRootCommonMountNameGenerator());
|
||||
@ -110,13 +112,12 @@ namespace LibHac.Fs.Shim
|
||||
public static Result MountHostRoot(this FileSystemClient fs, MountHostOption option)
|
||||
{
|
||||
IFileSystem hostFileSystem = default;
|
||||
var path = new FsPath();
|
||||
path.Str[0] = 0;
|
||||
FspPath.CreateEmpty(out FspPath path);
|
||||
|
||||
string LogMessageGenerator() =>
|
||||
$", name: \"{HostRootFileSystemMountName.ToString()}, mount_host_option: {option}\"";
|
||||
|
||||
Result OpenHostFs() => OpenHostFileSystemImpl(fs, out hostFileSystem, ref path, option);
|
||||
Result OpenHostFs() => OpenHostFileSystemImpl(fs, out hostFileSystem, in path, option);
|
||||
|
||||
Result MountHostFs() => fs.Register(HostRootFileSystemMountName, hostFileSystem,
|
||||
new HostRootCommonMountNameGenerator());
|
||||
@ -318,8 +319,7 @@ namespace LibHac.Fs.Shim
|
||||
if (pathLength + 1 > PathTools.MaxPathLength)
|
||||
return ResultFs.TooLongPath.Log();
|
||||
|
||||
FsPath fullPath;
|
||||
unsafe { _ = &fullPath; } // workaround for CS0165
|
||||
Unsafe.SkipInit(out FsPath fullPath);
|
||||
|
||||
var sb = new U8StringBuilder(fullPath.Str);
|
||||
sb.Append(StringTraits.DirectorySeparator).Append(path);
|
||||
@ -341,7 +341,9 @@ namespace LibHac.Fs.Shim
|
||||
}
|
||||
}
|
||||
|
||||
return OpenHostFileSystemImpl(fs, out fileSystem, ref fullPath, option);
|
||||
FspPath.FromSpan(out FspPath sfPath, fullPath.Str);
|
||||
|
||||
return OpenHostFileSystemImpl(fs, out fileSystem, in sfPath, option);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -352,26 +354,34 @@ namespace LibHac.Fs.Shim
|
||||
/// <param name="path">The path on the host computer to open. e.g. /C:\Windows\System32/</param>
|
||||
/// <param name="option">Options for opening the host file system.</param>
|
||||
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||
private static Result OpenHostFileSystemImpl(FileSystemClient fs, out IFileSystem fileSystem, ref FsPath path, MountHostOption option)
|
||||
private static Result OpenHostFileSystemImpl(FileSystemClient fs, out IFileSystem fileSystem, in FspPath path,
|
||||
MountHostOption option)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
IFileSystem hostFs;
|
||||
ReferenceCountedDisposable<IFileSystemSf> hostFs = null;
|
||||
|
||||
if (option == MountHostOption.None)
|
||||
try
|
||||
{
|
||||
Result rc = fsProxy.OpenHostFileSystem(out hostFs, ref path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
else
|
||||
{
|
||||
Result rc = fsProxy.OpenHostFileSystemWithOption(out hostFs, ref path, option);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
if (option == MountHostOption.None)
|
||||
{
|
||||
Result rc = fsProxy.OpenHostFileSystem(out hostFs, in path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
else
|
||||
{
|
||||
Result rc = fsProxy.OpenHostFileSystemWithOption(out hostFs, in path, option);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
fileSystem = hostFs;
|
||||
return Result.Success;
|
||||
fileSystem = new FileSystemServiceObjectAdapter(hostFs);
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
hostFs?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.FsSrv;
|
||||
using LibHac.Sf;
|
||||
|
||||
namespace LibHac.Fs.Shim
|
||||
{
|
||||
@ -21,7 +22,7 @@ namespace LibHac.Fs.Shim
|
||||
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
ReadOnlySpan<byte> mapInfoBuffer = MemoryMarshal.Cast<ProgramIndexMapInfo, byte>(mapInfo);
|
||||
var mapInfoBuffer = new InBuffer(MemoryMarshal.Cast<ProgramIndexMapInfo, byte>(mapInfo));
|
||||
|
||||
return fsProxy.RegisterProgramIndexMapInfo(mapInfoBuffer, mapInfo.Length);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
using LibHac.FsSrv;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Sf;
|
||||
|
||||
namespace LibHac.Fs.Shim
|
||||
{
|
||||
@ -16,8 +17,8 @@ namespace LibHac.Fs.Shim
|
||||
Result rc = registry.SetCurrentProcess(fs.Hos.ProcessId.Value);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return registry.RegisterProgram(processId, programId, storageId, accessControlData,
|
||||
accessControlDescriptor);
|
||||
return registry.RegisterProgram(processId, programId, storageId, new InBuffer(accessControlData),
|
||||
new InBuffer(accessControlDescriptor));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ProgramRegistryImpl.UnregisterProgram"/>
|
||||
|
@ -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)
|
||||
|
@ -1,8 +1,9 @@
|
||||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Fs.Impl;
|
||||
using LibHac.FsSrv;
|
||||
using LibHac.Ncm;
|
||||
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||
|
||||
namespace LibHac.Fs.Shim
|
||||
{
|
||||
@ -115,14 +116,14 @@ namespace LibHac.Fs.Shim
|
||||
if (fs.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||
{
|
||||
TimeSpan startTime = fs.Time.GetCurrent();
|
||||
rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, default, default, SaveDataType.Cache, false, (short)index);
|
||||
rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, default, default, SaveDataType.Cache, false, (ushort)index);
|
||||
TimeSpan endTime = fs.Time.GetCurrent();
|
||||
|
||||
fs.OutputAccessLog(rc, startTime, endTime, $", name: \"{mountName.ToString()}\", index: {index}");
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, default, default, SaveDataType.Cache, false, (short)index);
|
||||
rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, default, default, SaveDataType.Cache, false, (ushort)index);
|
||||
}
|
||||
|
||||
if (rc.IsSuccess() && fs.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||
@ -165,14 +166,14 @@ namespace LibHac.Fs.Shim
|
||||
if (fs.IsEnabledAccessLog(AccessLogTarget.System))
|
||||
{
|
||||
TimeSpan startTime = fs.Time.GetCurrent();
|
||||
rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, applicationId, default, SaveDataType.Cache, false, (short)index);
|
||||
rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, applicationId, default, SaveDataType.Cache, false, (ushort)index);
|
||||
TimeSpan endTime = fs.Time.GetCurrent();
|
||||
|
||||
fs.OutputAccessLog(rc, startTime, endTime, $", name: \"{mountName.ToString()}\", applicationid: 0x{applicationId}, index: {index}");
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, applicationId, default, SaveDataType.Cache, false, (short)index);
|
||||
rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, applicationId, default, SaveDataType.Cache, false, (ushort)index);
|
||||
}
|
||||
|
||||
if (rc.IsSuccess() && fs.IsEnabledAccessLog(AccessLogTarget.System))
|
||||
@ -184,7 +185,7 @@ namespace LibHac.Fs.Shim
|
||||
}
|
||||
|
||||
private static Result MountSaveDataImpl(this FileSystemClient fs, U8Span mountName, SaveDataSpaceId spaceId,
|
||||
ProgramId programId, UserId userId, SaveDataType type, bool openReadOnly, short index)
|
||||
ProgramId programId, UserId userId, SaveDataType type, bool openReadOnly, ushort index)
|
||||
{
|
||||
Result rc = MountHelpers.CheckMountName(mountName);
|
||||
if (rc.IsFailure()) return rc;
|
||||
@ -193,20 +194,29 @@ namespace LibHac.Fs.Shim
|
||||
|
||||
var attribute = new SaveDataAttribute(programId, type, userId, 0, index);
|
||||
|
||||
IFileSystem saveFs;
|
||||
ReferenceCountedDisposable<IFileSystemSf> saveFs = null;
|
||||
|
||||
if (openReadOnly)
|
||||
try
|
||||
{
|
||||
rc = fsProxy.OpenReadOnlySaveDataFileSystem(out saveFs, spaceId, ref attribute);
|
||||
if (openReadOnly)
|
||||
{
|
||||
rc = fsProxy.OpenReadOnlySaveDataFileSystem(out saveFs, spaceId, in attribute);
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = fsProxy.OpenSaveDataFileSystem(out saveFs, spaceId, in attribute);
|
||||
}
|
||||
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var fileSystemAdapter = new FileSystemServiceObjectAdapter(saveFs);
|
||||
|
||||
return fs.Register(mountName, fileSystemAdapter, fileSystemAdapter, null);
|
||||
}
|
||||
else
|
||||
finally
|
||||
{
|
||||
rc = fsProxy.OpenSaveDataFileSystem(out saveFs, spaceId, ref attribute);
|
||||
saveFs?.Dispose();
|
||||
}
|
||||
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return fs.Register(mountName, saveFs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.FsSrv;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Sf;
|
||||
|
||||
namespace LibHac.Fs.Shim
|
||||
{
|
||||
@ -29,13 +30,13 @@ namespace LibHac.Fs.Shim
|
||||
SpaceId = SaveDataSpaceId.User
|
||||
};
|
||||
|
||||
var metaInfo = new SaveMetaCreateInfo
|
||||
var metaInfo = new SaveDataMetaInfo
|
||||
{
|
||||
Type = SaveDataMetaType.Thumbnail,
|
||||
Size = 0x40060
|
||||
};
|
||||
|
||||
return fsProxy.CreateSaveDataFileSystem(ref attribute, ref createInfo, ref metaInfo);
|
||||
return fsProxy.CreateSaveDataFileSystem(in attribute, in createInfo, in metaInfo);
|
||||
},
|
||||
() =>
|
||||
$", applicationid: 0x{applicationId.Value:X}, userid: 0x{userId}, save_data_owner_id: 0x{ownerId:X}, save_data_size: {size}, save_data_journal_size: {journalSize}, save_data_flags: 0x{(int)flags:X8}");
|
||||
@ -61,14 +62,14 @@ namespace LibHac.Fs.Shim
|
||||
SpaceId = SaveDataSpaceId.User
|
||||
};
|
||||
|
||||
var metaInfo = new SaveMetaCreateInfo
|
||||
var metaInfo = new SaveDataMetaInfo
|
||||
{
|
||||
Type = SaveDataMetaType.Thumbnail,
|
||||
Size = 0x40060
|
||||
};
|
||||
|
||||
return fsProxy.CreateSaveDataFileSystemWithHashSalt(ref attribute, ref createInfo, ref metaInfo,
|
||||
ref hashSalt);
|
||||
return fsProxy.CreateSaveDataFileSystemWithHashSalt(in attribute, in createInfo, in metaInfo,
|
||||
in hashSalt);
|
||||
},
|
||||
() =>
|
||||
$", applicationid: 0x{applicationId.Value:X}, userid: 0x{userId}, save_data_owner_id: 0x{ownerId:X}, save_data_size: {size}, save_data_journal_size: {journalSize}, save_data_flags: 0x{(int)flags:X8}");
|
||||
@ -81,7 +82,7 @@ namespace LibHac.Fs.Shim
|
||||
{
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
var attribute = new SaveDataAttribute(applicationId, SaveDataType.Bcat, UserId.Zero, 0);
|
||||
var attribute = new SaveDataAttribute(applicationId, SaveDataType.Bcat, UserId.InvalidId, 0);
|
||||
|
||||
var createInfo = new SaveDataCreationInfo
|
||||
{
|
||||
@ -93,9 +94,9 @@ namespace LibHac.Fs.Shim
|
||||
SpaceId = SaveDataSpaceId.User
|
||||
};
|
||||
|
||||
var metaInfo = new SaveMetaCreateInfo();
|
||||
var metaInfo = new SaveDataMetaInfo();
|
||||
|
||||
return fsProxy.CreateSaveDataFileSystem(ref attribute, ref createInfo, ref metaInfo);
|
||||
return fsProxy.CreateSaveDataFileSystem(in attribute, in createInfo, in metaInfo);
|
||||
},
|
||||
() => $", applicationid: 0x{applicationId.Value:X}, save_data_size: {size}");
|
||||
}
|
||||
@ -108,7 +109,7 @@ namespace LibHac.Fs.Shim
|
||||
{
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
var attribute = new SaveDataAttribute(applicationId, SaveDataType.Device, UserId.Zero, 0);
|
||||
var attribute = new SaveDataAttribute(applicationId, SaveDataType.Device, UserId.InvalidId, 0);
|
||||
|
||||
var createInfo = new SaveDataCreationInfo
|
||||
{
|
||||
@ -120,9 +121,9 @@ namespace LibHac.Fs.Shim
|
||||
SpaceId = SaveDataSpaceId.User
|
||||
};
|
||||
|
||||
var metaInfo = new SaveMetaCreateInfo();
|
||||
var metaInfo = new SaveDataMetaInfo();
|
||||
|
||||
return fsProxy.CreateSaveDataFileSystem(ref attribute, ref createInfo, ref metaInfo);
|
||||
return fsProxy.CreateSaveDataFileSystem(in attribute, in createInfo, in metaInfo);
|
||||
},
|
||||
() => $", applicationid: 0x{applicationId.Value:X}, save_data_owner_id: 0x{ownerId:X}, save_data_size: {size}, save_data_journal_size: {journalSize}, save_data_flags: 0x{(int)flags:X8}");
|
||||
}
|
||||
@ -134,7 +135,7 @@ namespace LibHac.Fs.Shim
|
||||
{
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
var attribute = new SaveDataAttribute(applicationId, SaveDataType.Temporary, UserId.Zero, 0);
|
||||
var attribute = new SaveDataAttribute(applicationId, SaveDataType.Temporary, UserId.InvalidId, 0);
|
||||
|
||||
var createInfo = new SaveDataCreationInfo
|
||||
{
|
||||
@ -145,22 +146,22 @@ namespace LibHac.Fs.Shim
|
||||
SpaceId = SaveDataSpaceId.Temporary
|
||||
};
|
||||
|
||||
var metaInfo = new SaveMetaCreateInfo();
|
||||
var metaInfo = new SaveDataMetaInfo();
|
||||
|
||||
return fsProxy.CreateSaveDataFileSystem(ref attribute, ref createInfo, ref metaInfo);
|
||||
return fsProxy.CreateSaveDataFileSystem(in attribute, in createInfo, in metaInfo);
|
||||
},
|
||||
() => $", applicationid: 0x{applicationId.Value:X}, save_data_owner_id: 0x{ownerId:X}, save_data_size: {size}, save_data_flags: 0x{(int)flags:X8}");
|
||||
}
|
||||
|
||||
public static Result CreateCacheStorage(this FileSystemClient fs, Ncm.ApplicationId applicationId,
|
||||
SaveDataSpaceId spaceId, ulong ownerId, short index, long size, long journalSize, SaveDataFlags flags)
|
||||
SaveDataSpaceId spaceId, ulong ownerId, ushort index, long size, long journalSize, SaveDataFlags flags)
|
||||
{
|
||||
return fs.RunOperationWithAccessLog(AccessLogTarget.System,
|
||||
() =>
|
||||
{
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
var attribute = new SaveDataAttribute(applicationId, SaveDataType.Cache, UserId.Zero, 0, index);
|
||||
var attribute = new SaveDataAttribute(applicationId, SaveDataType.Cache, UserId.InvalidId, 0, index);
|
||||
|
||||
var creationInfo = new SaveDataCreationInfo
|
||||
{
|
||||
@ -172,9 +173,9 @@ namespace LibHac.Fs.Shim
|
||||
SpaceId = spaceId
|
||||
};
|
||||
|
||||
var metaInfo = new SaveMetaCreateInfo();
|
||||
var metaInfo = new SaveDataMetaInfo();
|
||||
|
||||
return fsProxy.CreateSaveDataFileSystem(ref attribute, ref creationInfo, ref metaInfo);
|
||||
return fsProxy.CreateSaveDataFileSystem(in attribute, in creationInfo, in metaInfo);
|
||||
},
|
||||
() => $", applicationid: 0x{applicationId.Value:X}, savedataspaceid: {spaceId}, save_data_owner_id: 0x{ownerId:X}, save_data_size: {size}, save_data_journal_size: {journalSize}, save_data_flags: 0x{(int)flags:X8}");
|
||||
}
|
||||
@ -211,7 +212,7 @@ namespace LibHac.Fs.Shim
|
||||
SpaceId = spaceId
|
||||
};
|
||||
|
||||
return fsProxy.CreateSaveDataFileSystemBySystemSaveDataId(ref attribute, ref createInfo);
|
||||
return fsProxy.CreateSaveDataFileSystemBySystemSaveDataId(in attribute, in createInfo);
|
||||
},
|
||||
() => $", savedataspaceid: {spaceId}, savedataid: 0x{saveDataId:X}, userid: 0x{userId.Id.High:X16}{userId.Id.Low:X16}, save_data_owner_id: 0x{ownerId:X}, save_data_size: {size}, save_data_journal_size: {journalSize}, save_data_flags: 0x{(int)flags:x8}");
|
||||
}
|
||||
@ -231,19 +232,19 @@ namespace LibHac.Fs.Shim
|
||||
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, ulong ownerId, long size,
|
||||
long journalSize, SaveDataFlags flags)
|
||||
{
|
||||
return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, UserId.Zero, ownerId, size, journalSize, flags);
|
||||
return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, UserId.InvalidId, ownerId, size, journalSize, flags);
|
||||
}
|
||||
|
||||
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, long size,
|
||||
long journalSize, SaveDataFlags flags)
|
||||
{
|
||||
return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, UserId.Zero, 0, size, journalSize, flags);
|
||||
return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, UserId.InvalidId, 0, size, journalSize, flags);
|
||||
}
|
||||
|
||||
public static Result CreateSystemSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId,
|
||||
ulong ownerId, long size, long journalSize, SaveDataFlags flags)
|
||||
{
|
||||
return CreateSystemSaveData(fs, spaceId, saveDataId, UserId.Zero, ownerId, size, journalSize, flags);
|
||||
return CreateSystemSaveData(fs, spaceId, saveDataId, UserId.InvalidId, ownerId, size, journalSize, flags);
|
||||
}
|
||||
|
||||
public static Result DeleteSaveData(this FileSystemClient fs, ulong saveDataId)
|
||||
@ -283,8 +284,8 @@ namespace LibHac.Fs.Shim
|
||||
|
||||
tempInfo = new SaveDataInfo();
|
||||
|
||||
Result rc = fsProxy.FindSaveDataWithFilter(out long count, SpanHelpers.AsByteSpan(ref tempInfo),
|
||||
spaceId, ref tempFilter);
|
||||
Result rc = fsProxy.FindSaveDataWithFilter(out long count, OutBuffer.FromStruct(ref tempInfo),
|
||||
spaceId, in tempFilter);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (count == 0)
|
||||
@ -329,49 +330,64 @@ namespace LibHac.Fs.Shim
|
||||
{
|
||||
var tempIterator = new SaveDataIterator();
|
||||
|
||||
Result result = fs.RunOperationWithAccessLog(AccessLogTarget.System,
|
||||
() =>
|
||||
{
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
try
|
||||
{
|
||||
Result result = fs.RunOperationWithAccessLog(AccessLogTarget.System,
|
||||
() =>
|
||||
{
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
Result rc = fsProxy.OpenSaveDataInfoReaderBySaveDataSpaceId(
|
||||
out ReferenceCountedDisposable<ISaveDataInfoReader> reader, spaceId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
Result rc = fsProxy.OpenSaveDataInfoReaderBySaveDataSpaceId(
|
||||
out ReferenceCountedDisposable<ISaveDataInfoReader> reader, spaceId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
tempIterator = new SaveDataIterator(fs, reader);
|
||||
tempIterator = new SaveDataIterator(fs, reader);
|
||||
|
||||
return Result.Success;
|
||||
},
|
||||
() => $", savedataspaceid: {spaceId}");
|
||||
return Result.Success;
|
||||
},
|
||||
() => $", savedataspaceid: {spaceId}");
|
||||
|
||||
iterator = result.IsSuccess() ? tempIterator : default;
|
||||
iterator = result.IsSuccess() ? tempIterator : default;
|
||||
tempIterator = default;
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
tempIterator.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static Result OpenSaveDataIterator(this FileSystemClient fs, out SaveDataIterator iterator, SaveDataSpaceId spaceId, ref SaveDataFilter filter)
|
||||
{
|
||||
ReferenceCountedDisposable<ISaveDataInfoReader> reader = null;
|
||||
var tempIterator = new SaveDataIterator();
|
||||
SaveDataFilter tempFilter = filter;
|
||||
|
||||
Result result = fs.RunOperationWithAccessLog(AccessLogTarget.System,
|
||||
() =>
|
||||
{
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
try
|
||||
{
|
||||
Result result = fs.RunOperationWithAccessLog(AccessLogTarget.System,
|
||||
() =>
|
||||
{
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
Result rc = fsProxy.OpenSaveDataInfoReaderWithFilter(
|
||||
out ReferenceCountedDisposable<ISaveDataInfoReader> reader, spaceId, ref tempFilter);
|
||||
if (rc.IsFailure()) return rc;
|
||||
Result rc = fsProxy.OpenSaveDataInfoReaderWithFilter(out reader, spaceId, in tempFilter);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
tempIterator = new SaveDataIterator(fs, reader);
|
||||
tempIterator = new SaveDataIterator(fs, reader);
|
||||
|
||||
return Result.Success;
|
||||
},
|
||||
() => $", savedataspaceid: {spaceId}");
|
||||
return Result.Success;
|
||||
},
|
||||
() => $", savedataspaceid: {spaceId}");
|
||||
|
||||
iterator = result.IsSuccess() ? tempIterator : default;
|
||||
iterator = result.IsSuccess() ? tempIterator : default;
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
reader?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public static void DisableAutoSaveDataCreation(this FileSystemClient fsClient)
|
||||
@ -395,14 +411,14 @@ namespace LibHac.Fs.Shim
|
||||
internal SaveDataIterator(FileSystemClient fsClient, ReferenceCountedDisposable<ISaveDataInfoReader> reader)
|
||||
{
|
||||
FsClient = fsClient;
|
||||
Reader = reader;
|
||||
Reader = reader.AddReference();
|
||||
}
|
||||
|
||||
public Result ReadSaveDataInfo(out long readCount, Span<SaveDataInfo> buffer)
|
||||
{
|
||||
Result rc;
|
||||
|
||||
Span<byte> byteBuffer = MemoryMarshal.Cast<SaveDataInfo, byte>(buffer);
|
||||
var byteBuffer = new OutBuffer(MemoryMarshal.Cast<SaveDataInfo, byte>(buffer));
|
||||
|
||||
if (FsClient.IsEnabledAccessLog(AccessLogTarget.System))
|
||||
{
|
||||
|
@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Fs.Impl;
|
||||
using LibHac.FsSrv;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||
|
||||
namespace LibHac.Fs.Shim
|
||||
{
|
||||
@ -41,31 +43,44 @@ namespace LibHac.Fs.Shim
|
||||
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
rc = fsProxy.OpenSdCardFileSystem(out IFileSystem fileSystem);
|
||||
rc = fsProxy.OpenSdCardFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return fs.Register(mountName, fileSystem);
|
||||
using (fileSystem)
|
||||
{
|
||||
var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem);
|
||||
|
||||
return fs.Register(mountName, fileSystemAdapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsSdCardInserted(this FileSystemClient fs)
|
||||
{
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
ReferenceCountedDisposable<IDeviceOperator> deviceOperator = null;
|
||||
try
|
||||
{
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
|
||||
Result rc = fsProxy.OpenDeviceOperator(out IDeviceOperator deviceOperator);
|
||||
if (rc.IsFailure()) throw new HorizonResultException(rc, "Abort");
|
||||
Result rc = fsProxy.OpenDeviceOperator(out deviceOperator);
|
||||
if (rc.IsFailure()) throw new HorizonResultException(rc, "Abort");
|
||||
|
||||
rc = deviceOperator.IsSdCardInserted(out bool isInserted);
|
||||
if (rc.IsFailure()) throw new HorizonResultException(rc, "Abort");
|
||||
rc = deviceOperator.Target.IsSdCardInserted(out bool isInserted);
|
||||
if (rc.IsFailure()) throw new HorizonResultException(rc, "Abort");
|
||||
|
||||
return isInserted;
|
||||
return isInserted;
|
||||
}
|
||||
finally
|
||||
{
|
||||
deviceOperator?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -1,15 +1,17 @@
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Fs.Impl;
|
||||
using LibHac.FsSrv;
|
||||
using LibHac.Ncm;
|
||||
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||
|
||||
namespace LibHac.Fs.Shim
|
||||
{
|
||||
public static class SystemSaveData
|
||||
{
|
||||
public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName, SaveDataSpaceId spaceId, ulong saveDataId)
|
||||
public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName, SaveDataSpaceId spaceId,
|
||||
ulong saveDataId)
|
||||
{
|
||||
return MountSystemSaveData(fs, mountName, spaceId, saveDataId, UserId.Zero);
|
||||
return MountSystemSaveData(fs, mountName, spaceId, saveDataId, UserId.InvalidId);
|
||||
}
|
||||
|
||||
public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName,
|
||||
@ -22,10 +24,20 @@ namespace LibHac.Fs.Shim
|
||||
|
||||
var attribute = new SaveDataAttribute(ProgramId.InvalidId, SaveDataType.System, userId, saveDataId);
|
||||
|
||||
rc = fsProxy.OpenSaveDataFileSystemBySystemSaveDataId(out IFileSystem fileSystem, spaceId, ref attribute);
|
||||
if (rc.IsFailure()) return rc;
|
||||
ReferenceCountedDisposable<IFileSystemSf> saveFs = null;
|
||||
|
||||
return fs.Register(mountName, fileSystem);
|
||||
try
|
||||
{
|
||||
rc = fsProxy.OpenSaveDataFileSystemBySystemSaveDataId(out saveFs, spaceId, in attribute);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var fileSystemAdapter = new FileSystemServiceObjectAdapter(saveFs);
|
||||
return fs.Register(mountName, fileSystemAdapter);
|
||||
}
|
||||
finally
|
||||
{
|
||||
saveFs?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
53
src/LibHac/Fs/Shim/UserFileSystem.cs
Normal file
53
src/LibHac/Fs/Shim/UserFileSystem.cs
Normal file
@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs.Accessors;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||
|
||||
namespace LibHac.Fs.Shim
|
||||
{
|
||||
public static class UserFileSystem
|
||||
{
|
||||
public static Result Commit(this FileSystemClient fs, ReadOnlySpan<U8String> mountNames)
|
||||
{
|
||||
// Todo: Add access log
|
||||
|
||||
if (mountNames.Length > 10)
|
||||
return ResultFs.InvalidCommitNameCount.Log();
|
||||
|
||||
if (mountNames.Length == 0)
|
||||
return Result.Success;
|
||||
|
||||
ReferenceCountedDisposable<IMultiCommitManager> commitManager = null;
|
||||
ReferenceCountedDisposable<IFileSystemSf> fileSystem = null;
|
||||
try
|
||||
{
|
||||
Result rc = fs.GetFileSystemProxyServiceObject().OpenMultiCommitManager(out commitManager);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
for (int i = 0; i < mountNames.Length; i++)
|
||||
{
|
||||
rc = fs.MountTable.Find(mountNames[i].ToString(), out FileSystemAccessor accessor);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
fileSystem = accessor.GetMultiCommitTarget();
|
||||
if (fileSystem is null)
|
||||
return ResultFs.UnsupportedCommitTarget.Log();
|
||||
|
||||
rc = commitManager.Target.Add(fileSystem);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
rc = commitManager.Target.Commit();
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
commitManager?.Dispose();
|
||||
fileSystem?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@ namespace LibHac.Fs
|
||||
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
|
||||
public struct UserId : IEquatable<UserId>, IComparable<UserId>, IComparable
|
||||
{
|
||||
public static UserId Zero => default;
|
||||
public static UserId InvalidId => default;
|
||||
|
||||
public readonly Id128 Id;
|
||||
|
||||
|
66
src/LibHac/FsSrv/AccessLogService.cs
Normal file
66
src/LibHac/FsSrv/AccessLogService.cs
Normal file
@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using LibHac.Fs;
|
||||
using LibHac.FsSrv.Impl;
|
||||
using LibHac.Sf;
|
||||
|
||||
namespace LibHac.FsSrv
|
||||
{
|
||||
internal readonly struct AccessLogService
|
||||
{
|
||||
private readonly AccessLogServiceImpl _serviceImpl;
|
||||
private readonly ulong _processId;
|
||||
|
||||
public AccessLogService(AccessLogServiceImpl serviceImpl, ulong processId)
|
||||
{
|
||||
_serviceImpl = serviceImpl;
|
||||
_processId = processId;
|
||||
}
|
||||
|
||||
public Result SetAccessLogMode(GlobalAccessLogMode mode)
|
||||
{
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (!programInfo.AccessControl.CanCall(OperationType.SetGlobalAccessLogMode))
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
_serviceImpl.SetAccessLogMode(mode);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result GetAccessLogMode(out GlobalAccessLogMode mode)
|
||||
{
|
||||
mode = _serviceImpl.GetAccessLogMode();
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result OutputAccessLogToSdCard(InBuffer textBuffer)
|
||||
{
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
return _serviceImpl.OutputAccessLogToSdCard(textBuffer.Buffer, programInfo.ProgramIdValue, _processId);
|
||||
}
|
||||
|
||||
public Result OutputMultiProgramTagAccessLog()
|
||||
{
|
||||
_serviceImpl.OutputAccessLogToSdCard(MultiProgramTag, _processId).IgnoreResult();
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private Result GetProgramInfo(out ProgramInfo programInfo)
|
||||
{
|
||||
return _serviceImpl.GetProgramInfo(out programInfo, _processId);
|
||||
}
|
||||
|
||||
private static ReadOnlySpan<byte> MultiProgramTag => // FS_ACCESS: { multi_program_tag: true }\n
|
||||
new[]
|
||||
{
|
||||
(byte) 'F', (byte) 'S', (byte) '_', (byte) 'A', (byte) 'C', (byte) 'C', (byte) 'E', (byte) 'S',
|
||||
(byte) 'S', (byte) ':', (byte) ' ', (byte) '{', (byte) ' ', (byte) 'm', (byte) 'u', (byte) 'l',
|
||||
(byte) 't', (byte) 'i', (byte) '_', (byte) 'p', (byte) 'r', (byte) 'o', (byte) 'g', (byte) 'r',
|
||||
(byte) 'a', (byte) 'm', (byte) '_', (byte) 't', (byte) 'a', (byte) 'g', (byte) ':', (byte) ' ',
|
||||
(byte) 't', (byte) 'r', (byte) 'u', (byte) 'e', (byte) ' ', (byte) '}', (byte) '\n'
|
||||
};
|
||||
}
|
||||
}
|
56
src/LibHac/FsSrv/AccessLogServiceImpl.cs
Normal file
56
src/LibHac/FsSrv/AccessLogServiceImpl.cs
Normal file
@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using LibHac.Fs;
|
||||
using LibHac.FsSrv.Impl;
|
||||
|
||||
namespace LibHac.FsSrv
|
||||
{
|
||||
public class AccessLogServiceImpl : IDisposable
|
||||
{
|
||||
private Configuration _config;
|
||||
private GlobalAccessLogMode _accessLogMode;
|
||||
|
||||
public AccessLogServiceImpl(in Configuration configuration)
|
||||
{
|
||||
_config = configuration;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public struct Configuration
|
||||
{
|
||||
public ulong MinimumProgramIdForSdCardLog;
|
||||
|
||||
// LibHac additions
|
||||
public HorizonClient HorizonClient;
|
||||
public ProgramRegistryImpl ProgramRegistry;
|
||||
}
|
||||
|
||||
public void SetAccessLogMode(GlobalAccessLogMode mode)
|
||||
{
|
||||
_accessLogMode = mode;
|
||||
}
|
||||
|
||||
public GlobalAccessLogMode GetAccessLogMode()
|
||||
{
|
||||
return _accessLogMode;
|
||||
}
|
||||
|
||||
public Result OutputAccessLogToSdCard(ReadOnlySpan<byte> text, ulong processId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OutputAccessLogToSdCard(ReadOnlySpan<byte> text, ulong programId, ulong processId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal Result GetProgramInfo(out ProgramInfo programInfo, ulong processId)
|
||||
{
|
||||
return _config.ProgramRegistry.GetProgramInfo(out programInfo, processId);
|
||||
}
|
||||
}
|
||||
}
|
257
src/LibHac/FsSrv/BaseFileSystemService.cs
Normal file
257
src/LibHac/FsSrv/BaseFileSystemService.cs
Normal file
@ -0,0 +1,257 @@
|
||||
using LibHac.Fs;
|
||||
using LibHac.FsSrv.Impl;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.Sf;
|
||||
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
|
||||
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||
|
||||
namespace LibHac.FsSrv
|
||||
{
|
||||
public readonly struct BaseFileSystemService
|
||||
{
|
||||
private readonly BaseFileSystemServiceImpl _serviceImpl;
|
||||
private readonly ulong _processId;
|
||||
|
||||
public BaseFileSystemService(BaseFileSystemServiceImpl serviceImpl, ulong processId)
|
||||
{
|
||||
_serviceImpl = serviceImpl;
|
||||
_processId = processId;
|
||||
}
|
||||
|
||||
public Result OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, in FspPath rootPath,
|
||||
BisPartitionId partitionId)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Get the permissions the caller needs
|
||||
AccessibilityType requiredAccess = partitionId switch
|
||||
{
|
||||
BisPartitionId.CalibrationFile => AccessibilityType.MountBisCalibrationFile,
|
||||
BisPartitionId.SafeMode => AccessibilityType.MountBisSafeMode,
|
||||
BisPartitionId.User => AccessibilityType.MountBisUser,
|
||||
BisPartitionId.System => AccessibilityType.MountBisSystem,
|
||||
BisPartitionId.SystemProperPartition => AccessibilityType.MountBisSystemProperPartition,
|
||||
_ => AccessibilityType.NotMount
|
||||
};
|
||||
|
||||
// Reject opening invalid partitions
|
||||
if (requiredAccess == AccessibilityType.NotMount)
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
|
||||
// Verify the caller has the required permissions
|
||||
Accessibility accessibility = programInfo.AccessControl.GetAccessibilityFor(requiredAccess);
|
||||
|
||||
if (!accessibility.CanRead || !accessibility.CanWrite)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
// Normalize the path
|
||||
var normalizer = new PathNormalizer(rootPath, PathNormalizer.Option.AcceptEmpty);
|
||||
if (normalizer.Result.IsFailure()) return normalizer.Result;
|
||||
|
||||
ReferenceCountedDisposable<IFileSystem> fs = null;
|
||||
|
||||
try
|
||||
{
|
||||
// Open the file system
|
||||
rc = _serviceImpl.OpenBisFileSystem(out fs, normalizer.Path,
|
||||
partitionId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Create an SF adapter for the file system
|
||||
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref fs);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
fs?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public Result CreatePaddingFile(long size)
|
||||
{
|
||||
// File size must be non-negative
|
||||
if (size < 0)
|
||||
return ResultFs.InvalidSize.Log();
|
||||
|
||||
// Caller must have the FillBis permission
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (!programInfo.AccessControl.CanCall(OperationType.FillBis))
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
return _serviceImpl.CreatePaddingFile(size);
|
||||
}
|
||||
|
||||
public Result DeleteAllPaddingFiles()
|
||||
{
|
||||
// Caller must have the FillBis permission
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (!programInfo.AccessControl.CanCall(OperationType.FillBis))
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
return _serviceImpl.DeleteAllPaddingFiles();
|
||||
}
|
||||
|
||||
public Result OpenGameCardFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, GameCardHandle handle,
|
||||
GameCardPartition partitionId)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (!programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountGameCard).CanRead)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
ReferenceCountedDisposable<IFileSystem> fs = null;
|
||||
|
||||
try
|
||||
{
|
||||
rc = _serviceImpl.OpenGameCardFileSystem(out fs, handle, partitionId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Create an SF adapter for the file system
|
||||
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref fs);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
fs?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public Result OpenSdCardFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
Accessibility accessibility = programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountSdCard);
|
||||
|
||||
if (!accessibility.CanRead || !accessibility.CanWrite)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
ReferenceCountedDisposable<IFileSystem> fs = null;
|
||||
|
||||
try
|
||||
{
|
||||
rc = _serviceImpl.OpenSdCardProxyFileSystem(out fs);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Create an SF adapter for the file system
|
||||
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref fs);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
fs?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public Result FormatSdCardFileSystem()
|
||||
{
|
||||
// Caller must have the FormatSdCard permission
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (!programInfo.AccessControl.CanCall(OperationType.FormatSdCard))
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
return _serviceImpl.FormatSdCardProxyFileSystem();
|
||||
}
|
||||
|
||||
public Result FormatSdCardDryRun()
|
||||
{
|
||||
// No permissions are needed to call this method
|
||||
|
||||
return _serviceImpl.FormatSdCardProxyFileSystem();
|
||||
}
|
||||
|
||||
public Result IsExFatSupported(out bool isSupported)
|
||||
{
|
||||
// No permissions are needed to call this method
|
||||
|
||||
isSupported = _serviceImpl.IsExFatSupported();
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result OpenImageDirectoryFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem,
|
||||
ImageDirectoryId directoryId)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
// Caller must have the MountImageAndVideoStorage permission
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
Accessibility accessibility =
|
||||
programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountImageAndVideoStorage);
|
||||
|
||||
if (!accessibility.CanRead || !accessibility.CanWrite)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
// Get the base file system ID
|
||||
int id;
|
||||
switch (directoryId)
|
||||
{
|
||||
case ImageDirectoryId.Nand:
|
||||
id = 0;
|
||||
break;
|
||||
case ImageDirectoryId.SdCard:
|
||||
id = 1;
|
||||
break;
|
||||
default:
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
}
|
||||
ReferenceCountedDisposable<IFileSystem> fs = null;
|
||||
|
||||
try
|
||||
{
|
||||
rc = _serviceImpl.OpenBaseFileSystem(out fs, id);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Create an SF adapter for the file system
|
||||
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref fs);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
fs?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public Result OpenBisWiper(out ReferenceCountedDisposable<IWiper> bisWiper, NativeHandle transferMemoryHandle, ulong transferMemorySize)
|
||||
{
|
||||
bisWiper = default;
|
||||
|
||||
// Caller must have the OpenBisWiper permission
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (!programInfo.AccessControl.CanCall(OperationType.OpenBisWiper))
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
rc = _serviceImpl.OpenBisWiper(out IWiper wiper, transferMemoryHandle, transferMemorySize);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
bisWiper = new ReferenceCountedDisposable<IWiper>(wiper);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private Result GetProgramInfo(out ProgramInfo programInfo)
|
||||
{
|
||||
return _serviceImpl.GetProgramInfo(out programInfo, _processId);
|
||||
}
|
||||
}
|
||||
}
|
133
src/LibHac/FsSrv/BaseFileSystemServiceImpl.cs
Normal file
133
src/LibHac/FsSrv/BaseFileSystemServiceImpl.cs
Normal file
@ -0,0 +1,133 @@
|
||||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.FsSrv.Creators;
|
||||
using LibHac.FsSrv.Impl;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.Sf;
|
||||
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
|
||||
|
||||
namespace LibHac.FsSrv
|
||||
{
|
||||
public class BaseFileSystemServiceImpl
|
||||
{
|
||||
private Configuration _config;
|
||||
|
||||
public delegate Result BisWiperCreator(out IWiper wiper, NativeHandle transferMemoryHandle,
|
||||
ulong transferMemorySize);
|
||||
|
||||
public BaseFileSystemServiceImpl(in Configuration configuration)
|
||||
{
|
||||
_config = configuration;
|
||||
}
|
||||
|
||||
public struct Configuration
|
||||
{
|
||||
public IBuiltInStorageFileSystemCreator BisFileSystemCreator;
|
||||
public IGameCardFileSystemCreator GameCardFileSystemCreator;
|
||||
public ISdCardProxyFileSystemCreator SdCardFileSystemCreator;
|
||||
// CurrentTimeFunction
|
||||
// FatFileSystemCacheManager
|
||||
// AlbumDirectoryFileSystemManager
|
||||
public BisWiperCreator BisWiperCreator;
|
||||
|
||||
// Note: The program registry service is global as of FS 10.0.0
|
||||
public ProgramRegistryImpl ProgramRegistry;
|
||||
}
|
||||
|
||||
public Result OpenBaseFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, int fileSystemId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, U8Span rootPath,
|
||||
BisPartitionId partitionId)
|
||||
{
|
||||
return OpenBisFileSystem(out fileSystem, rootPath, partitionId, false);
|
||||
}
|
||||
|
||||
public Result OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, U8Span rootPath,
|
||||
BisPartitionId partitionId, bool caseSensitive)
|
||||
{
|
||||
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)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result DeleteAllPaddingFiles()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenGameCardFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, GameCardHandle handle,
|
||||
GameCardPartition partitionId)
|
||||
{
|
||||
Result rc;
|
||||
int tries = 0;
|
||||
|
||||
do
|
||||
{
|
||||
rc = _config.GameCardFileSystemCreator.Create(out fileSystem, handle, partitionId);
|
||||
|
||||
if (!ResultFs.DataCorrupted.Includes(rc))
|
||||
break;
|
||||
|
||||
tries++;
|
||||
} while (tries < 2);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
public Result OpenSdCardProxyFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem)
|
||||
{
|
||||
return OpenSdCardProxyFileSystem(out fileSystem, false);
|
||||
}
|
||||
|
||||
public Result OpenSdCardProxyFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, bool openCaseSensitive)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
// Todo: Shared
|
||||
Result rc = _config.SdCardFileSystemCreator.Create(out IFileSystem fs, openCaseSensitive);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
fileSystem = new ReferenceCountedDisposable<IFileSystem>(fs);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result FormatSdCardProxyFileSystem()
|
||||
{
|
||||
return _config.SdCardFileSystemCreator.Format();
|
||||
}
|
||||
|
||||
public Result FormatSdCardDryRun()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool IsExFatSupported()
|
||||
{
|
||||
// Returning false should probably be fine
|
||||
return false;
|
||||
}
|
||||
|
||||
public Result OpenBisWiper(out IWiper wiper, NativeHandle transferMemoryHandle, ulong transferMemorySize)
|
||||
{
|
||||
return _config.BisWiperCreator(out wiper, transferMemoryHandle, transferMemorySize);
|
||||
}
|
||||
|
||||
internal Result GetProgramInfo(out ProgramInfo programInfo, ulong processId)
|
||||
{
|
||||
return _config.ProgramRegistry.GetProgramInfo(out programInfo, processId);
|
||||
}
|
||||
}
|
||||
}
|
240
src/LibHac/FsSrv/BaseStorageService.cs
Normal file
240
src/LibHac/FsSrv/BaseStorageService.cs
Normal file
@ -0,0 +1,240 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Fs;
|
||||
using LibHac.FsSrv.Creators;
|
||||
using LibHac.FsSrv.Impl;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.FsSystem;
|
||||
using IStorage = LibHac.Fs.IStorage;
|
||||
using IStorageSf = LibHac.FsSrv.Sf.IStorage;
|
||||
|
||||
namespace LibHac.FsSrv
|
||||
{
|
||||
public readonly struct BaseStorageService
|
||||
{
|
||||
private readonly BaseStorageServiceImpl _serviceImpl;
|
||||
private readonly ulong _processId;
|
||||
|
||||
public BaseStorageService(BaseStorageServiceImpl serviceImpl, ulong processId)
|
||||
{
|
||||
_serviceImpl = serviceImpl;
|
||||
_processId = processId;
|
||||
}
|
||||
|
||||
public Result OpenBisStorage(out ReferenceCountedDisposable<IStorageSf> storage, BisPartitionId id)
|
||||
{
|
||||
storage = default;
|
||||
|
||||
var storageFlag = StorageType.Bis;
|
||||
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(storageFlag);
|
||||
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = GetAccessibilityForOpenBisPartition(out Accessibility accessibility, programInfo, id);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
bool canAccess = accessibility.CanRead && accessibility.CanWrite;
|
||||
|
||||
if (!canAccess)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
ReferenceCountedDisposable<IStorage> tempStorage = null;
|
||||
try
|
||||
{
|
||||
rc = _serviceImpl.OpenBisStorage(out tempStorage, id);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
tempStorage = StorageLayoutTypeSetStorage.CreateShared(ref tempStorage, storageFlag);
|
||||
|
||||
// Todo: Async storage
|
||||
|
||||
storage = StorageInterfaceAdapter.CreateShared(ref tempStorage);
|
||||
return Result.Success;
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
tempStorage?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public Result InvalidateBisCache()
|
||||
{
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (!programInfo.AccessControl.CanCall(OperationType.InvalidateBisCache))
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
return _serviceImpl.InvalidateBisCache();
|
||||
}
|
||||
|
||||
public Result OpenGameCardStorage(out ReferenceCountedDisposable<IStorageSf> storage, GameCardHandle handle,
|
||||
GameCardPartitionRaw partitionId)
|
||||
{
|
||||
storage = default;
|
||||
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
Accessibility accessibility =
|
||||
programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.OpenGameCardStorage);
|
||||
|
||||
bool canAccess = accessibility.CanRead && accessibility.CanWrite;
|
||||
|
||||
if (!canAccess)
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
ReferenceCountedDisposable<IStorage> tempStorage = null;
|
||||
try
|
||||
{
|
||||
rc = _serviceImpl.OpenGameCardPartition(out tempStorage, handle, partitionId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Todo: Async storage
|
||||
|
||||
storage = StorageInterfaceAdapter.CreateShared(ref tempStorage);
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
tempStorage?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public Result OpenDeviceOperator(out ReferenceCountedDisposable<IDeviceOperator> deviceOperator)
|
||||
{
|
||||
deviceOperator = _serviceImpl.Config.DeviceOperator.AddReference();
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result OpenSdCardDetectionEventNotifier(out ReferenceCountedDisposable<IEventNotifier> eventNotifier)
|
||||
{
|
||||
eventNotifier = default;
|
||||
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (!programInfo.AccessControl.CanCall(OperationType.OpenSdCardDetectionEventNotifier))
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenGameCardDetectionEventNotifier(out ReferenceCountedDisposable<IEventNotifier> eventNotifier)
|
||||
{
|
||||
eventNotifier = default;
|
||||
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (!programInfo.AccessControl.CanCall(OperationType.OpenGameCardDetectionEventNotifier))
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result SimulateDeviceDetectionEvent(SdmmcPort port, SimulatingDeviceDetectionMode mode, bool signalEvent)
|
||||
{
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (!programInfo.AccessControl.CanCall(OperationType.SimulateDevice))
|
||||
return ResultFs.PermissionDenied.Log();
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private Result GetProgramInfo(out ProgramInfo programInfo)
|
||||
{
|
||||
return _serviceImpl.GetProgramInfo(out programInfo, _processId);
|
||||
}
|
||||
|
||||
private Result GetAccessibilityForOpenBisPartition(out Accessibility accessibility, ProgramInfo programInfo,
|
||||
BisPartitionId partitionId)
|
||||
{
|
||||
Unsafe.SkipInit(out accessibility);
|
||||
|
||||
AccessibilityType type = partitionId switch
|
||||
{
|
||||
BisPartitionId.BootPartition1Root => AccessibilityType.OpenBisPartitionBootPartition1Root,
|
||||
BisPartitionId.BootPartition2Root => AccessibilityType.OpenBisPartitionBootPartition2Root,
|
||||
BisPartitionId.UserDataRoot => AccessibilityType.OpenBisPartitionUserDataRoot,
|
||||
BisPartitionId.BootConfigAndPackage2Part1 => AccessibilityType.OpenBisPartitionBootConfigAndPackage2Part1,
|
||||
BisPartitionId.BootConfigAndPackage2Part2 => AccessibilityType.OpenBisPartitionBootConfigAndPackage2Part2,
|
||||
BisPartitionId.BootConfigAndPackage2Part3 => AccessibilityType.OpenBisPartitionBootConfigAndPackage2Part3,
|
||||
BisPartitionId.BootConfigAndPackage2Part4 => AccessibilityType.OpenBisPartitionBootConfigAndPackage2Part4,
|
||||
BisPartitionId.BootConfigAndPackage2Part5 => AccessibilityType.OpenBisPartitionBootConfigAndPackage2Part5,
|
||||
BisPartitionId.BootConfigAndPackage2Part6 => AccessibilityType.OpenBisPartitionBootConfigAndPackage2Part6,
|
||||
BisPartitionId.CalibrationBinary => AccessibilityType.OpenBisPartitionCalibrationBinary,
|
||||
BisPartitionId.CalibrationFile => AccessibilityType.OpenBisPartitionCalibrationFile,
|
||||
BisPartitionId.SafeMode => AccessibilityType.OpenBisPartitionSafeMode,
|
||||
BisPartitionId.User => AccessibilityType.OpenBisPartitionUser,
|
||||
BisPartitionId.System => AccessibilityType.OpenBisPartitionSystem,
|
||||
BisPartitionId.SystemProperEncryption => AccessibilityType.OpenBisPartitionSystemProperEncryption,
|
||||
BisPartitionId.SystemProperPartition => AccessibilityType.OpenBisPartitionSystemProperPartition,
|
||||
_ => (AccessibilityType)(-1)
|
||||
};
|
||||
|
||||
if (type == (AccessibilityType)(-1))
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
|
||||
accessibility = programInfo.AccessControl.GetAccessibilityFor(type);
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
public class BaseStorageServiceImpl
|
||||
{
|
||||
internal Configuration Config;
|
||||
|
||||
public BaseStorageServiceImpl(in Configuration configuration)
|
||||
{
|
||||
Config = configuration;
|
||||
}
|
||||
|
||||
public struct Configuration
|
||||
{
|
||||
public IBuiltInStorageCreator BisStorageCreator;
|
||||
public IGameCardStorageCreator GameCardStorageCreator;
|
||||
|
||||
// LibHac additions
|
||||
public ProgramRegistryImpl ProgramRegistry;
|
||||
// Todo: The DeviceOperator in FS uses mostly global state. Decide how to handle this.
|
||||
public ReferenceCountedDisposable<IDeviceOperator> DeviceOperator;
|
||||
}
|
||||
|
||||
public Result OpenBisStorage(out ReferenceCountedDisposable<IStorage> storage, BisPartitionId partitionId)
|
||||
{
|
||||
return Config.BisStorageCreator.Create(out storage, partitionId);
|
||||
}
|
||||
|
||||
public Result InvalidateBisCache()
|
||||
{
|
||||
return Config.BisStorageCreator.InvalidateCache();
|
||||
}
|
||||
|
||||
public Result OpenGameCardPartition(out ReferenceCountedDisposable<IStorage> storage, GameCardHandle handle,
|
||||
GameCardPartitionRaw partitionId)
|
||||
{
|
||||
switch (partitionId)
|
||||
{
|
||||
case GameCardPartitionRaw.NormalReadOnly:
|
||||
return Config.GameCardStorageCreator.CreateReadOnly(handle, out storage);
|
||||
case GameCardPartitionRaw.SecureReadOnly:
|
||||
return Config.GameCardStorageCreator.CreateSecureReadOnly(handle, out storage);
|
||||
case GameCardPartitionRaw.RootWriteOnly:
|
||||
return Config.GameCardStorageCreator.CreateWriteOnly(handle, out storage);
|
||||
default:
|
||||
storage = default;
|
||||
return ResultFs.InvalidArgument.Log();
|
||||
}
|
||||
}
|
||||
|
||||
internal Result GetProgramInfo(out ProgramInfo programInfo, ulong processId)
|
||||
{
|
||||
return Config.ProgramRegistry.GetProgramInfo(out programInfo, processId);
|
||||
}
|
||||
}
|
||||
}
|
@ -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,53 @@ namespace LibHac.FsSrv.Creators
|
||||
return Util.CreateSubFileSystemImpl(out fileSystem, subFileSystem, rootPath);
|
||||
}
|
||||
|
||||
// Todo: Make case sensitive
|
||||
public Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, U8Span rootPath,
|
||||
BisPartitionId partitionId, bool caseSensitive)
|
||||
{
|
||||
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();
|
||||
|
||||
ReferenceCountedDisposable<IFileSystem> partitionFileSystem = null;
|
||||
ReferenceCountedDisposable<IFileSystem> sharedRootFs = null;
|
||||
try
|
||||
{
|
||||
sharedRootFs = new ReferenceCountedDisposable<IFileSystem>(Config.RootFileSystem);
|
||||
|
||||
Result rc = Utility.WrapSubDirectory(out partitionFileSystem, ref sharedRootFs, partitionPath, true);
|
||||
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (rootPath.IsEmpty())
|
||||
{
|
||||
Shared.Move(out fileSystem, ref partitionFileSystem);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
return Utility.CreateSubDirectoryFileSystem(out fileSystem, ref partitionFileSystem, rootPath);
|
||||
}
|
||||
finally
|
||||
{
|
||||
partitionFileSystem?.Dispose();
|
||||
sharedRootFs?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ namespace LibHac.FsSrv.Creators
|
||||
GameCard = gameCard;
|
||||
}
|
||||
|
||||
public Result CreateNormal(GameCardHandle handle, out IStorage storage)
|
||||
public Result CreateReadOnly(GameCardHandle handle, out ReferenceCountedDisposable<IStorage> storage)
|
||||
{
|
||||
storage = default;
|
||||
|
||||
@ -21,16 +21,17 @@ namespace LibHac.FsSrv.Creators
|
||||
return ResultFs.InvalidGameCardHandleOnOpenNormalPartition.Log();
|
||||
}
|
||||
|
||||
var baseStorage = new ReadOnlyGameCardStorage(GameCard, handle);
|
||||
var baseStorage = new ReferenceCountedDisposable<IStorage>(new ReadOnlyGameCardStorage(GameCard, handle));
|
||||
|
||||
Result rc = GameCard.GetCardInfo(out GameCardInfo cardInfo, handle);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
storage = new SubStorage(baseStorage, 0, cardInfo.SecureAreaOffset);
|
||||
storage = new ReferenceCountedDisposable<IStorage>(
|
||||
new SubStorage(baseStorage, 0, cardInfo.SecureAreaOffset));
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result CreateSecure(GameCardHandle handle, out IStorage storage)
|
||||
public Result CreateSecureReadOnly(GameCardHandle handle, out ReferenceCountedDisposable<IStorage> storage)
|
||||
{
|
||||
storage = default;
|
||||
|
||||
@ -48,16 +49,19 @@ namespace LibHac.FsSrv.Creators
|
||||
rc = GameCard.GetGameCardImageHash(imageHash);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var baseStorage = new ReadOnlyGameCardStorage(GameCard, handle, deviceId, imageHash);
|
||||
var baseStorage =
|
||||
new ReferenceCountedDisposable<IStorage>(new ReadOnlyGameCardStorage(GameCard, handle, deviceId,
|
||||
imageHash));
|
||||
|
||||
rc = GameCard.GetCardInfo(out GameCardInfo cardInfo, handle);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
storage = new SubStorage(baseStorage, cardInfo.SecureAreaOffset, cardInfo.SecureAreaSize);
|
||||
storage = new ReferenceCountedDisposable<IStorage>(new SubStorage(baseStorage, cardInfo.SecureAreaOffset,
|
||||
cardInfo.SecureAreaSize));
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result CreateWritable(GameCardHandle handle, out IStorage storage)
|
||||
public Result CreateWriteOnly(GameCardHandle handle, out ReferenceCountedDisposable<IStorage> storage)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ using LibHac.Fs.Fsa;
|
||||
|
||||
namespace LibHac.FsSrv.Creators
|
||||
{
|
||||
public class EmulatedSdFileSystemCreator : ISdFileSystemCreator
|
||||
public class EmulatedSdCardFileSystemCreator : ISdCardProxyFileSystemCreator
|
||||
{
|
||||
private const string DefaultPath = "/sdcard";
|
||||
|
||||
@ -14,20 +14,20 @@ namespace LibHac.FsSrv.Creators
|
||||
|
||||
private IFileSystem SdCardFileSystem { get; set; }
|
||||
|
||||
public EmulatedSdFileSystemCreator(EmulatedSdCard sdCard, IFileSystem rootFileSystem)
|
||||
public EmulatedSdCardFileSystemCreator(EmulatedSdCard sdCard, IFileSystem rootFileSystem)
|
||||
{
|
||||
SdCard = sdCard;
|
||||
RootFileSystem = rootFileSystem;
|
||||
}
|
||||
|
||||
public EmulatedSdFileSystemCreator(EmulatedSdCard sdCard, IFileSystem rootFileSystem, string path)
|
||||
public EmulatedSdCardFileSystemCreator(EmulatedSdCard sdCard, IFileSystem rootFileSystem, string path)
|
||||
{
|
||||
SdCard = sdCard;
|
||||
RootFileSystem = rootFileSystem;
|
||||
Path = path;
|
||||
}
|
||||
|
||||
public Result Create(out IFileSystem fileSystem)
|
||||
public Result Create(out IFileSystem fileSystem, bool isCaseSensitive)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
@ -61,7 +61,12 @@ namespace LibHac.FsSrv.Creators
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result Format(bool closeOpenEntries)
|
||||
public Result Format(bool removeFromFatFsCache)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result Format()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
@ -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, KeySet.SdCardEncryptionKeys[(int)keyId].DataRo.ToArray(), 0x4000);
|
||||
encryptedFileSystem = new ReferenceCountedDisposable<IFileSystem>(fs);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
@ -17,6 +17,6 @@
|
||||
public IEncryptedFileSystemCreator EncryptedFileSystemCreator { get; set; }
|
||||
public IMemoryStorageCreator MemoryStorageCreator { get; set; }
|
||||
public IBuiltInStorageFileSystemCreator BuiltInStorageFileSystemCreator { get; set; }
|
||||
public ISdFileSystemCreator SdFileSystemCreator { get; set; }
|
||||
public ISdCardProxyFileSystemCreator SdCardFileSystemCreator { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ namespace LibHac.FsSrv.Creators
|
||||
{
|
||||
public interface IBuiltInStorageCreator
|
||||
{
|
||||
Result Create(out IStorage storage, BisPartitionId partitionId);
|
||||
Result Create(out ReferenceCountedDisposable<IStorage> storage, BisPartitionId partitionId);
|
||||
Result InvalidateCache();
|
||||
}
|
||||
}
|
||||
|
@ -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, bool caseSensitive);
|
||||
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);
|
||||
}
|
||||
}
|
@ -4,8 +4,8 @@ namespace LibHac.FsSrv.Creators
|
||||
{
|
||||
public interface IGameCardStorageCreator
|
||||
{
|
||||
Result CreateNormal(GameCardHandle handle, out IStorage storage);
|
||||
Result CreateSecure(GameCardHandle handle, out IStorage storage);
|
||||
Result CreateWritable(GameCardHandle handle, out IStorage storage);
|
||||
Result CreateReadOnly(GameCardHandle handle, out ReferenceCountedDisposable<IStorage> storage);
|
||||
Result CreateSecureReadOnly(GameCardHandle handle, out ReferenceCountedDisposable<IStorage> storage);
|
||||
Result CreateWriteOnly(GameCardHandle handle, out ReferenceCountedDisposable<IStorage> storage);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSystem.Save;
|
||||
using LibHac.FsSystem;
|
||||
|
||||
namespace LibHac.FsSrv.Creators
|
||||
{
|
||||
@ -10,9 +10,10 @@ namespace LibHac.FsSrv.Creators
|
||||
{
|
||||
Result CreateFile(out IFile file, IFileSystem sourceFileSystem, ulong saveDataId, OpenMode openMode);
|
||||
|
||||
Result Create(out IFileSystem fileSystem, out ISaveDataExtraDataAccessor extraDataAccessor,
|
||||
IFileSystem sourceFileSystem, ulong saveDataId, bool allowDirectorySaveData, bool useDeviceUniqueMac,
|
||||
SaveDataType type, ITimeStampGenerator timeStampGenerator);
|
||||
Result Create(out IFileSystem fileSystem,
|
||||
out ReferenceCountedDisposable<ISaveDataExtraDataAccessor> extraDataAccessor, IFileSystem sourceFileSystem,
|
||||
ulong saveDataId, bool allowDirectorySaveData, bool useDeviceUniqueMac, SaveDataType type,
|
||||
ITimeStampGenerator timeStampGenerator);
|
||||
|
||||
void SetSdCardEncryptionSeed(ReadOnlySpan<byte> seed);
|
||||
}
|
||||
|
23
src/LibHac/FsSrv/Creators/ISdCardProxyFileSystemCreator.cs
Normal file
23
src/LibHac/FsSrv/Creators/ISdCardProxyFileSystemCreator.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using LibHac.Fs.Fsa;
|
||||
|
||||
namespace LibHac.FsSrv.Creators
|
||||
{
|
||||
public interface ISdCardProxyFileSystemCreator
|
||||
{
|
||||
Result Create(out IFileSystem fileSystem, bool isCaseSensitive);
|
||||
|
||||
/// <summary>
|
||||
/// Formats the SD card.
|
||||
/// </summary>
|
||||
/// <param name="removeFromFatFsCache">Should the SD card file system be removed from the
|
||||
/// FAT file system cache?</param>
|
||||
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||
Result Format(bool removeFromFatFsCache);
|
||||
|
||||
/// <summary>
|
||||
/// Automatically closes all open proxy file system entries and formats the SD card.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||
Result Format();
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
using LibHac.Fs.Fsa;
|
||||
|
||||
namespace LibHac.FsSrv.Creators
|
||||
{
|
||||
public interface ISdFileSystemCreator
|
||||
{
|
||||
Result Create(out IFileSystem fileSystem);
|
||||
Result Format(bool closeOpenEntries);
|
||||
}
|
||||
}
|
@ -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,7 @@ namespace LibHac.FsSrv.Creators
|
||||
{
|
||||
public interface ISubDirectoryFileSystemCreator
|
||||
{
|
||||
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, ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path);
|
||||
Result Create(out ReferenceCountedDisposable<IFileSystem> subDirFileSystem, ref 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 GetCaseSensitivePath(out bool isSuccess, Span<byte> path);
|
||||
Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, bool openCaseSensitive);
|
||||
Result NormalizeCaseOfPath(out bool isSupported, 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;
|
||||
}
|
||||
}
|
||||
|
@ -22,9 +22,10 @@ namespace LibHac.FsSrv.Creators
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result Create(out IFileSystem fileSystem, out ISaveDataExtraDataAccessor extraDataAccessor,
|
||||
IFileSystem sourceFileSystem, ulong saveDataId, bool allowDirectorySaveData, bool useDeviceUniqueMac,
|
||||
SaveDataType type, ITimeStampGenerator timeStampGenerator)
|
||||
public Result Create(out IFileSystem fileSystem,
|
||||
out ReferenceCountedDisposable<ISaveDataExtraDataAccessor> extraDataAccessor, IFileSystem sourceFileSystem,
|
||||
ulong saveDataId, bool allowDirectorySaveData, bool useDeviceUniqueMac, SaveDataType type,
|
||||
ITimeStampGenerator timeStampGenerator)
|
||||
{
|
||||
fileSystem = default;
|
||||
extraDataAccessor = default;
|
||||
@ -42,13 +43,15 @@ namespace LibHac.FsSrv.Creators
|
||||
case DirectoryEntryType.Directory:
|
||||
if (!allowDirectorySaveData) return ResultFs.InvalidSaveDataEntryType.Log();
|
||||
|
||||
rc = SubdirectoryFileSystem.CreateNew(out SubdirectoryFileSystem subDirFs, sourceFileSystem, saveDataPath);
|
||||
rc = SubdirectoryFileSystem.CreateNew(out SubdirectoryFileSystem subDirFs, sourceFileSystem,
|
||||
saveDataPath);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
bool isPersistentSaveData = type != SaveDataType.Temporary;
|
||||
bool isUserSaveData = type == SaveDataType.Account || type == SaveDataType.Device;
|
||||
|
||||
rc = DirectorySaveDataFileSystem.CreateNew(out DirectorySaveDataFileSystem saveFs, subDirFs, isPersistentSaveData, isUserSaveData);
|
||||
rc = DirectorySaveDataFileSystem.CreateNew(out DirectorySaveDataFileSystem saveFs, subDirFs,
|
||||
isPersistentSaveData, isUserSaveData);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
fileSystem = saveFs;
|
||||
@ -62,7 +65,8 @@ namespace LibHac.FsSrv.Creators
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
var saveDataStorage = new DisposingFileStorage(saveDataFile);
|
||||
fileSystem = new SaveDataFileSystem(KeySet, saveDataStorage, IntegrityCheckLevel.ErrorOnInvalid, false);
|
||||
fileSystem = new SaveDataFileSystem(KeySet, saveDataStorage, IntegrityCheckLevel.ErrorOnInvalid,
|
||||
false);
|
||||
|
||||
// Todo: ISaveDataExtraDataAccessor
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -6,21 +6,30 @@ namespace LibHac.FsSrv.Creators
|
||||
{
|
||||
public class SubDirectoryFileSystemCreator : ISubDirectoryFileSystemCreator
|
||||
{
|
||||
public Result Create(out IFileSystem subDirFileSystem, IFileSystem baseFileSystem, U8Span path)
|
||||
public Result Create(out ReferenceCountedDisposable<IFileSystem> subDirFileSystem,
|
||||
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path)
|
||||
{
|
||||
return Create(out subDirFileSystem, baseFileSystem, path, false);
|
||||
return Create(out subDirFileSystem, ref baseFileSystem, path, false);
|
||||
}
|
||||
|
||||
public Result Create(out IFileSystem subDirFileSystem, IFileSystem baseFileSystem, U8Span path, bool preserveUnc)
|
||||
public Result Create(out ReferenceCountedDisposable<IFileSystem> subDirFileSystem,
|
||||
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path, bool preserveUnc)
|
||||
{
|
||||
subDirFileSystem = default;
|
||||
|
||||
Result rc = baseFileSystem.OpenDirectory(out IDirectory _, path, OpenDirectoryMode.Directory);
|
||||
// Verify the sub-path exists
|
||||
Result rc = baseFileSystem.Target.OpenDirectory(out IDirectory _, path, OpenDirectoryMode.Directory);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = SubdirectoryFileSystem.CreateNew(out SubdirectoryFileSystem fs, baseFileSystem, path.ToU8String(), preserveUnc);
|
||||
subDirFileSystem = fs;
|
||||
return rc;
|
||||
// Initialize the SubdirectoryFileSystem
|
||||
var subDir = new SubdirectoryFileSystem(ref 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,7 +10,12 @@ namespace LibHac.FsSrv.Creators
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result GetCaseSensitivePath(out bool isSuccess, Span<byte> path)
|
||||
public Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, bool openCaseSensitive)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result NormalizeCaseOfPath(out bool isSupported, Span<byte> path)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
using LibHac.Common.Keys;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSrv.Creators;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
|
||||
|
||||
namespace LibHac.FsSrv
|
||||
{
|
||||
@ -29,7 +30,7 @@ namespace LibHac.FsSrv
|
||||
creators.GameCardFileSystemCreator = new EmulatedGameCardFsCreator(gcStorageCreator, gameCard);
|
||||
creators.EncryptedFileSystemCreator = new EncryptedFileSystemCreator(keySet);
|
||||
creators.BuiltInStorageFileSystemCreator = new EmulatedBisFileSystemCreator(rootFileSystem);
|
||||
creators.SdFileSystemCreator = new EmulatedSdFileSystemCreator(sdCard, rootFileSystem);
|
||||
creators.SdCardFileSystemCreator = new EmulatedSdCardFileSystemCreator(sdCard, rootFileSystem);
|
||||
|
||||
var deviceOperator = new EmulatedDeviceOperator(gameCard, sdCard);
|
||||
|
||||
|
12
src/LibHac/FsSrv/Delegates.cs
Normal file
12
src/LibHac/FsSrv/Delegates.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace LibHac.FsSrv
|
||||
{
|
||||
public delegate Result RandomDataGenerator(Span<byte> buffer);
|
||||
|
||||
public delegate Result SaveTransferAesKeyGenerator(Span<byte> key,
|
||||
SaveDataTransferCryptoConfiguration.KeyIndex index, ReadOnlySpan<byte> keySource, int keyGeneration);
|
||||
|
||||
public delegate Result SaveTransferCmacGenerator(Span<byte> mac, ReadOnlySpan<byte> data,
|
||||
SaveDataTransferCryptoConfiguration.KeyIndex index, int keyGeneration);
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using LibHac.Fs;
|
||||
using LibHac.FsSrv.Sf;
|
||||
|
||||
namespace LibHac.FsSrv
|
||||
{
|
||||
@ -13,6 +14,8 @@ namespace LibHac.FsSrv
|
||||
SdCard = sdCard;
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
public Result IsSdCardInserted(out bool isInserted)
|
||||
{
|
||||
isInserted = SdCard.IsSdCardInserted();
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -5,6 +5,12 @@ namespace LibHac.FsSrv
|
||||
public class FileSystemProxyConfiguration
|
||||
{
|
||||
public FileSystemCreators FsCreatorInterfaces { get; set; }
|
||||
public ProgramRegistryServiceImpl ProgramRegistryServiceImpl { get; set; }
|
||||
public BaseStorageServiceImpl BaseStorageService { get; set; }
|
||||
public BaseFileSystemServiceImpl BaseFileSystemService { get; set; }
|
||||
public NcaFileSystemServiceImpl NcaFileSystemService { get; set; }
|
||||
public SaveDataFileSystemServiceImpl SaveDataFileSystemService { get; set; }
|
||||
public TimeServiceImpl TimeService { get; set; }
|
||||
public ProgramRegistryServiceImpl ProgramRegistryService { get; set; }
|
||||
public AccessLogServiceImpl AccessLogService { get; set; }
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
128
src/LibHac/FsSrv/FileSystemProxyCoreImpl.cs
Normal file
128
src/LibHac/FsSrv/FileSystemProxyCoreImpl.cs
Normal file
@ -0,0 +1,128 @@
|
||||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Fs.Shim;
|
||||
using LibHac.FsSrv.Creators;
|
||||
|
||||
namespace LibHac.FsSrv
|
||||
{
|
||||
public class FileSystemProxyCoreImpl
|
||||
{
|
||||
internal FileSystemProxyConfiguration Config { get; }
|
||||
private FileSystemCreators FsCreators => Config.FsCreatorInterfaces;
|
||||
internal ProgramRegistryImpl ProgramRegistry { get; }
|
||||
|
||||
private byte[] SdEncryptionSeed { get; } = new byte[0x10];
|
||||
|
||||
private const string NintendoDirectoryName = "Nintendo";
|
||||
|
||||
public FileSystemProxyCoreImpl(FileSystemProxyConfiguration config)
|
||||
{
|
||||
Config = config;
|
||||
ProgramRegistry = new ProgramRegistryImpl(Config.ProgramRegistryService);
|
||||
}
|
||||
|
||||
public Result OpenCustomStorageFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
CustomStorageId storageId)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
switch (storageId)
|
||||
{
|
||||
case CustomStorageId.SdCard:
|
||||
{
|
||||
Result rc = FsCreators.SdCardFileSystemCreator.Create(out IFileSystem sdFs, false);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
string customStorageDir = CustomStorage.GetCustomStorageDirectoryName(CustomStorageId.SdCard);
|
||||
string subDirName = $"/{NintendoDirectoryName}/{customStorageDir}";
|
||||
|
||||
rc = Util.CreateSubFileSystem(out IFileSystem subFs, sdFs, subDirName, true);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rc = FsCreators.EncryptedFileSystemCreator.Create(out IFileSystem encryptedFs, subFs,
|
||||
EncryptedFsKeyId.CustomStorage, SdEncryptionSeed);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
fileSystem = new ReferenceCountedDisposable<IFileSystem>(encryptedFs);
|
||||
return Result.Success;
|
||||
}
|
||||
case CustomStorageId.System:
|
||||
{
|
||||
Result rc = FsCreators.BuiltInStorageFileSystemCreator.Create(out IFileSystem userFs, string.Empty,
|
||||
BisPartitionId.User);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
string customStorageDir = CustomStorage.GetCustomStorageDirectoryName(CustomStorageId.System);
|
||||
string subDirName = $"/{customStorageDir}";
|
||||
|
||||
rc = Util.CreateSubFileSystem(out IFileSystem subFs, userFs, subDirName, true);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
// Todo: Get shared object from earlier functions
|
||||
fileSystem = new ReferenceCountedDisposable<IFileSystem>(subFs);
|
||||
return Result.Success;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
// Todo: Return shared fs from Create
|
||||
rc = FsCreators.TargetManagerFileSystemCreator.Create(out IFileSystem hostFs, openCaseSensitive);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
ReferenceCountedDisposable<IFileSystem> sharedHostFs = null;
|
||||
ReferenceCountedDisposable<IFileSystem> subDirFs = null;
|
||||
|
||||
try
|
||||
{
|
||||
sharedHostFs = new ReferenceCountedDisposable<IFileSystem>(hostFs);
|
||||
|
||||
if (path.IsEmpty())
|
||||
{
|
||||
ReadOnlySpan<byte> rootHostPath = new[] { (byte)'C', (byte)':', (byte)'/' };
|
||||
rc = sharedHostFs.Target.GetEntryType(out _, new U8Span(rootHostPath));
|
||||
|
||||
// Nintendo ignores all results other than this one
|
||||
if (ResultFs.TargetNotFound.Includes(rc))
|
||||
return rc;
|
||||
|
||||
Shared.Move(out fileSystem, ref sharedHostFs);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
rc = FsCreators.SubDirectoryFileSystemCreator.Create(out subDirFs, ref sharedHostFs, path,
|
||||
preserveUnc: true);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
fileSystem = subDirFs;
|
||||
return Result.Success;
|
||||
}
|
||||
finally
|
||||
{
|
||||
sharedHostFs?.Dispose();
|
||||
subDirFs?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public Result SetSdCardEncryptionSeed(in EncryptionSeed seed)
|
||||
{
|
||||
seed.Value.CopyTo(SdEncryptionSeed);
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
1030
src/LibHac/FsSrv/FileSystemProxyImpl.cs
Normal file
1030
src/LibHac/FsSrv/FileSystemProxyImpl.cs
Normal file
File diff suppressed because it is too large
Load Diff
@ -3,6 +3,8 @@ using LibHac.Fs;
|
||||
using LibHac.Fs.Impl;
|
||||
using LibHac.Fs.Shim;
|
||||
using LibHac.FsSrv.Creators;
|
||||
using LibHac.FsSrv.Impl;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.Sm;
|
||||
|
||||
namespace LibHac.FsSrv
|
||||
@ -11,7 +13,10 @@ namespace LibHac.FsSrv
|
||||
{
|
||||
internal const ulong SaveIndexerId = 0x8000000000000000;
|
||||
|
||||
private FileSystemProxyCore FsProxyCore { get; }
|
||||
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>
|
||||
public HorizonClient Hos { get; }
|
||||
@ -36,23 +41,23 @@ namespace LibHac.FsSrv
|
||||
|
||||
IsDebugMode = false;
|
||||
|
||||
ExternalKeySet externalKeySet = config.ExternalKeySet ?? new ExternalKeySet();
|
||||
Timer = config.TimeSpanGenerator ?? new StopWatchTimeSpanGenerator();
|
||||
|
||||
var fspConfig = new FileSystemProxyConfiguration
|
||||
{
|
||||
FsCreatorInterfaces = config.FsCreators,
|
||||
ProgramRegistryServiceImpl = new ProgramRegistryServiceImpl(this)
|
||||
};
|
||||
FileSystemProxyConfiguration fspConfig = InitializeFileSystemProxyConfiguration(config);
|
||||
|
||||
FsProxyCore = new FileSystemProxyCore(fspConfig, externalKeySet, config.DeviceOperator);
|
||||
FsProxyCore = new FileSystemProxyCoreImpl(fspConfig);
|
||||
|
||||
FsProxyCore.SetSaveDataIndexerManager(new SaveDataIndexerManager(Hos.Fs, SaveIndexerId,
|
||||
new ArrayPoolMemoryResource(), new SdHandleManager(), false));
|
||||
FileSystemProxyImpl fsProxy = GetFileSystemProxyServiceObject();
|
||||
ulong processId = Hos.Os.GetCurrentProcessId().Value;
|
||||
fsProxy.SetCurrentProcess(processId).IgnoreResult();
|
||||
|
||||
FileSystemProxy fsProxy = GetFileSystemProxyServiceObject();
|
||||
fsProxy.SetCurrentProcess(Hos.Os.GetCurrentProcessId().Value).IgnoreResult();
|
||||
fsProxy.CleanUpTemporaryStorage().IgnoreResult();
|
||||
var saveService = new SaveDataFileSystemService(fspConfig.SaveDataFileSystemService, processId);
|
||||
|
||||
saveService.CleanUpTemporaryStorage().IgnoreResult();
|
||||
saveService.CleanUpSaveData().IgnoreResult();
|
||||
saveService.CompleteSaveDataExtension().IgnoreResult();
|
||||
saveService.FixSaveData().IgnoreResult();
|
||||
saveService.RecoverMultiCommit().IgnoreResult();
|
||||
|
||||
Hos.Sm.RegisterService(new FileSystemProxyService(this), "fsp-srv").IgnoreResult();
|
||||
Hos.Sm.RegisterService(new FileSystemProxyForLoaderService(this), "fsp-ldr").IgnoreResult();
|
||||
@ -81,19 +86,100 @@ namespace LibHac.FsSrv
|
||||
return new FileSystemClient(Hos);
|
||||
}
|
||||
|
||||
private FileSystemProxy GetFileSystemProxyServiceObject()
|
||||
private FileSystemProxyConfiguration InitializeFileSystemProxyConfiguration(FileSystemServerConfig config)
|
||||
{
|
||||
return new FileSystemProxy(Hos, FsProxyCore);
|
||||
var saveDataIndexerManager = new SaveDataIndexerManager(Hos.Fs, SaveIndexerId,
|
||||
new ArrayPoolMemoryResource(), new SdHandleManager(), false);
|
||||
|
||||
var programRegistryService = new ProgramRegistryServiceImpl(this);
|
||||
var programRegistry = new ProgramRegistryImpl(programRegistryService);
|
||||
|
||||
var baseStorageConfig = new BaseStorageServiceImpl.Configuration();
|
||||
baseStorageConfig.BisStorageCreator = config.FsCreators.BuiltInStorageCreator;
|
||||
baseStorageConfig.GameCardStorageCreator = config.FsCreators.GameCardStorageCreator;
|
||||
baseStorageConfig.ProgramRegistry = programRegistry;
|
||||
baseStorageConfig.DeviceOperator = new ReferenceCountedDisposable<IDeviceOperator>(config.DeviceOperator);
|
||||
var baseStorageService = new BaseStorageServiceImpl(in baseStorageConfig);
|
||||
|
||||
var timeServiceConfig = new TimeServiceImpl.Configuration();
|
||||
timeServiceConfig.HorizonClient = Hos;
|
||||
timeServiceConfig.ProgramRegistry = programRegistry;
|
||||
var timeService = new TimeServiceImpl(in timeServiceConfig);
|
||||
|
||||
var baseFsServiceConfig = new BaseFileSystemServiceImpl.Configuration();
|
||||
baseFsServiceConfig.BisFileSystemCreator = config.FsCreators.BuiltInStorageFileSystemCreator;
|
||||
baseFsServiceConfig.GameCardFileSystemCreator = config.FsCreators.GameCardFileSystemCreator;
|
||||
baseFsServiceConfig.SdCardFileSystemCreator = config.FsCreators.SdCardFileSystemCreator;
|
||||
baseFsServiceConfig.BisWiperCreator = BisWiper.CreateWiper;
|
||||
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 saveFsServiceConfig = new SaveDataFileSystemServiceImpl.Configuration();
|
||||
saveFsServiceConfig.BaseFsService = baseFsService;
|
||||
saveFsServiceConfig.HostFsCreator = config.FsCreators.HostFileSystemCreator;
|
||||
saveFsServiceConfig.TargetManagerFsCreator = config.FsCreators.TargetManagerFileSystemCreator;
|
||||
saveFsServiceConfig.SaveFsCreator = config.FsCreators.SaveDataFileSystemCreator;
|
||||
saveFsServiceConfig.EncryptedFsCreator = config.FsCreators.EncryptedFileSystemCreator;
|
||||
saveFsServiceConfig.ProgramRegistryService = programRegistryService;
|
||||
saveFsServiceConfig.ShouldCreateDirectorySaveData = () => true;
|
||||
saveFsServiceConfig.SaveIndexerManager = saveDataIndexerManager;
|
||||
saveFsServiceConfig.HorizonClient = Hos;
|
||||
saveFsServiceConfig.ProgramRegistry = programRegistry;
|
||||
|
||||
var saveFsService = new SaveDataFileSystemServiceImpl(in saveFsServiceConfig);
|
||||
|
||||
var accessLogServiceConfig = new AccessLogServiceImpl.Configuration();
|
||||
accessLogServiceConfig.MinimumProgramIdForSdCardLog = 0x0100000000003000;
|
||||
accessLogServiceConfig.HorizonClient = Hos;
|
||||
accessLogServiceConfig.ProgramRegistry = programRegistry;
|
||||
var accessLogService = new AccessLogServiceImpl(in accessLogServiceConfig);
|
||||
|
||||
var fspConfig = new FileSystemProxyConfiguration
|
||||
{
|
||||
FsCreatorInterfaces = config.FsCreators,
|
||||
BaseStorageService = baseStorageService,
|
||||
BaseFileSystemService = baseFsService,
|
||||
NcaFileSystemService = ncaFsService,
|
||||
SaveDataFileSystemService = saveFsService,
|
||||
TimeService = timeService,
|
||||
ProgramRegistryService = programRegistryService,
|
||||
AccessLogService = accessLogService
|
||||
};
|
||||
|
||||
return fspConfig;
|
||||
}
|
||||
|
||||
private FileSystemProxy GetFileSystemProxyForLoaderServiceObject()
|
||||
private FileSystemProxyImpl GetFileSystemProxyServiceObject()
|
||||
{
|
||||
return new FileSystemProxy(Hos, FsProxyCore);
|
||||
return new FileSystemProxyImpl(FsProxyCore);
|
||||
}
|
||||
|
||||
private FileSystemProxyImpl GetFileSystemProxyForLoaderServiceObject()
|
||||
{
|
||||
return new FileSystemProxyImpl(FsProxyCore);
|
||||
}
|
||||
|
||||
private ProgramRegistryImpl GetProgramRegistryServiceObject()
|
||||
{
|
||||
return new ProgramRegistryImpl(FsProxyCore.Config.ProgramRegistryServiceImpl);
|
||||
return new ProgramRegistryImpl(FsProxyCore.Config.ProgramRegistryService);
|
||||
}
|
||||
|
||||
private class FileSystemProxyService : IServiceObject
|
||||
|
@ -1,107 +1,119 @@
|
||||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.Fs;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Sf;
|
||||
using LibHac.Spl;
|
||||
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||
using IFileSf = LibHac.FsSrv.Sf.IFile;
|
||||
using IStorageSf = LibHac.FsSrv.Sf.IStorage;
|
||||
|
||||
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, ref FsPath rootPath, BisPartitionId partitionId);
|
||||
Result OpenBisStorage(out IStorage storage, BisPartitionId partitionId);
|
||||
Result OpenDataFileSystemByCurrentProcess(out ReferenceCountedDisposable<IFileSystemSf> fileSystem);
|
||||
Result OpenFileSystemWithPatch(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, ProgramId programId, FileSystemProxyType fsType);
|
||||
Result OpenFileSystemWithId(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, in FspPath path, ulong id, FileSystemProxyType fsType);
|
||||
Result OpenDataFileSystemByProgramId(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, ProgramId programId);
|
||||
Result OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, in FspPath rootPath, BisPartitionId partitionId);
|
||||
Result OpenBisStorage(out ReferenceCountedDisposable<IStorageSf> 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 OpenHostFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, in FspPath path);
|
||||
Result OpenSdCardFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem);
|
||||
Result FormatSdCardFileSystem();
|
||||
Result DeleteSaveDataFileSystem(ulong saveDataId);
|
||||
Result CreateSaveDataFileSystem(ref SaveDataAttribute attribute, ref SaveDataCreationInfo creationInfo, ref SaveMetaCreateInfo metaCreateInfo);
|
||||
Result CreateSaveDataFileSystemBySystemSaveDataId(ref SaveDataAttribute attribute, ref SaveDataCreationInfo creationInfo);
|
||||
Result RegisterSaveDataFileSystemAtomicDeletion(ReadOnlySpan<ulong> saveDataIds);
|
||||
Result CreateSaveDataFileSystem(in SaveDataAttribute attribute, in SaveDataCreationInfo creationInfo, in SaveDataMetaInfo metaInfo);
|
||||
Result CreateSaveDataFileSystemBySystemSaveDataId(in SaveDataAttribute attribute, in SaveDataCreationInfo creationInfo);
|
||||
Result RegisterSaveDataFileSystemAtomicDeletion(InBuffer saveDataIds);
|
||||
Result DeleteSaveDataFileSystemBySaveDataSpaceId(SaveDataSpaceId spaceId, ulong saveDataId);
|
||||
Result FormatSdCardDryRun();
|
||||
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 DeleteSaveDataFileSystemBySaveDataAttribute(SaveDataSpaceId spaceId, in SaveDataAttribute attribute);
|
||||
Result OpenGameCardStorage(out ReferenceCountedDisposable<IStorageSf> storage, GameCardHandle handle, GameCardPartitionRaw partitionId);
|
||||
Result OpenGameCardFileSystem(out ReferenceCountedDisposable<IFileSystemSf> 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);
|
||||
Result CreateSaveDataFileSystemWithHashSalt(ref SaveDataAttribute attribute, ref SaveDataCreationInfo creationInfo, ref SaveMetaCreateInfo metaCreateInfo, ref HashSalt hashSalt);
|
||||
Result OpenSaveDataFileSystem(out IFileSystem fileSystem, SaveDataSpaceId spaceId, ref SaveDataAttribute attribute);
|
||||
Result OpenSaveDataFileSystemBySystemSaveDataId(out IFileSystem fileSystem, SaveDataSpaceId spaceId, ref SaveDataAttribute attribute);
|
||||
Result OpenReadOnlySaveDataFileSystem(out IFileSystem fileSystem, SaveDataSpaceId spaceId, ref SaveDataAttribute attribute);
|
||||
Result ReadSaveDataFileSystemExtraDataBySaveDataSpaceId(Span<byte> extraDataBuffer, SaveDataSpaceId spaceId, ulong saveDataId);
|
||||
Result ReadSaveDataFileSystemExtraData(Span<byte> extraDataBuffer, ulong saveDataId);
|
||||
Result WriteSaveDataFileSystemExtraData(ulong saveDataId, SaveDataSpaceId spaceId, ReadOnlySpan<byte> extraDataBuffer);
|
||||
Result DeleteCacheStorage(ushort index);
|
||||
Result GetCacheStorageSize(out long dataSize, out long journalSize, ushort index);
|
||||
Result CreateSaveDataFileSystemWithHashSalt(in SaveDataAttribute attribute, in SaveDataCreationInfo creationInfo, in SaveDataMetaInfo metaInfo, in HashSalt hashSalt);
|
||||
Result OpenHostFileSystemWithOption(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, in FspPath path, MountHostOption option);
|
||||
Result OpenSaveDataFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, SaveDataSpaceId spaceId, in SaveDataAttribute attribute);
|
||||
Result OpenSaveDataFileSystemBySystemSaveDataId(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, SaveDataSpaceId spaceId, in SaveDataAttribute attribute);
|
||||
Result OpenReadOnlySaveDataFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, SaveDataSpaceId spaceId, in SaveDataAttribute attribute);
|
||||
Result ReadSaveDataFileSystemExtraDataBySaveDataSpaceId(OutBuffer extraDataBuffer, SaveDataSpaceId spaceId, ulong saveDataId);
|
||||
Result ReadSaveDataFileSystemExtraData(OutBuffer extraDataBuffer, ulong saveDataId);
|
||||
Result WriteSaveDataFileSystemExtraData(ulong saveDataId, SaveDataSpaceId spaceId, InBuffer extraDataBuffer);
|
||||
Result OpenSaveDataInfoReader(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader);
|
||||
Result OpenSaveDataInfoReaderBySaveDataSpaceId(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader, SaveDataSpaceId spaceId);
|
||||
Result OpenSaveDataInfoReaderOnlyCacheStorage(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader);
|
||||
Result OpenSaveDataInternalStorageFileSystem(out IFileSystem fileSystem, SaveDataSpaceId spaceId, ulong saveDataId);
|
||||
Result OpenSaveDataInternalStorageFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, SaveDataSpaceId spaceId, ulong saveDataId);
|
||||
Result UpdateSaveDataMacForDebug(SaveDataSpaceId spaceId, ulong saveDataId);
|
||||
Result WriteSaveDataFileSystemExtraDataWithMask(ulong saveDataId, SaveDataSpaceId spaceId, ReadOnlySpan<byte> extraDataBuffer, ReadOnlySpan<byte> maskBuffer);
|
||||
Result FindSaveDataWithFilter(out long count, Span<byte> saveDataInfoBuffer, SaveDataSpaceId spaceId, ref SaveDataFilter filter);
|
||||
Result OpenSaveDataInfoReaderWithFilter(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader, SaveDataSpaceId spaceId, ref SaveDataFilter filter);
|
||||
Result ReadSaveDataFileSystemExtraDataBySaveDataAttribute(Span<byte> extraDataBuffer, SaveDataSpaceId spaceId, ref SaveDataAttribute attribute);
|
||||
Result WriteSaveDataFileSystemExtraDataBySaveDataAttribute(ref SaveDataAttribute attribute, SaveDataSpaceId spaceId, ReadOnlySpan<byte> extraDataBuffer, ReadOnlySpan<byte> maskBuffer);
|
||||
Result OpenSaveDataMetaFile(out IFile file, SaveDataSpaceId spaceId, ref SaveDataAttribute attribute, SaveDataMetaType type);
|
||||
Result WriteSaveDataFileSystemExtraDataWithMask(ulong saveDataId, SaveDataSpaceId spaceId, InBuffer extraDataBuffer, InBuffer maskBuffer);
|
||||
Result FindSaveDataWithFilter(out long count, OutBuffer saveDataInfoBuffer, SaveDataSpaceId spaceId, in SaveDataFilter filter);
|
||||
Result OpenSaveDataInfoReaderWithFilter(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader, SaveDataSpaceId spaceId, in SaveDataFilter filter);
|
||||
Result ReadSaveDataFileSystemExtraDataBySaveDataAttribute(OutBuffer extraDataBuffer, SaveDataSpaceId spaceId, in SaveDataAttribute attribute);
|
||||
Result WriteSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(in SaveDataAttribute attribute, SaveDataSpaceId spaceId, InBuffer extraDataBuffer, InBuffer maskBuffer);
|
||||
Result ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(OutBuffer extraDataBuffer, SaveDataSpaceId spaceId, in SaveDataAttribute attribute, InBuffer maskBuffer);
|
||||
Result OpenSaveDataMetaFile(out ReferenceCountedDisposable<IFileSf> file, SaveDataSpaceId spaceId, in 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 dirId);
|
||||
Result OpenContentStorageFileSystem(out 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 OpenPatchDataStorageByCurrentProcess(out IStorage storage);
|
||||
Result OpenDataFileSystemWithProgramIndex(out IFileSystem fileSystem, byte programIndex);
|
||||
Result OpenDataStorageWithProgramIndex(out IStorage storage, byte programIndex);
|
||||
Result OpenDeviceOperator(out IDeviceOperator deviceOperator);
|
||||
Result ListAccessibleSaveDataOwnerId(out int readCount, OutBuffer idBuffer, ProgramId programId, int startIndex, int bufferIdCount);
|
||||
Result OpenSaveDataMover(out ReferenceCountedDisposable<ISaveDataMover> saveMover, SaveDataSpaceId sourceSpaceId, SaveDataSpaceId destinationSpaceId, NativeHandle workBufferHandle, ulong workBufferSize);
|
||||
Result OpenImageDirectoryFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, ImageDirectoryId directoryId);
|
||||
Result OpenContentStorageFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, ContentStorageId storageId);
|
||||
Result OpenCloudBackupWorkStorageFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, CloudBackupWorkStorageId storageId);
|
||||
Result OpenCustomStorageFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, CustomStorageId storageId);
|
||||
Result OpenDataStorageByCurrentProcess(out ReferenceCountedDisposable<IStorageSf> storage);
|
||||
Result OpenDataStorageByProgramId(out ReferenceCountedDisposable<IStorageSf> storage, ProgramId programId);
|
||||
Result OpenDataStorageByDataId(out ReferenceCountedDisposable<IStorageSf> storage, DataId dataId, StorageId storageId);
|
||||
Result OpenPatchDataStorageByCurrentProcess(out ReferenceCountedDisposable<IStorageSf> storage);
|
||||
Result OpenDataFileSystemWithProgramIndex(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, byte programIndex);
|
||||
Result OpenDataStorageWithProgramIndex(out ReferenceCountedDisposable<IStorageSf> storage, byte programIndex);
|
||||
Result OpenDeviceOperator(out ReferenceCountedDisposable<IDeviceOperator> deviceOperator);
|
||||
|
||||
Result OpenSdCardDetectionEventNotifier(out ReferenceCountedDisposable<IEventNotifier> eventNotifier);
|
||||
Result OpenGameCardDetectionEventNotifier(out ReferenceCountedDisposable<IEventNotifier> eventNotifier);
|
||||
Result OpenSystemDataUpdateEventNotifier(out ReferenceCountedDisposable<IEventNotifier> eventNotifier);
|
||||
Result NotifySystemDataUpdateEvent();
|
||||
Result SimulateDeviceDetectionEvent(SdmmcPort port, SimulatingDeviceDetectionMode mode, bool signalEvent);
|
||||
|
||||
Result QuerySaveDataTotalSize(out long totalSize, long dataSize, long journalSize);
|
||||
Result VerifySaveDataFileSystem(ulong saveDataId, Span<byte> readBuffer);
|
||||
Result VerifySaveDataFileSystem(ulong saveDataId, OutBuffer 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 SetCurrentPosixTimeWithTimeDifference(long time, int difference);
|
||||
Result GetRightsIdByPath(out RightsId rightsId, in FspPath path);
|
||||
Result GetRightsIdAndKeyGenerationByPath(out RightsId rightsId, out byte keyGeneration, in FspPath path);
|
||||
Result SetCurrentPosixTimeWithTimeDifference(long currentTime, int timeDifference);
|
||||
Result GetFreeSpaceSizeForSaveData(out long freeSpaceSize, SaveDataSpaceId spaceId);
|
||||
Result VerifySaveDataFileSystemBySaveDataSpaceId(SaveDataSpaceId spaceId, ulong saveDataId, Span<byte> readBuffer);
|
||||
Result VerifySaveDataFileSystemBySaveDataSpaceId(SaveDataSpaceId spaceId, ulong saveDataId, OutBuffer 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);
|
||||
|
||||
Result RegisterProgramIndexMapInfo(ReadOnlySpan<byte> programIndexMapInfoBuffer, int programCount);
|
||||
Result SetBisRootForHost(BisPartitionId partitionId, ref FsPath path);
|
||||
Result RegisterProgramIndexMapInfo(InBuffer programIndexMapInfoBuffer, int programCount);
|
||||
Result SetBisRootForHost(BisPartitionId partitionId, in FspPath path);
|
||||
Result SetSaveDataSize(long saveDataSize, long saveDataJournalSize);
|
||||
Result SetSaveDataRootPath(ref FsPath path);
|
||||
Result SetSaveDataRootPath(in FspPath path);
|
||||
Result DisableAutoSaveDataCreation();
|
||||
Result SetGlobalAccessLogMode(GlobalAccessLogMode mode);
|
||||
Result GetGlobalAccessLogMode(out GlobalAccessLogMode mode);
|
||||
Result OutputAccessLogToSdCard(U8Span logString);
|
||||
Result OutputAccessLogToSdCard(InBuffer textBuffer);
|
||||
Result RegisterUpdatePartition();
|
||||
Result OpenRegisteredUpdatePartition(out IFileSystem fileSystem);
|
||||
Result OpenRegisteredUpdatePartition(out ReferenceCountedDisposable<IFileSystemSf> fileSystem);
|
||||
|
||||
Result GetProgramIndexForAccessLog(out int programIndex, out int programCount);
|
||||
Result OverrideSaveDataTransferTokenSignVerificationKey(ReadOnlySpan<byte> key);
|
||||
Result UnsetSaveDataRootPath();
|
||||
Result OutputMultiProgramTagAccessLog();
|
||||
Result OverrideSaveDataTransferTokenSignVerificationKey(InBuffer key);
|
||||
Result CorruptSaveDataFileSystemByOffset(SaveDataSpaceId spaceId, ulong saveDataId, long offset);
|
||||
Result OpenMultiCommitManager(out IMultiCommitManager commitManager);
|
||||
Result OpenMultiCommitManager(out ReferenceCountedDisposable<IMultiCommitManager> commitManager);
|
||||
Result OpenBisWiper(out ReferenceCountedDisposable<IWiper> bisWiper, NativeHandle transferMemoryHandle, ulong transferMemorySize);
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
using LibHac.Fs.Fsa;
|
||||
|
||||
namespace LibHac.FsSrv
|
||||
{
|
||||
public interface IMultiCommitManager
|
||||
{
|
||||
Result Add(IFileSystem fileSystem);
|
||||
Result Commit();
|
||||
}
|
||||
}
|
@ -139,10 +139,10 @@ namespace LibHac.FsSrv
|
||||
int GetIndexCount();
|
||||
|
||||
/// <summary>
|
||||
/// Returns an <see cref="ISaveDataInfoReader"/> that iterates through the <see cref="SaveDataIndexer"/>.
|
||||
/// Returns an <see cref="SaveDataInfoReaderImpl"/> that iterates through the <see cref="SaveDataIndexer"/>.
|
||||
/// </summary>
|
||||
/// <param name="infoReader">If the method returns successfully, contains the created <see cref="ISaveDataInfoReader"/>.</param>
|
||||
/// <param name="infoReader">If the method returns successfully, contains the created <see cref="SaveDataInfoReaderImpl"/>.</param>
|
||||
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||
Result OpenSaveDataInfoReader(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader);
|
||||
Result OpenSaveDataInfoReader(out ReferenceCountedDisposable<SaveDataInfoReaderImpl> infoReader);
|
||||
}
|
||||
}
|
@ -4,8 +4,8 @@ namespace LibHac.FsSrv
|
||||
{
|
||||
public interface ISaveDataIndexerManager
|
||||
{
|
||||
Result OpenAccessor(out SaveDataIndexerAccessor accessor, out bool neededInit, SaveDataSpaceId spaceId);
|
||||
void ResetTemporaryStorageIndexer(SaveDataSpaceId spaceId);
|
||||
void InvalidateSdCardIndexer(SaveDataSpaceId spaceId);
|
||||
Result OpenSaveDataIndexerAccessor(out SaveDataIndexerAccessor accessor, out bool neededInit, SaveDataSpaceId spaceId);
|
||||
void ResetIndexer(SaveDataSpaceId spaceId);
|
||||
void InvalidateIndexer(SaveDataSpaceId spaceId);
|
||||
}
|
||||
}
|
@ -830,6 +830,7 @@ namespace LibHac.FsSrv.Impl
|
||||
RegisterProgramIndexMapInfo,
|
||||
ChallengeCardExistence,
|
||||
CreateOwnSaveData,
|
||||
DeleteOwnSaveData,
|
||||
ReadOwnSaveDataFileSystemExtraData,
|
||||
ExtendOwnSaveData,
|
||||
OpenOwnSaveDataTransferProhibiter,
|
||||
|
27
src/LibHac/FsSrv/Impl/AsynchronousAccessFileSystem.cs
Normal file
27
src/LibHac/FsSrv/Impl/AsynchronousAccessFileSystem.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSystem;
|
||||
|
||||
namespace LibHac.FsSrv.Impl
|
||||
{
|
||||
public class AsynchronousAccessFileSystem : ForwardingFileSystem
|
||||
{
|
||||
protected AsynchronousAccessFileSystem(ref ReferenceCountedDisposable<IFileSystem> baseFileSystem) : base(
|
||||
ref baseFileSystem)
|
||||
{ }
|
||||
|
||||
public static ReferenceCountedDisposable<IFileSystem> CreateShared(
|
||||
ref ReferenceCountedDisposable<IFileSystem> fileSystem)
|
||||
{
|
||||
return new ReferenceCountedDisposable<IFileSystem>(new AsynchronousAccessFileSystem(ref fileSystem));
|
||||
}
|
||||
|
||||
// ReSharper disable once RedundantOverriddenMember
|
||||
protected override Result DoOpenFile(out IFile file, U8Span path, OpenMode mode)
|
||||
{
|
||||
// Todo: Implement
|
||||
return base.DoOpenFile(out file, path, mode);
|
||||
}
|
||||
}
|
||||
}
|
33
src/LibHac/FsSrv/Impl/BisWiper.cs
Normal file
33
src/LibHac/FsSrv/Impl/BisWiper.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.Sf;
|
||||
|
||||
namespace LibHac.FsSrv.Impl
|
||||
{
|
||||
internal class BisWiper : IWiper
|
||||
{
|
||||
// ReSharper disable UnusedParameter.Local
|
||||
public BisWiper(NativeHandle memoryHandle, ulong memorySize) { }
|
||||
// ReSharper restore UnusedParameter.Local
|
||||
|
||||
public Result Startup(out long spaceToWipe)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result Process(out long remainingSpaceToWipe)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static Result CreateWiper(out IWiper wiper, NativeHandle memoryHandle, ulong memorySize)
|
||||
{
|
||||
wiper = new BisWiper(memoryHandle, memorySize);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
58
src/LibHac/FsSrv/Impl/DeepRetryFileSystem.cs
Normal file
58
src/LibHac/FsSrv/Impl/DeepRetryFileSystem.cs
Normal file
@ -0,0 +1,58 @@
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSystem;
|
||||
|
||||
namespace LibHac.FsSrv.Impl
|
||||
{
|
||||
public class DeepRetryFileSystem : ForwardingFileSystem
|
||||
{
|
||||
private ReferenceCountedDisposable<DeepRetryFileSystem>.WeakReference SelfReference { get; set; }
|
||||
private ReferenceCountedDisposable<IRomFileSystemAccessFailureManager> AccessFailureManager { get; set; }
|
||||
|
||||
protected DeepRetryFileSystem(ref ReferenceCountedDisposable<IFileSystem> baseFileSystem,
|
||||
ref ReferenceCountedDisposable<IRomFileSystemAccessFailureManager> accessFailureManager) : base(
|
||||
ref baseFileSystem)
|
||||
{
|
||||
AccessFailureManager = Shared.Move(ref accessFailureManager);
|
||||
}
|
||||
|
||||
public static ReferenceCountedDisposable<IFileSystem> CreateShared(
|
||||
ref ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
ref ReferenceCountedDisposable<IRomFileSystemAccessFailureManager> accessFailureManager)
|
||||
{
|
||||
ReferenceCountedDisposable<DeepRetryFileSystem> sharedRetryFileSystem = null;
|
||||
try
|
||||
{
|
||||
var retryFileSystem = new DeepRetryFileSystem(ref fileSystem, ref accessFailureManager);
|
||||
sharedRetryFileSystem = new ReferenceCountedDisposable<DeepRetryFileSystem>(retryFileSystem);
|
||||
|
||||
retryFileSystem.SelfReference =
|
||||
new ReferenceCountedDisposable<DeepRetryFileSystem>.WeakReference(sharedRetryFileSystem);
|
||||
|
||||
return sharedRetryFileSystem.AddReference<IFileSystem>();
|
||||
}
|
||||
finally
|
||||
{
|
||||
sharedRetryFileSystem?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
AccessFailureManager?.Dispose();
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
// ReSharper disable once RedundantOverriddenMember
|
||||
protected override Result DoOpenFile(out IFile file, U8Span path, OpenMode mode)
|
||||
{
|
||||
// Todo: Implement
|
||||
return base.DoOpenFile(out file, path, mode);
|
||||
}
|
||||
}
|
||||
}
|
65
src/LibHac/FsSrv/Impl/DirectoryInterfaceAdapter.cs
Normal file
65
src/LibHac/FsSrv/Impl/DirectoryInterfaceAdapter.cs
Normal file
@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Sf;
|
||||
using IDirectory = LibHac.Fs.Fsa.IDirectory;
|
||||
using IDirectorySf = LibHac.FsSrv.Sf.IDirectory;
|
||||
|
||||
namespace LibHac.FsSrv.Impl
|
||||
{
|
||||
internal class DirectoryInterfaceAdapter : IDirectorySf
|
||||
{
|
||||
private ReferenceCountedDisposable<FileSystemInterfaceAdapter> ParentFs { get; }
|
||||
private IDirectory BaseDirectory { get; }
|
||||
|
||||
public DirectoryInterfaceAdapter(IDirectory baseDirectory,
|
||||
ref ReferenceCountedDisposable<FileSystemInterfaceAdapter> parentFileSystem)
|
||||
{
|
||||
BaseDirectory = baseDirectory;
|
||||
ParentFs = parentFileSystem;
|
||||
parentFileSystem = null;
|
||||
}
|
||||
|
||||
public Result Read(out long entriesRead, OutBuffer entryBuffer)
|
||||
{
|
||||
const int maxTryCount = 2;
|
||||
entriesRead = default;
|
||||
|
||||
Span<DirectoryEntry> entries = MemoryMarshal.Cast<byte, DirectoryEntry>(entryBuffer.Buffer);
|
||||
|
||||
Result rc = Result.Success;
|
||||
long tmpEntriesRead = 0;
|
||||
|
||||
for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
|
||||
{
|
||||
rc = BaseDirectory.Read(out tmpEntriesRead, entries);
|
||||
|
||||
// Retry on ResultDataCorrupted
|
||||
if (!ResultFs.DataCorrupted.Includes(rc))
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
entriesRead = tmpEntriesRead;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result GetEntryCount(out long entryCount)
|
||||
{
|
||||
entryCount = default;
|
||||
|
||||
Result rc = BaseDirectory.GetEntryCount(out long tmpEntryCount);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
entryCount = tmpEntryCount;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
BaseDirectory?.Dispose();
|
||||
ParentFs?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
131
src/LibHac/FsSrv/Impl/FileInterfaceAdapter.cs
Normal file
131
src/LibHac/FsSrv/Impl/FileInterfaceAdapter.cs
Normal file
@ -0,0 +1,131 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using IFile = LibHac.Fs.Fsa.IFile;
|
||||
using IFileSf = LibHac.FsSrv.Sf.IFile;
|
||||
|
||||
namespace LibHac.FsSrv.Impl
|
||||
{
|
||||
internal class FileInterfaceAdapter : IFileSf
|
||||
{
|
||||
private ReferenceCountedDisposable<FileSystemInterfaceAdapter> ParentFs { get; }
|
||||
private IFile BaseFile { get; }
|
||||
|
||||
public FileInterfaceAdapter(IFile baseFile,
|
||||
ref ReferenceCountedDisposable<FileSystemInterfaceAdapter> parentFileSystem)
|
||||
{
|
||||
BaseFile = baseFile;
|
||||
ParentFs = parentFileSystem;
|
||||
parentFileSystem = null;
|
||||
}
|
||||
|
||||
public Result Read(out long bytesRead, long offset, Span<byte> destination, ReadOption option)
|
||||
{
|
||||
const int maxTryCount = 2;
|
||||
bytesRead = default;
|
||||
|
||||
if (offset < 0)
|
||||
return ResultFs.InvalidOffset.Log();
|
||||
|
||||
if (destination.Length < 0)
|
||||
return ResultFs.InvalidSize.Log();
|
||||
|
||||
Result rc = Result.Success;
|
||||
long tmpBytesRead = 0;
|
||||
|
||||
for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
|
||||
{
|
||||
rc = BaseFile.Read(out tmpBytesRead, offset, destination, option);
|
||||
|
||||
// Retry on ResultDataCorrupted
|
||||
if (!ResultFs.DataCorrupted.Includes(rc))
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
bytesRead = tmpBytesRead;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result Write(long offset, ReadOnlySpan<byte> source, WriteOption option)
|
||||
{
|
||||
if (offset < 0)
|
||||
return ResultFs.InvalidOffset.Log();
|
||||
|
||||
if (source.Length < 0)
|
||||
return ResultFs.InvalidSize.Log();
|
||||
|
||||
// Note: Thread priority is temporarily when writing in FS
|
||||
|
||||
return BaseFile.Write(offset, source, option);
|
||||
}
|
||||
|
||||
public Result Flush()
|
||||
{
|
||||
return BaseFile.Flush();
|
||||
}
|
||||
|
||||
public Result SetSize(long size)
|
||||
{
|
||||
if (size < 0)
|
||||
return ResultFs.InvalidSize.Log();
|
||||
|
||||
return BaseFile.SetSize(size);
|
||||
}
|
||||
|
||||
public Result GetSize(out long size)
|
||||
{
|
||||
const int maxTryCount = 2;
|
||||
size = default;
|
||||
|
||||
Result rc = Result.Success;
|
||||
long tmpSize = 0;
|
||||
|
||||
for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
|
||||
{
|
||||
rc = BaseFile.GetSize(out tmpSize);
|
||||
|
||||
// Retry on ResultDataCorrupted
|
||||
if (!ResultFs.DataCorrupted.Includes(rc))
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
size = tmpSize;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result OperateRange(out QueryRangeInfo rangeInfo, int operationId, long offset, long size)
|
||||
{
|
||||
rangeInfo = new QueryRangeInfo();
|
||||
|
||||
if (operationId == (int)OperationId.InvalidateCache)
|
||||
{
|
||||
Result rc = BaseFile.OperateRange(Span<byte>.Empty, OperationId.InvalidateCache, offset, size,
|
||||
ReadOnlySpan<byte>.Empty);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
else if (operationId == (int)OperationId.QueryRange)
|
||||
{
|
||||
Unsafe.SkipInit(out QueryRangeInfo info);
|
||||
|
||||
Result rc = BaseFile.OperateRange(SpanHelpers.AsByteSpan(ref info), OperationId.QueryRange, offset, size,
|
||||
ReadOnlySpan<byte>.Empty);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
rangeInfo.Merge(in info);
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
BaseFile?.Dispose();
|
||||
ParentFs?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
292
src/LibHac/FsSrv/Impl/FileSystemInterfaceAdapter.cs
Normal file
292
src/LibHac/FsSrv/Impl/FileSystemInterfaceAdapter.cs
Normal file
@ -0,0 +1,292 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.Util;
|
||||
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
|
||||
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||
using IFileSf = LibHac.FsSrv.Sf.IFile;
|
||||
using IDirectorySf = LibHac.FsSrv.Sf.IDirectory;
|
||||
|
||||
namespace LibHac.FsSrv.Impl
|
||||
{
|
||||
internal class FileSystemInterfaceAdapter : IFileSystemSf
|
||||
{
|
||||
private ReferenceCountedDisposable<IFileSystem> BaseFileSystem { get; }
|
||||
private bool IsHostFsRoot { get; }
|
||||
|
||||
// In FS, FileSystemInterfaceAdapter is derived from ISharedObject, so that's used for ref-counting when
|
||||
// creating files and directories. We don't have an ISharedObject, so a self-reference is used instead.
|
||||
private ReferenceCountedDisposable<FileSystemInterfaceAdapter>.WeakReference _selfReference;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="FileSystemInterfaceAdapter"/> by creating
|
||||
/// a new reference to <paramref name="fileSystem"/>.
|
||||
/// </summary>
|
||||
/// <param name="fileSystem">The base file system.</param>
|
||||
/// <param name="isHostFsRoot">Does the base file system come from the root directory of a host file system?</param>
|
||||
private FileSystemInterfaceAdapter(ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
bool isHostFsRoot = false)
|
||||
{
|
||||
BaseFileSystem = fileSystem.AddReference();
|
||||
IsHostFsRoot = isHostFsRoot;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="FileSystemInterfaceAdapter"/> by moving the file system object.
|
||||
/// Avoids allocations from incrementing and then decrementing the ref-count.
|
||||
/// </summary>
|
||||
/// <param name="fileSystem">The base file system. Will be null upon returning.</param>
|
||||
/// <param name="isHostFsRoot">Does the base file system come from the root directory of a host file system?</param>
|
||||
private FileSystemInterfaceAdapter(ref ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||
bool isHostFsRoot = false)
|
||||
{
|
||||
BaseFileSystem = fileSystem;
|
||||
fileSystem = null;
|
||||
IsHostFsRoot = isHostFsRoot;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="FileSystemInterfaceAdapter"/>, creating a copy of the input file system object.
|
||||
/// </summary>
|
||||
/// <param name="baseFileSystem">The base file system.</param>
|
||||
/// <param name="isHostFsRoot">Does the base file system come from the root directory of a host file system?</param>
|
||||
public static ReferenceCountedDisposable<FileSystemInterfaceAdapter> CreateShared(
|
||||
ReferenceCountedDisposable<IFileSystem> baseFileSystem, bool isHostFsRoot = false)
|
||||
{
|
||||
var adapter = new FileSystemInterfaceAdapter(baseFileSystem, isHostFsRoot);
|
||||
|
||||
return ReferenceCountedDisposable<FileSystemInterfaceAdapter>.Create(adapter, out adapter._selfReference);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="FileSystemInterfaceAdapter"/> cast to an <see cref="IFileSystemSf"/>
|
||||
/// by moving the input file system object. Avoids allocations from incrementing and then decrementing the ref-count.
|
||||
/// </summary>
|
||||
/// <param name="baseFileSystem">The base file system. Will be null upon returning.</param>
|
||||
/// <param name="isHostFsRoot">Does the base file system come from the root directory of a host file system?</param>
|
||||
public static ReferenceCountedDisposable<IFileSystemSf> CreateShared(
|
||||
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, bool isHostFsRoot = false)
|
||||
{
|
||||
var adapter = new FileSystemInterfaceAdapter(ref baseFileSystem, isHostFsRoot);
|
||||
|
||||
return ReferenceCountedDisposable<IFileSystemSf>.Create(adapter, out adapter._selfReference);
|
||||
}
|
||||
|
||||
private static ReadOnlySpan<byte> RootDir => new[] { (byte)'/' };
|
||||
|
||||
public Result GetImpl(out ReferenceCountedDisposable<IFileSystem> fileSystem)
|
||||
{
|
||||
fileSystem = BaseFileSystem.AddReference();
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result CreateFile(in Path path, long size, int option)
|
||||
{
|
||||
if (size < 0)
|
||||
return ResultFs.InvalidSize.Log();
|
||||
|
||||
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
|
||||
if (normalizer.Result.IsFailure()) return normalizer.Result;
|
||||
|
||||
return BaseFileSystem.Target.CreateFile(normalizer.Path, size, (CreateFileOptions)option);
|
||||
}
|
||||
|
||||
public Result DeleteFile(in Path path)
|
||||
{
|
||||
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
|
||||
if (normalizer.Result.IsFailure()) return normalizer.Result;
|
||||
|
||||
return BaseFileSystem.Target.DeleteFile(normalizer.Path);
|
||||
}
|
||||
|
||||
public Result CreateDirectory(in Path path)
|
||||
{
|
||||
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
|
||||
if (normalizer.Result.IsFailure()) return normalizer.Result;
|
||||
|
||||
if (StringUtils.Compare(RootDir, normalizer.Path) != 0)
|
||||
return ResultFs.PathAlreadyExists.Log();
|
||||
|
||||
return BaseFileSystem.Target.CreateDirectory(normalizer.Path);
|
||||
}
|
||||
|
||||
public Result DeleteDirectory(in Path path)
|
||||
{
|
||||
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
|
||||
if (normalizer.Result.IsFailure()) return normalizer.Result;
|
||||
|
||||
if (StringUtils.Compare(RootDir, normalizer.Path) != 0)
|
||||
return ResultFs.DirectoryNotDeletable.Log();
|
||||
|
||||
return BaseFileSystem.Target.DeleteDirectory(normalizer.Path);
|
||||
}
|
||||
|
||||
public Result DeleteDirectoryRecursively(in Path path)
|
||||
{
|
||||
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
|
||||
if (normalizer.Result.IsFailure()) return normalizer.Result;
|
||||
|
||||
if (StringUtils.Compare(RootDir, normalizer.Path) != 0)
|
||||
return ResultFs.DirectoryNotDeletable.Log();
|
||||
|
||||
return BaseFileSystem.Target.DeleteDirectoryRecursively(normalizer.Path);
|
||||
}
|
||||
|
||||
public Result RenameFile(in Path oldPath, in Path newPath)
|
||||
{
|
||||
var normalizerOldPath = new PathNormalizer(new U8Span(oldPath.Str), GetPathNormalizerOption());
|
||||
if (normalizerOldPath.Result.IsFailure()) return normalizerOldPath.Result;
|
||||
|
||||
var normalizerNewPath = new PathNormalizer(new U8Span(newPath.Str), GetPathNormalizerOption());
|
||||
if (normalizerNewPath.Result.IsFailure()) return normalizerNewPath.Result;
|
||||
|
||||
return BaseFileSystem.Target.RenameFile(new U8Span(normalizerOldPath.Path),
|
||||
new U8Span(normalizerNewPath.Path));
|
||||
}
|
||||
|
||||
public Result RenameDirectory(in Path oldPath, in Path newPath)
|
||||
{
|
||||
var normalizerOldPath = new PathNormalizer(new U8Span(oldPath.Str), GetPathNormalizerOption());
|
||||
if (normalizerOldPath.Result.IsFailure()) return normalizerOldPath.Result;
|
||||
|
||||
var normalizerNewPath = new PathNormalizer(new U8Span(newPath.Str), GetPathNormalizerOption());
|
||||
if (normalizerNewPath.Result.IsFailure()) return normalizerNewPath.Result;
|
||||
|
||||
if (PathTool.IsSubpath(normalizerOldPath.Path, normalizerNewPath.Path))
|
||||
return ResultFs.DirectoryNotRenamable.Log();
|
||||
|
||||
return BaseFileSystem.Target.RenameDirectory(normalizerOldPath.Path, normalizerNewPath.Path);
|
||||
}
|
||||
|
||||
public Result GetEntryType(out uint entryType, in Path path)
|
||||
{
|
||||
entryType = default;
|
||||
|
||||
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
|
||||
if (normalizer.Result.IsFailure()) return normalizer.Result;
|
||||
|
||||
ref DirectoryEntryType type = ref Unsafe.As<uint, DirectoryEntryType>(ref entryType);
|
||||
|
||||
return BaseFileSystem.Target.GetEntryType(out type, new U8Span(normalizer.Path));
|
||||
}
|
||||
|
||||
public Result OpenFile(out ReferenceCountedDisposable<IFileSf> file, in Path path, uint mode)
|
||||
{
|
||||
const int maxTryCount = 2;
|
||||
file = default;
|
||||
|
||||
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
|
||||
if (normalizer.Result.IsFailure()) return normalizer.Result;
|
||||
|
||||
Result rc = Result.Success;
|
||||
Fs.Fsa.IFile fileInterface = null;
|
||||
|
||||
for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
|
||||
{
|
||||
rc = BaseFileSystem.Target.OpenFile(out fileInterface, new U8Span(normalizer.Path), (OpenMode)mode);
|
||||
|
||||
// Retry on ResultDataCorrupted
|
||||
if (!ResultFs.DataCorrupted.Includes(rc))
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
ReferenceCountedDisposable<FileSystemInterfaceAdapter> selfReference = _selfReference.TryAddReference();
|
||||
var adapter = new FileInterfaceAdapter(fileInterface, ref selfReference);
|
||||
file = new ReferenceCountedDisposable<IFileSf>(adapter);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result OpenDirectory(out ReferenceCountedDisposable<IDirectorySf> directory, in Path path, uint mode)
|
||||
{
|
||||
const int maxTryCount = 2;
|
||||
directory = default;
|
||||
|
||||
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
|
||||
if (normalizer.Result.IsFailure()) return normalizer.Result;
|
||||
|
||||
Result rc = Result.Success;
|
||||
Fs.Fsa.IDirectory dirInterface = null;
|
||||
|
||||
for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
|
||||
{
|
||||
rc = BaseFileSystem.Target.OpenDirectory(out dirInterface, new U8Span(normalizer.Path), (OpenDirectoryMode)mode);
|
||||
|
||||
// Retry on ResultDataCorrupted
|
||||
if (!ResultFs.DataCorrupted.Includes(rc))
|
||||
break;
|
||||
}
|
||||
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
ReferenceCountedDisposable<FileSystemInterfaceAdapter> selfReference = _selfReference.TryAddReference();
|
||||
var adapter = new DirectoryInterfaceAdapter(dirInterface, ref selfReference);
|
||||
directory = new ReferenceCountedDisposable<IDirectorySf>(adapter);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result Commit()
|
||||
{
|
||||
return BaseFileSystem.Target.Commit();
|
||||
}
|
||||
|
||||
public Result GetFreeSpaceSize(out long freeSpace, in Path path)
|
||||
{
|
||||
freeSpace = default;
|
||||
|
||||
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
|
||||
if (normalizer.Result.IsFailure()) return normalizer.Result;
|
||||
|
||||
return BaseFileSystem.Target.GetFreeSpaceSize(out freeSpace, normalizer.Path);
|
||||
}
|
||||
|
||||
public Result GetTotalSpaceSize(out long totalSpace, in Path path)
|
||||
{
|
||||
totalSpace = default;
|
||||
|
||||
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
|
||||
if (normalizer.Result.IsFailure()) return normalizer.Result;
|
||||
|
||||
return BaseFileSystem.Target.GetTotalSpaceSize(out totalSpace, normalizer.Path);
|
||||
}
|
||||
|
||||
public Result CleanDirectoryRecursively(in Path path)
|
||||
{
|
||||
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
|
||||
if (normalizer.Result.IsFailure()) return normalizer.Result;
|
||||
|
||||
return BaseFileSystem.Target.CleanDirectoryRecursively(normalizer.Path);
|
||||
}
|
||||
|
||||
public Result GetFileTimeStampRaw(out FileTimeStampRaw timeStamp, in Path path)
|
||||
{
|
||||
timeStamp = default;
|
||||
|
||||
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
|
||||
if (normalizer.Result.IsFailure()) return normalizer.Result;
|
||||
|
||||
return BaseFileSystem.Target.GetFileTimeStampRaw(out timeStamp, normalizer.Path);
|
||||
}
|
||||
|
||||
public Result QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, int queryId, in Path path)
|
||||
{
|
||||
return BaseFileSystem.Target.QueryEntry(outBuffer, inBuffer, (QueryId)queryId, new U8Span(path.Str));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
BaseFileSystem?.Dispose();
|
||||
}
|
||||
|
||||
private PathNormalizer.Option GetPathNormalizerOption()
|
||||
{
|
||||
return IsHostFsRoot ? PathNormalizer.Option.PreserveUnc : PathNormalizer.Option.None;
|
||||
}
|
||||
}
|
||||
}
|
10
src/LibHac/FsSrv/Impl/IEntryOpenCountSemaphoreManager.cs
Normal file
10
src/LibHac/FsSrv/Impl/IEntryOpenCountSemaphoreManager.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System;
|
||||
using LibHac.FsSystem;
|
||||
|
||||
namespace LibHac.FsSrv.Impl
|
||||
{
|
||||
public interface IEntryOpenCountSemaphoreManager : IDisposable
|
||||
{
|
||||
Result TryAcquireEntryOpenCountSemaphore(out IUniqueLock semaphore);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user