mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2025-02-09 13:14:46 +01:00
Separate fssrv save code to SaveDataFileSystemService
This commit is contained in:
parent
3837ed7eea
commit
882e6bc937
@ -1 +1 @@
|
|||||||
3.1.401
|
3.1.402
|
@ -267,6 +267,7 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
|
|||||||
2,6203,,InvalidOpenModeForWrite,
|
2,6203,,InvalidOpenModeForWrite,
|
||||||
|
|
||||||
2,6300,6399,UnsupportedOperation,
|
2,6300,6399,UnsupportedOperation,
|
||||||
|
2,6301,,UnsupportedCommitTarget,
|
||||||
2,6302,,UnsupportedOperationInSubStorageSetSize,Attempted to resize a non-resizable SubStorage.
|
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,6303,,UnsupportedOperationInResizableSubStorageSetSize,Attempted to resize a SubStorage that wasn't located at the end of the base storage.
|
||||||
2,6304,,UnsupportedOperationInMemoryStorageSetSize,
|
2,6304,,UnsupportedOperationInMemoryStorageSetSize,
|
||||||
@ -297,10 +298,15 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
|
|||||||
|
|
||||||
2,6452,,ExternalKeyAlreadyRegistered,
|
2,6452,,ExternalKeyAlreadyRegistered,
|
||||||
2,6454,,WriteStateUnflushed,
|
2,6454,,WriteStateUnflushed,
|
||||||
|
2,6456,,DirectoryNotClosed,
|
||||||
2,6457,,WriteModeFileNotClosed,
|
2,6457,,WriteModeFileNotClosed,
|
||||||
|
2,6458,,AllocatorAlreadyRegistered,
|
||||||
|
2,6459,,DefaultAllocatorUsed,
|
||||||
2,6461,,AllocatorAlignmentViolation,
|
2,6461,,AllocatorAlignmentViolation,
|
||||||
2,6463,,MultiCommitFileSystemAlreadyAdded,The provided file system has already been added to the multi-commit manager.
|
2,6463,,MultiCommitFileSystemAlreadyAdded,The provided file system has already been added to the multi-commit manager.
|
||||||
2,6465,,UserNotExist,
|
2,6465,,UserNotExist,
|
||||||
|
2,6466,,DefaultGlobalFileDataCacheEnabled,
|
||||||
|
2,6467,,SaveDataRootPathUnavailable,
|
||||||
|
|
||||||
2,6600,6699,EntryNotFound,
|
2,6600,6699,EntryNotFound,
|
||||||
2,6605,,TargetProgramNotFound,Specified program is not found in the program registry.
|
2,6605,,TargetProgramNotFound,Specified program is not found in the program registry.
|
||||||
|
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.Bootloader => 0,
|
||||||
Package1Section.SecureMonitor => 1,
|
Package1Section.SecureMonitor => 1,
|
||||||
Package1Section.WarmBoot => 2,
|
Package1Section.WarmBoot => 2,
|
||||||
_ => -1,
|
_ => -1
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -513,7 +513,7 @@ namespace LibHac.Boot
|
|||||||
Package1Section.Bootloader => 1,
|
Package1Section.Bootloader => 1,
|
||||||
Package1Section.SecureMonitor => 2,
|
Package1Section.SecureMonitor => 2,
|
||||||
Package1Section.WarmBoot => 0,
|
Package1Section.WarmBoot => 0,
|
||||||
_ => -1,
|
_ => -1
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -522,7 +522,7 @@ namespace LibHac.Boot
|
|||||||
Package1Section.Bootloader => 1,
|
Package1Section.Bootloader => 1,
|
||||||
Package1Section.SecureMonitor => 0,
|
Package1Section.SecureMonitor => 0,
|
||||||
Package1Section.WarmBoot => 2,
|
Package1Section.WarmBoot => 2,
|
||||||
_ => -1,
|
_ => -1
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ namespace LibHac.Common.Keys
|
|||||||
CommonSeedDiff = Common | Seed | DifferentDev,
|
CommonSeedDiff = Common | Seed | DifferentDev,
|
||||||
CommonDrvd = Common | Derived,
|
CommonDrvd = Common | Derived,
|
||||||
DeviceRoot = Device | Root,
|
DeviceRoot = Device | Root,
|
||||||
DeviceDrvd = Device | Derived,
|
DeviceDrvd = Device | Derived
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly string Name;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
|
using LibHac.FsSrv.Sf;
|
||||||
|
|
||||||
namespace LibHac.Fs.Accessors
|
namespace LibHac.Fs.Accessors
|
||||||
{
|
{
|
||||||
@ -20,10 +21,13 @@ namespace LibHac.Fs.Accessors
|
|||||||
private readonly object _locker = new object();
|
private readonly object _locker = new object();
|
||||||
|
|
||||||
internal bool IsAccessLogEnabled { get; set; }
|
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;
|
FileSystem = baseFileSystem;
|
||||||
FsClient = fsClient;
|
FsClient = fsClient;
|
||||||
MountNameGenerator = nameGenerator;
|
MountNameGenerator = nameGenerator;
|
||||||
@ -147,6 +151,11 @@ namespace LibHac.Fs.Accessors
|
|||||||
return MountNameGenerator.GenerateCommonMountName(nameBuffer);
|
return MountNameGenerator.GenerateCommonMountName(nameBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ReferenceCountedDisposable<IFileSystemSf> GetMultiCommitTarget()
|
||||||
|
{
|
||||||
|
return MultiCommitTarget?.GetMultiCommitTarget();
|
||||||
|
}
|
||||||
|
|
||||||
internal void NotifyCloseFile(FileAccessor file)
|
internal void NotifyCloseFile(FileAccessor file)
|
||||||
{
|
{
|
||||||
lock (_locker)
|
lock (_locker)
|
||||||
|
@ -232,7 +232,7 @@ namespace LibHac.Fs
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Result EnsureApplicationCacheStorageImpl(this FileSystemClient fs, out long requiredSize,
|
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)
|
long dataSize, long journalSize, bool allowExisting)
|
||||||
{
|
{
|
||||||
requiredSize = default;
|
requiredSize = default;
|
||||||
@ -299,7 +299,7 @@ namespace LibHac.Fs
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Result EnsureApplicationCacheStorage(this FileSystemClient fs, out long requiredSize,
|
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)
|
long dataSize, long journalSize, bool allowExisting)
|
||||||
{
|
{
|
||||||
return EnsureApplicationCacheStorageImpl(fs, out requiredSize, out target, applicationId, saveDataOwnerId,
|
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,
|
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)
|
long dataSize, long journalSize, bool allowExisting)
|
||||||
{
|
{
|
||||||
requiredSize = default;
|
requiredSize = default;
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
using LibHac.Common;
|
using System;
|
||||||
|
using LibHac.Common;
|
||||||
|
|
||||||
namespace LibHac.Fs
|
namespace LibHac.Fs
|
||||||
{
|
{
|
||||||
internal static class CommonMountNames
|
internal static class CommonPaths
|
||||||
{
|
{
|
||||||
public const char ReservedMountNamePrefixCharacter = '@';
|
public const char ReservedMountNamePrefixCharacter = '@';
|
||||||
|
|
||||||
@ -21,5 +22,11 @@ namespace LibHac.Fs
|
|||||||
public const char GameCardFileSystemMountNameUpdateSuffix = 'U';
|
public const char GameCardFileSystemMountNameUpdateSuffix = 'U';
|
||||||
public const char GameCardFileSystemMountNameNormalSuffix = 'N';
|
public const char GameCardFileSystemMountNameNormalSuffix = 'N';
|
||||||
public const char GameCardFileSystemMountNameSecureSuffix = 'S';
|
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'
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -134,7 +134,13 @@ namespace LibHac.Fs
|
|||||||
|
|
||||||
public Result Register(U8Span mountName, IFileSystem fileSystem, ICommonMountNameGenerator nameGenerator)
|
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);
|
Result rc = MountTable.Mount(accessor);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
@ -173,8 +179,8 @@ namespace LibHac.Fs
|
|||||||
if (path.IsNull())
|
if (path.IsNull())
|
||||||
return ResultFs.NullptrArgument.Log();
|
return ResultFs.NullptrArgument.Log();
|
||||||
|
|
||||||
int hostMountNameLen = StringUtils.GetLength(CommonMountNames.HostRootFileSystemMountName);
|
int hostMountNameLen = StringUtils.GetLength(CommonPaths.HostRootFileSystemMountName);
|
||||||
if (StringUtils.Compare(path, CommonMountNames.HostRootFileSystemMountName, hostMountNameLen) == 0)
|
if (StringUtils.Compare(path, CommonPaths.HostRootFileSystemMountName, hostMountNameLen) == 0)
|
||||||
{
|
{
|
||||||
return ResultFs.NotMounted.Log();
|
return ResultFs.NotMounted.Log();
|
||||||
}
|
}
|
||||||
@ -195,7 +201,7 @@ namespace LibHac.Fs
|
|||||||
|
|
||||||
if (PathUtility.IsWindowsDrive(path) || PathUtility.IsUnc(path))
|
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;
|
mountName.Name[PathTools.MountNameLengthMax] = StringTraits.NullTerminator;
|
||||||
|
|
||||||
subPath = path;
|
subPath = path;
|
||||||
|
@ -96,10 +96,11 @@ namespace LibHac.Fs
|
|||||||
public enum SaveDataState : byte
|
public enum SaveDataState : byte
|
||||||
{
|
{
|
||||||
Normal = 0,
|
Normal = 0,
|
||||||
Creating = 1,
|
Processing = 1,
|
||||||
State2 = 2,
|
State2 = 2,
|
||||||
MarkedForDeletion = 3,
|
MarkedForDeletion = 3,
|
||||||
Extending = 4
|
Extending = 4,
|
||||||
|
ImportSuspended = 5
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ImageDirectoryId
|
public enum ImageDirectoryId
|
||||||
@ -165,7 +166,8 @@ namespace LibHac.Fs
|
|||||||
KeepAfterResettingSystemSaveData = 1 << 0,
|
KeepAfterResettingSystemSaveData = 1 << 0,
|
||||||
KeepAfterRefurbishment = 1 << 1,
|
KeepAfterRefurbishment = 1 << 1,
|
||||||
KeepAfterResettingSystemSaveDataWithoutUserSaveData = 1 << 2,
|
KeepAfterResettingSystemSaveDataWithoutUserSaveData = 1 << 2,
|
||||||
NeedsSecureDelete = 1 << 3
|
NeedsSecureDelete = 1 << 3,
|
||||||
|
Restore = 1 << 4
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum SdmmcPort
|
public enum SdmmcPort
|
||||||
|
@ -37,6 +37,31 @@ namespace LibHac.Fs.Fsa
|
|||||||
return DoCreateFile(path, size, option);
|
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>
|
/// <summary>
|
||||||
/// Deletes the specified file.
|
/// Deletes the specified file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -129,11 +129,19 @@ namespace LibHac.Fs.Impl
|
|||||||
Result rc = GetPathForServiceObject(out Path sfPath, path);
|
Result rc = GetPathForServiceObject(out Path sfPath, path);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = BaseFs.Target.OpenFile(out ReferenceCountedDisposable<IFileSf> sfFile, in sfPath, (uint)mode);
|
ReferenceCountedDisposable<IFileSf> sfFile = null;
|
||||||
if (rc.IsFailure()) return rc;
|
try
|
||||||
|
{
|
||||||
|
rc = BaseFs.Target.OpenFile(out sfFile, in sfPath, (uint)mode);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
file = new FileServiceObjectAdapter(sfFile);
|
file = new FileServiceObjectAdapter(sfFile);
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
sfFile?.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Result DoOpenDirectory(out IDirectory directory, U8Span path, OpenDirectoryMode mode)
|
protected override Result DoOpenDirectory(out IDirectory directory, U8Span path, OpenDirectoryMode mode)
|
||||||
@ -143,11 +151,19 @@ namespace LibHac.Fs.Impl
|
|||||||
Result rc = GetPathForServiceObject(out Path sfPath, path);
|
Result rc = GetPathForServiceObject(out Path sfPath, path);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = BaseFs.Target.OpenDirectory(out ReferenceCountedDisposable<IDirectorySf> sfDir, in sfPath, (uint)mode);
|
ReferenceCountedDisposable<IDirectorySf> sfDir = null;
|
||||||
if (rc.IsFailure()) return rc;
|
try
|
||||||
|
{
|
||||||
|
rc = BaseFs.Target.OpenDirectory(out sfDir, in sfPath, (uint)mode);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
directory = new DirectoryServiceObjectAdapter(sfDir);
|
directory = new DirectoryServiceObjectAdapter(sfDir);
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
sfDir?.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Result DoCommit()
|
protected override Result DoCommit()
|
||||||
@ -175,7 +191,7 @@ namespace LibHac.Fs.Impl
|
|||||||
|
|
||||||
public ReferenceCountedDisposable<IFileSystemSf> GetMultiCommitTarget()
|
public ReferenceCountedDisposable<IFileSystemSf> GetMultiCommitTarget()
|
||||||
{
|
{
|
||||||
return BaseFs;
|
return BaseFs.AddReference();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using static LibHac.Fs.CommonMountNames;
|
using static LibHac.Fs.CommonPaths;
|
||||||
|
|
||||||
namespace LibHac.Fs
|
namespace LibHac.Fs
|
||||||
{
|
{
|
||||||
|
@ -25,7 +25,7 @@ namespace LibHac.Fs
|
|||||||
{
|
{
|
||||||
InternalKeyForSoftwareAes = 1 << 0,
|
InternalKeyForSoftwareAes = 1 << 0,
|
||||||
InternalKeyForHardwareAes = 1 << 1,
|
InternalKeyForHardwareAes = 1 << 1,
|
||||||
ExternalKeyForHardwareAes = 1 << 2,
|
ExternalKeyForHardwareAes = 1 << 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -423,6 +423,8 @@ namespace LibHac.Fs
|
|||||||
|
|
||||||
/// <summary>Error code: 2002-6300; Range: 6300-6399; Inner value: 0x313802</summary>
|
/// <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); }
|
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>
|
/// <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);
|
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>
|
/// <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>
|
||||||
@ -481,14 +483,24 @@ namespace LibHac.Fs
|
|||||||
public static Result.Base ExternalKeyAlreadyRegistered => new Result.Base(ModuleFs, 6452);
|
public static Result.Base ExternalKeyAlreadyRegistered => new Result.Base(ModuleFs, 6452);
|
||||||
/// <summary>Error code: 2002-6454; Inner value: 0x326c02</summary>
|
/// <summary>Error code: 2002-6454; Inner value: 0x326c02</summary>
|
||||||
public static Result.Base WriteStateUnflushed => new Result.Base(ModuleFs, 6454);
|
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>
|
/// <summary>Error code: 2002-6457; Inner value: 0x327202</summary>
|
||||||
public static Result.Base WriteModeFileNotClosed => new Result.Base(ModuleFs, 6457);
|
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>
|
/// <summary>Error code: 2002-6461; Inner value: 0x327a02</summary>
|
||||||
public static Result.Base AllocatorAlignmentViolation => new Result.Base(ModuleFs, 6461);
|
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>
|
/// <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);
|
public static Result.Base MultiCommitFileSystemAlreadyAdded => new Result.Base(ModuleFs, 6463);
|
||||||
/// <summary>Error code: 2002-6465; Inner value: 0x328202</summary>
|
/// <summary>Error code: 2002-6465; Inner value: 0x328202</summary>
|
||||||
public static Result.Base UserNotExist => new Result.Base(ModuleFs, 6465);
|
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>
|
/// <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); }
|
public static Result.Base EntryNotFound { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6600, 6699); }
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Ncm;
|
using LibHac.Ncm;
|
||||||
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.Fs
|
namespace LibHac.Fs
|
||||||
{
|
{
|
||||||
@ -13,17 +15,17 @@ namespace LibHac.Fs
|
|||||||
[FieldOffset(0x18)] public ulong StaticSaveDataId;
|
[FieldOffset(0x18)] public ulong StaticSaveDataId;
|
||||||
[FieldOffset(0x20)] public SaveDataType Type;
|
[FieldOffset(0x20)] public SaveDataType Type;
|
||||||
[FieldOffset(0x21)] public SaveDataRank Rank;
|
[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(
|
public SaveDataAttribute(ProgramId programId, SaveDataType type, UserId userId, ulong saveDataId) : this(
|
||||||
programId, type, userId, saveDataId, 0, SaveDataRank.Primary)
|
programId, type, userId, saveDataId, 0, SaveDataRank.Primary)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
public SaveDataAttribute(ProgramId programId, SaveDataType type, UserId userId, ulong saveDataId,
|
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)
|
SaveDataRank rank)
|
||||||
{
|
{
|
||||||
ProgramId = programId;
|
ProgramId = programId;
|
||||||
@ -34,6 +36,38 @@ namespace LibHac.Fs
|
|||||||
Rank = rank;
|
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)
|
public override readonly bool Equals(object obj)
|
||||||
{
|
{
|
||||||
return obj is SaveDataAttribute attribute && Equals(attribute);
|
return obj is SaveDataAttribute attribute && Equals(attribute);
|
||||||
@ -85,40 +119,96 @@ namespace LibHac.Fs
|
|||||||
[FieldOffset(0x04)] public bool FilterByIndex;
|
[FieldOffset(0x04)] public bool FilterByIndex;
|
||||||
[FieldOffset(0x05)] public SaveDataRank Rank;
|
[FieldOffset(0x05)] public SaveDataRank Rank;
|
||||||
|
|
||||||
[FieldOffset(0x08)] public ProgramId ProgramId;
|
[FieldOffset(0x08)] public SaveDataAttribute Attribute;
|
||||||
[FieldOffset(0x10)] public UserId UserId;
|
|
||||||
[FieldOffset(0x20)] public ulong SaveDataId;
|
|
||||||
[FieldOffset(0x28)] public SaveDataType SaveDataType;
|
|
||||||
[FieldOffset(0x2A)] public short Index;
|
|
||||||
|
|
||||||
public void SetProgramId(ProgramId value)
|
public void SetProgramId(ProgramId value)
|
||||||
{
|
{
|
||||||
FilterByProgramId = true;
|
FilterByProgramId = true;
|
||||||
ProgramId = value;
|
Attribute.ProgramId = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetSaveDataType(SaveDataType value)
|
public void SetSaveDataType(SaveDataType value)
|
||||||
{
|
{
|
||||||
FilterBySaveDataType = true;
|
FilterBySaveDataType = true;
|
||||||
SaveDataType = value;
|
Attribute.Type = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetUserId(UserId value)
|
public void SetUserId(UserId value)
|
||||||
{
|
{
|
||||||
FilterByUserId = true;
|
FilterByUserId = true;
|
||||||
UserId = value;
|
Attribute.UserId = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetSaveDataId(ulong value)
|
public void SetSaveDataId(ulong value)
|
||||||
{
|
{
|
||||||
FilterBySaveDataId = true;
|
FilterBySaveDataId = true;
|
||||||
SaveDataId = value;
|
Attribute.StaticSaveDataId = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetIndex(short value)
|
public void SetIndex(ushort value)
|
||||||
{
|
{
|
||||||
FilterByIndex = true;
|
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;
|
[FieldOffset(0x00)] private byte _hashStart;
|
||||||
|
|
||||||
public Span<byte> Hash => SpanHelpers.CreateSpan(ref _hashStart, HashLength);
|
public Span<byte> Hash => SpanHelpers.CreateSpan(ref _hashStart, HashLength);
|
||||||
|
public ReadOnlySpan<byte> HashRo => SpanHelpers.CreateReadOnlySpan(in _hashStart, HashLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
@ -140,7 +231,7 @@ namespace LibHac.Fs
|
|||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x10)]
|
[StructLayout(LayoutKind.Explicit, Size = 0x10)]
|
||||||
public struct SaveMetaCreateInfo
|
public struct SaveDataMetaInfo
|
||||||
{
|
{
|
||||||
[FieldOffset(0)] public int Size;
|
[FieldOffset(0)] public int Size;
|
||||||
[FieldOffset(4)] public SaveDataMetaType Type;
|
[FieldOffset(4)] public SaveDataMetaType Type;
|
||||||
@ -151,7 +242,7 @@ namespace LibHac.Fs
|
|||||||
{
|
{
|
||||||
[FieldOffset(0x00)] public long Size;
|
[FieldOffset(0x00)] public long Size;
|
||||||
[FieldOffset(0x08)] public long JournalSize;
|
[FieldOffset(0x08)] public long JournalSize;
|
||||||
[FieldOffset(0x10)] public ulong BlockSize;
|
[FieldOffset(0x10)] public long BlockSize;
|
||||||
[FieldOffset(0x18)] public ulong OwnerId;
|
[FieldOffset(0x18)] public ulong OwnerId;
|
||||||
[FieldOffset(0x20)] public SaveDataFlags Flags;
|
[FieldOffset(0x20)] public SaveDataFlags Flags;
|
||||||
[FieldOffset(0x24)] public SaveDataSpaceId SpaceId;
|
[FieldOffset(0x24)] public SaveDataSpaceId SpaceId;
|
||||||
@ -168,8 +259,66 @@ namespace LibHac.Fs
|
|||||||
[FieldOffset(0x20)] public ulong StaticSaveDataId;
|
[FieldOffset(0x20)] public ulong StaticSaveDataId;
|
||||||
[FieldOffset(0x28)] public ProgramId ProgramId;
|
[FieldOffset(0x28)] public ProgramId ProgramId;
|
||||||
[FieldOffset(0x30)] public long Size;
|
[FieldOffset(0x30)] public long Size;
|
||||||
[FieldOffset(0x38)] public short Index;
|
[FieldOffset(0x38)] public ushort Index;
|
||||||
[FieldOffset(0x3A)] public SaveDataRank Rank;
|
[FieldOffset(0x3A)] public SaveDataRank Rank;
|
||||||
[FieldOffset(0x3B)] public SaveDataState State;
|
[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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Impl;
|
||||||
using LibHac.FsSrv;
|
using LibHac.FsSrv;
|
||||||
|
using LibHac.FsSrv.Sf;
|
||||||
|
|
||||||
namespace LibHac.Fs.Shim
|
namespace LibHac.Fs.Shim
|
||||||
{
|
{
|
||||||
@ -43,12 +44,23 @@ namespace LibHac.Fs.Shim
|
|||||||
|
|
||||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
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);
|
ReferenceCountedDisposable<IFileSystemSf> saveFs = null;
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ using LibHac.FsSrv;
|
|||||||
using LibHac.FsSrv.Sf;
|
using LibHac.FsSrv.Sf;
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
using static LibHac.Fs.CommonMountNames;
|
using static LibHac.Fs.CommonPaths;
|
||||||
|
|
||||||
namespace LibHac.Fs.Shim
|
namespace LibHac.Fs.Shim
|
||||||
{
|
{
|
||||||
|
@ -83,7 +83,7 @@ namespace LibHac.Fs.Shim
|
|||||||
ContentType.Control => FileSystemProxyType.Control,
|
ContentType.Control => FileSystemProxyType.Control,
|
||||||
ContentType.Manual => FileSystemProxyType.Manual,
|
ContentType.Manual => FileSystemProxyType.Manual,
|
||||||
ContentType.Data => FileSystemProxyType.Data,
|
ContentType.Data => FileSystemProxyType.Data,
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null),
|
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,11 +39,11 @@ namespace LibHac.Fs.Shim
|
|||||||
switch (storageId)
|
switch (storageId)
|
||||||
{
|
{
|
||||||
case ContentStorageId.System:
|
case ContentStorageId.System:
|
||||||
return CommonMountNames.ContentStorageSystemMountName;
|
return CommonPaths.ContentStorageSystemMountName;
|
||||||
case ContentStorageId.User:
|
case ContentStorageId.User:
|
||||||
return CommonMountNames.ContentStorageUserMountName;
|
return CommonPaths.ContentStorageUserMountName;
|
||||||
case ContentStorageId.SdCard:
|
case ContentStorageId.SdCard:
|
||||||
return CommonMountNames.ContentStorageSdCardMountName;
|
return CommonPaths.ContentStorageSdCardMountName;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException(nameof(storageId), storageId, null);
|
throw new ArgumentOutOfRangeException(nameof(storageId), storageId, null);
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,7 @@ namespace LibHac.Fs.Shim
|
|||||||
{
|
{
|
||||||
char letter = GetGameCardMountNameSuffix(PartitionId);
|
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);
|
new U8Span(mountName).Value.CopyTo(nameBuffer);
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
|
@ -6,7 +6,7 @@ using LibHac.Fs.Fsa;
|
|||||||
using LibHac.FsSrv;
|
using LibHac.FsSrv;
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
using static LibHac.Fs.CommonMountNames;
|
using static LibHac.Fs.CommonPaths;
|
||||||
|
|
||||||
namespace LibHac.Fs.Shim
|
namespace LibHac.Fs.Shim
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using LibHac.FsSrv;
|
using LibHac.FsSrv;
|
||||||
|
using LibHac.Sf;
|
||||||
|
|
||||||
namespace LibHac.Fs.Shim
|
namespace LibHac.Fs.Shim
|
||||||
{
|
{
|
||||||
@ -21,7 +22,7 @@ namespace LibHac.Fs.Shim
|
|||||||
|
|
||||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
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);
|
return fsProxy.RegisterProgramIndexMapInfo(mapInfoBuffer, mapInfo.Length);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Impl;
|
||||||
using LibHac.FsSrv;
|
using LibHac.FsSrv;
|
||||||
|
using LibHac.FsSrv.Sf;
|
||||||
using LibHac.Ncm;
|
using LibHac.Ncm;
|
||||||
|
|
||||||
namespace LibHac.Fs.Shim
|
namespace LibHac.Fs.Shim
|
||||||
@ -115,14 +116,14 @@ namespace LibHac.Fs.Shim
|
|||||||
if (fs.IsEnabledAccessLog(AccessLogTarget.Application))
|
if (fs.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||||
{
|
{
|
||||||
TimeSpan startTime = fs.Time.GetCurrent();
|
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();
|
TimeSpan endTime = fs.Time.GetCurrent();
|
||||||
|
|
||||||
fs.OutputAccessLog(rc, startTime, endTime, $", name: \"{mountName.ToString()}\", index: {index}");
|
fs.OutputAccessLog(rc, startTime, endTime, $", name: \"{mountName.ToString()}\", index: {index}");
|
||||||
}
|
}
|
||||||
else
|
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))
|
if (rc.IsSuccess() && fs.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||||
@ -165,14 +166,14 @@ namespace LibHac.Fs.Shim
|
|||||||
if (fs.IsEnabledAccessLog(AccessLogTarget.System))
|
if (fs.IsEnabledAccessLog(AccessLogTarget.System))
|
||||||
{
|
{
|
||||||
TimeSpan startTime = fs.Time.GetCurrent();
|
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();
|
TimeSpan endTime = fs.Time.GetCurrent();
|
||||||
|
|
||||||
fs.OutputAccessLog(rc, startTime, endTime, $", name: \"{mountName.ToString()}\", applicationid: 0x{applicationId}, index: {index}");
|
fs.OutputAccessLog(rc, startTime, endTime, $", name: \"{mountName.ToString()}\", applicationid: 0x{applicationId}, index: {index}");
|
||||||
}
|
}
|
||||||
else
|
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))
|
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,
|
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);
|
Result rc = MountHelpers.CheckMountName(mountName);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
@ -193,20 +194,29 @@ namespace LibHac.Fs.Shim
|
|||||||
|
|
||||||
var attribute = new SaveDataAttribute(programId, type, userId, 0, index);
|
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;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using LibHac.Common;
|
|
||||||
using LibHac.Diag;
|
using LibHac.Diag;
|
||||||
using LibHac.FsSrv;
|
using LibHac.FsSrv;
|
||||||
|
using LibHac.FsSrv.Sf;
|
||||||
using LibHac.Ncm;
|
using LibHac.Ncm;
|
||||||
|
using LibHac.Sf;
|
||||||
|
|
||||||
namespace LibHac.Fs.Shim
|
namespace LibHac.Fs.Shim
|
||||||
{
|
{
|
||||||
@ -29,13 +30,13 @@ namespace LibHac.Fs.Shim
|
|||||||
SpaceId = SaveDataSpaceId.User
|
SpaceId = SaveDataSpaceId.User
|
||||||
};
|
};
|
||||||
|
|
||||||
var metaInfo = new SaveMetaCreateInfo
|
var metaInfo = new SaveDataMetaInfo
|
||||||
{
|
{
|
||||||
Type = SaveDataMetaType.Thumbnail,
|
Type = SaveDataMetaType.Thumbnail,
|
||||||
Size = 0x40060
|
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}");
|
$", 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
|
SpaceId = SaveDataSpaceId.User
|
||||||
};
|
};
|
||||||
|
|
||||||
var metaInfo = new SaveMetaCreateInfo
|
var metaInfo = new SaveDataMetaInfo
|
||||||
{
|
{
|
||||||
Type = SaveDataMetaType.Thumbnail,
|
Type = SaveDataMetaType.Thumbnail,
|
||||||
Size = 0x40060
|
Size = 0x40060
|
||||||
};
|
};
|
||||||
|
|
||||||
return fsProxy.CreateSaveDataFileSystemWithHashSalt(ref attribute, ref createInfo, ref metaInfo,
|
return fsProxy.CreateSaveDataFileSystemWithHashSalt(in attribute, in createInfo, in metaInfo,
|
||||||
ref hashSalt);
|
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}");
|
$", 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();
|
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
|
var createInfo = new SaveDataCreationInfo
|
||||||
{
|
{
|
||||||
@ -93,9 +94,9 @@ namespace LibHac.Fs.Shim
|
|||||||
SpaceId = SaveDataSpaceId.User
|
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}");
|
() => $", applicationid: 0x{applicationId.Value:X}, save_data_size: {size}");
|
||||||
}
|
}
|
||||||
@ -108,7 +109,7 @@ namespace LibHac.Fs.Shim
|
|||||||
{
|
{
|
||||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
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
|
var createInfo = new SaveDataCreationInfo
|
||||||
{
|
{
|
||||||
@ -120,9 +121,9 @@ namespace LibHac.Fs.Shim
|
|||||||
SpaceId = SaveDataSpaceId.User
|
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}");
|
() => $", 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();
|
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
|
var createInfo = new SaveDataCreationInfo
|
||||||
{
|
{
|
||||||
@ -145,22 +146,22 @@ namespace LibHac.Fs.Shim
|
|||||||
SpaceId = SaveDataSpaceId.Temporary
|
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}");
|
() => $", 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,
|
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,
|
return fs.RunOperationWithAccessLog(AccessLogTarget.System,
|
||||||
() =>
|
() =>
|
||||||
{
|
{
|
||||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
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
|
var creationInfo = new SaveDataCreationInfo
|
||||||
{
|
{
|
||||||
@ -172,9 +173,9 @@ namespace LibHac.Fs.Shim
|
|||||||
SpaceId = spaceId
|
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}");
|
() => $", 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}");
|
||||||
}
|
}
|
||||||
@ -231,19 +232,19 @@ namespace LibHac.Fs.Shim
|
|||||||
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, ulong ownerId, long size,
|
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, ulong ownerId, long size,
|
||||||
long journalSize, SaveDataFlags flags)
|
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,
|
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, long size,
|
||||||
long journalSize, SaveDataFlags flags)
|
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,
|
public static Result CreateSystemSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId,
|
||||||
ulong ownerId, long size, long journalSize, SaveDataFlags flags)
|
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)
|
public static Result DeleteSaveData(this FileSystemClient fs, ulong saveDataId)
|
||||||
@ -283,8 +284,8 @@ namespace LibHac.Fs.Shim
|
|||||||
|
|
||||||
tempInfo = new SaveDataInfo();
|
tempInfo = new SaveDataInfo();
|
||||||
|
|
||||||
Result rc = fsProxy.FindSaveDataWithFilter(out long count, SpanHelpers.AsByteSpan(ref tempInfo),
|
Result rc = fsProxy.FindSaveDataWithFilter(out long count, OutBuffer.FromStruct(ref tempInfo),
|
||||||
spaceId, ref tempFilter);
|
spaceId, in tempFilter);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
@ -329,49 +330,64 @@ namespace LibHac.Fs.Shim
|
|||||||
{
|
{
|
||||||
var tempIterator = new SaveDataIterator();
|
var tempIterator = new SaveDataIterator();
|
||||||
|
|
||||||
Result result = fs.RunOperationWithAccessLog(AccessLogTarget.System,
|
try
|
||||||
() =>
|
{
|
||||||
{
|
Result result = fs.RunOperationWithAccessLog(AccessLogTarget.System,
|
||||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
() =>
|
||||||
|
{
|
||||||
|
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||||
|
|
||||||
Result rc = fsProxy.OpenSaveDataInfoReaderBySaveDataSpaceId(
|
Result rc = fsProxy.OpenSaveDataInfoReaderBySaveDataSpaceId(
|
||||||
out ReferenceCountedDisposable<ISaveDataInfoReader> reader, spaceId);
|
out ReferenceCountedDisposable<ISaveDataInfoReader> reader, spaceId);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
tempIterator = new SaveDataIterator(fs, reader);
|
tempIterator = new SaveDataIterator(fs, reader);
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
},
|
},
|
||||||
() => $", savedataspaceid: {spaceId}");
|
() => $", 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)
|
public static Result OpenSaveDataIterator(this FileSystemClient fs, out SaveDataIterator iterator, SaveDataSpaceId spaceId, ref SaveDataFilter filter)
|
||||||
{
|
{
|
||||||
|
ReferenceCountedDisposable<ISaveDataInfoReader> reader = null;
|
||||||
var tempIterator = new SaveDataIterator();
|
var tempIterator = new SaveDataIterator();
|
||||||
SaveDataFilter tempFilter = filter;
|
SaveDataFilter tempFilter = filter;
|
||||||
|
|
||||||
Result result = fs.RunOperationWithAccessLog(AccessLogTarget.System,
|
try
|
||||||
() =>
|
{
|
||||||
{
|
Result result = fs.RunOperationWithAccessLog(AccessLogTarget.System,
|
||||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
() =>
|
||||||
|
{
|
||||||
|
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||||
|
|
||||||
Result rc = fsProxy.OpenSaveDataInfoReaderWithFilter(
|
Result rc = fsProxy.OpenSaveDataInfoReaderWithFilter(out reader, spaceId, in tempFilter);
|
||||||
out ReferenceCountedDisposable<ISaveDataInfoReader> reader, spaceId, ref tempFilter);
|
if (rc.IsFailure()) return rc;
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
tempIterator = new SaveDataIterator(fs, reader);
|
tempIterator = new SaveDataIterator(fs, reader);
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
},
|
},
|
||||||
() => $", savedataspaceid: {spaceId}");
|
() => $", 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)
|
public static void DisableAutoSaveDataCreation(this FileSystemClient fsClient)
|
||||||
@ -395,14 +411,14 @@ namespace LibHac.Fs.Shim
|
|||||||
internal SaveDataIterator(FileSystemClient fsClient, ReferenceCountedDisposable<ISaveDataInfoReader> reader)
|
internal SaveDataIterator(FileSystemClient fsClient, ReferenceCountedDisposable<ISaveDataInfoReader> reader)
|
||||||
{
|
{
|
||||||
FsClient = fsClient;
|
FsClient = fsClient;
|
||||||
Reader = reader;
|
Reader = reader.AddReference();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result ReadSaveDataInfo(out long readCount, Span<SaveDataInfo> buffer)
|
public Result ReadSaveDataInfo(out long readCount, Span<SaveDataInfo> buffer)
|
||||||
{
|
{
|
||||||
Result rc;
|
Result rc;
|
||||||
|
|
||||||
Span<byte> byteBuffer = MemoryMarshal.Cast<SaveDataInfo, byte>(buffer);
|
var byteBuffer = new OutBuffer(MemoryMarshal.Cast<SaveDataInfo, byte>(buffer));
|
||||||
|
|
||||||
if (FsClient.IsEnabledAccessLog(AccessLogTarget.System))
|
if (FsClient.IsEnabledAccessLog(AccessLogTarget.System))
|
||||||
{
|
{
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Impl;
|
||||||
using LibHac.FsSrv;
|
using LibHac.FsSrv;
|
||||||
|
using LibHac.FsSrv.Sf;
|
||||||
using LibHac.Ncm;
|
using LibHac.Ncm;
|
||||||
|
|
||||||
namespace LibHac.Fs.Shim
|
namespace LibHac.Fs.Shim
|
||||||
{
|
{
|
||||||
public static class SystemSaveData
|
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,
|
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);
|
var attribute = new SaveDataAttribute(ProgramId.InvalidId, SaveDataType.System, userId, saveDataId);
|
||||||
|
|
||||||
rc = fsProxy.OpenSaveDataFileSystemBySystemSaveDataId(out IFileSystem fileSystem, spaceId, ref attribute);
|
ReferenceCountedDisposable<IFileSystemSf> saveFs = null;
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
52
src/LibHac/Fs/Shim/UserFileSystem.cs
Normal file
52
src/LibHac/Fs/Shim/UserFileSystem.cs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
using System;
|
||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Fs.Accessors;
|
||||||
|
using LibHac.FsSrv.Sf;
|
||||||
|
|
||||||
|
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)]
|
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
|
||||||
public struct UserId : IEquatable<UserId>, IComparable<UserId>, IComparable
|
public struct UserId : IEquatable<UserId>, IComparable<UserId>, IComparable
|
||||||
{
|
{
|
||||||
public static UserId Zero => default;
|
public static UserId InvalidId => default;
|
||||||
|
|
||||||
public readonly Id128 Id;
|
public readonly Id128 Id;
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ namespace LibHac.FsSrv
|
|||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
// Create an SF adapter for the file system
|
// Create an SF adapter for the file system
|
||||||
fileSystem = FileSystemInterfaceAdapter.CreateSharedSfFileSystem(ref fs);
|
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref fs);
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
@ -117,7 +117,7 @@ namespace LibHac.FsSrv
|
|||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
// Create an SF adapter for the file system
|
// Create an SF adapter for the file system
|
||||||
fileSystem = FileSystemInterfaceAdapter.CreateSharedSfFileSystem(ref fs);
|
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref fs);
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
@ -147,7 +147,7 @@ namespace LibHac.FsSrv
|
|||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
// Create an SF adapter for the file system
|
// Create an SF adapter for the file system
|
||||||
fileSystem = FileSystemInterfaceAdapter.CreateSharedSfFileSystem(ref fs);
|
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref fs);
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
@ -220,7 +220,7 @@ namespace LibHac.FsSrv
|
|||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
// Create an SF adapter for the file system
|
// Create an SF adapter for the file system
|
||||||
fileSystem = FileSystemInterfaceAdapter.CreateSharedSfFileSystem(ref fs);
|
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref fs);
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,14 @@ namespace LibHac.FsSrv
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, U8Span rootPath, BisPartitionId partitionId)
|
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;
|
fileSystem = default;
|
||||||
|
|
||||||
@ -85,12 +92,12 @@ namespace LibHac.FsSrv
|
|||||||
return OpenSdCardProxyFileSystem(out fileSystem, false);
|
return OpenSdCardProxyFileSystem(out fileSystem, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result OpenSdCardProxyFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, bool isCaseSensitive)
|
public Result OpenSdCardProxyFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, bool openCaseSensitive)
|
||||||
{
|
{
|
||||||
fileSystem = default;
|
fileSystem = default;
|
||||||
|
|
||||||
// Todo: Shared
|
// Todo: Shared
|
||||||
Result rc = _config.SdCardFileSystemCreator.Create(out IFileSystem fs, isCaseSensitive);
|
Result rc = _config.SdCardFileSystemCreator.Create(out IFileSystem fs, openCaseSensitive);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
fileSystem = new ReferenceCountedDisposable<IFileSystem>(fs);
|
fileSystem = new ReferenceCountedDisposable<IFileSystem>(fs);
|
||||||
|
@ -87,7 +87,9 @@ namespace LibHac.FsSrv.Creators
|
|||||||
return Util.CreateSubFileSystemImpl(out fileSystem, subFileSystem, rootPath);
|
return Util.CreateSubFileSystemImpl(out fileSystem, subFileSystem, rootPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, U8Span rootPath, BisPartitionId partitionId)
|
// Todo: Make case sensitive
|
||||||
|
public Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, U8Span rootPath,
|
||||||
|
BisPartitionId partitionId, bool caseSensitive)
|
||||||
{
|
{
|
||||||
fileSystem = default;
|
fileSystem = default;
|
||||||
|
|
||||||
@ -107,21 +109,28 @@ namespace LibHac.FsSrv.Creators
|
|||||||
|
|
||||||
var partitionPath = GetPartitionPath(partitionId).ToU8String();
|
var partitionPath = GetPartitionPath(partitionId).ToU8String();
|
||||||
|
|
||||||
// Todo: Store shared file systems
|
ReferenceCountedDisposable<IFileSystem> partitionFileSystem = null;
|
||||||
using var sharedRootFs = new ReferenceCountedDisposable<IFileSystem>(Config.RootFileSystem);
|
try
|
||||||
|
|
||||||
Result rc = Utility.WrapSubDirectory(out ReferenceCountedDisposable<IFileSystem> partitionFileSystem,
|
|
||||||
sharedRootFs, partitionPath, true);
|
|
||||||
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
if (rootPath.IsEmpty())
|
|
||||||
{
|
{
|
||||||
fileSystem = partitionFileSystem.AddReference();
|
// Todo: Store shared file systems
|
||||||
return Result.Success;
|
using var sharedRootFs = new ReferenceCountedDisposable<IFileSystem>(Config.RootFileSystem);
|
||||||
}
|
|
||||||
|
|
||||||
return Utility.CreateSubDirectoryFileSystem(out fileSystem, partitionFileSystem, rootPath);
|
Result rc = Utility.WrapSubDirectory(out partitionFileSystem, 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, partitionFileSystem, rootPath);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
partitionFileSystem?.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result CreateFatFileSystem(out IFileSystem fileSystem, BisPartitionId partitionId)
|
public Result CreateFatFileSystem(out IFileSystem fileSystem, BisPartitionId partitionId)
|
||||||
|
@ -48,7 +48,7 @@ namespace LibHac.FsSrv.Creators
|
|||||||
KeySet.SetSdSeed(encryptionSeed.Value);
|
KeySet.SetSdSeed(encryptionSeed.Value);
|
||||||
|
|
||||||
// Todo: pass ReferenceCountedDisposable to AesXtsFileSystem
|
// Todo: pass ReferenceCountedDisposable to AesXtsFileSystem
|
||||||
var fs = new AesXtsFileSystem(baseFileSystem.AddReference().Target, KeySet.SdCardEncryptionKeys[(int)keyId].DataRo.ToArray(), 0x4000);
|
var fs = new AesXtsFileSystem(baseFileSystem, KeySet.SdCardEncryptionKeys[(int)keyId].DataRo.ToArray(), 0x4000);
|
||||||
encryptedFileSystem = new ReferenceCountedDisposable<IFileSystem>(fs);
|
encryptedFileSystem = new ReferenceCountedDisposable<IFileSystem>(fs);
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
|
@ -8,7 +8,7 @@ namespace LibHac.FsSrv.Creators
|
|||||||
{
|
{
|
||||||
// Todo: Remove raw IFileSystem overload
|
// Todo: Remove raw IFileSystem overload
|
||||||
Result Create(out IFileSystem fileSystem, string rootPath, BisPartitionId partitionId);
|
Result Create(out IFileSystem fileSystem, string rootPath, BisPartitionId partitionId);
|
||||||
Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, U8Span rootPath, BisPartitionId partitionId);
|
Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, U8Span rootPath, BisPartitionId partitionId, bool caseSensitive);
|
||||||
Result CreateFatFileSystem(out IFileSystem fileSystem, BisPartitionId partitionId);
|
Result CreateFatFileSystem(out IFileSystem fileSystem, BisPartitionId partitionId);
|
||||||
Result SetBisRootForHost(BisPartitionId partitionId, string rootPath);
|
Result SetBisRootForHost(BisPartitionId partitionId, string rootPath);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
using LibHac.FsSystem.Save;
|
using LibHac.FsSystem;
|
||||||
|
|
||||||
namespace LibHac.FsSrv.Creators
|
namespace LibHac.FsSrv.Creators
|
||||||
{
|
{
|
||||||
@ -10,9 +10,10 @@ namespace LibHac.FsSrv.Creators
|
|||||||
{
|
{
|
||||||
Result CreateFile(out IFile file, IFileSystem sourceFileSystem, ulong saveDataId, OpenMode openMode);
|
Result CreateFile(out IFile file, IFileSystem sourceFileSystem, ulong saveDataId, OpenMode openMode);
|
||||||
|
|
||||||
Result Create(out IFileSystem fileSystem, out ISaveDataExtraDataAccessor extraDataAccessor,
|
Result Create(out IFileSystem fileSystem,
|
||||||
IFileSystem sourceFileSystem, ulong saveDataId, bool allowDirectorySaveData, bool useDeviceUniqueMac,
|
out ReferenceCountedDisposable<ISaveDataExtraDataAccessor> extraDataAccessor, IFileSystem sourceFileSystem,
|
||||||
SaveDataType type, ITimeStampGenerator timeStampGenerator);
|
ulong saveDataId, bool allowDirectorySaveData, bool useDeviceUniqueMac, SaveDataType type,
|
||||||
|
ITimeStampGenerator timeStampGenerator);
|
||||||
|
|
||||||
void SetSdCardEncryptionSeed(ReadOnlySpan<byte> seed);
|
void SetSdCardEncryptionSeed(ReadOnlySpan<byte> seed);
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,6 @@ namespace LibHac.FsSrv.Creators
|
|||||||
// Todo: Remove raw IFilesystem function
|
// Todo: Remove raw IFilesystem function
|
||||||
Result Create(out IFileSystem fileSystem, bool openCaseSensitive);
|
Result Create(out IFileSystem fileSystem, bool openCaseSensitive);
|
||||||
Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, bool openCaseSensitive);
|
Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, bool openCaseSensitive);
|
||||||
Result GetCaseSensitivePath(out bool isSuccess, Span<byte> path);
|
Result NormalizeCaseOfPath(out bool isSupported, Span<byte> path);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -22,9 +22,10 @@ namespace LibHac.FsSrv.Creators
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result Create(out IFileSystem fileSystem, out ISaveDataExtraDataAccessor extraDataAccessor,
|
public Result Create(out IFileSystem fileSystem,
|
||||||
IFileSystem sourceFileSystem, ulong saveDataId, bool allowDirectorySaveData, bool useDeviceUniqueMac,
|
out ReferenceCountedDisposable<ISaveDataExtraDataAccessor> extraDataAccessor, IFileSystem sourceFileSystem,
|
||||||
SaveDataType type, ITimeStampGenerator timeStampGenerator)
|
ulong saveDataId, bool allowDirectorySaveData, bool useDeviceUniqueMac, SaveDataType type,
|
||||||
|
ITimeStampGenerator timeStampGenerator)
|
||||||
{
|
{
|
||||||
fileSystem = default;
|
fileSystem = default;
|
||||||
extraDataAccessor = default;
|
extraDataAccessor = default;
|
||||||
@ -42,13 +43,15 @@ namespace LibHac.FsSrv.Creators
|
|||||||
case DirectoryEntryType.Directory:
|
case DirectoryEntryType.Directory:
|
||||||
if (!allowDirectorySaveData) return ResultFs.InvalidSaveDataEntryType.Log();
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
bool isPersistentSaveData = type != SaveDataType.Temporary;
|
bool isPersistentSaveData = type != SaveDataType.Temporary;
|
||||||
bool isUserSaveData = type == SaveDataType.Account || type == SaveDataType.Device;
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
fileSystem = saveFs;
|
fileSystem = saveFs;
|
||||||
@ -62,7 +65,8 @@ namespace LibHac.FsSrv.Creators
|
|||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
var saveDataStorage = new DisposingFileStorage(saveDataFile);
|
var saveDataStorage = new DisposingFileStorage(saveDataFile);
|
||||||
fileSystem = new SaveDataFileSystem(KeySet, saveDataStorage, IntegrityCheckLevel.ErrorOnInvalid, false);
|
fileSystem = new SaveDataFileSystem(KeySet, saveDataStorage, IntegrityCheckLevel.ErrorOnInvalid,
|
||||||
|
false);
|
||||||
|
|
||||||
// Todo: ISaveDataExtraDataAccessor
|
// Todo: ISaveDataExtraDataAccessor
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ namespace LibHac.FsSrv.Creators
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result GetCaseSensitivePath(out bool isSuccess, Span<byte> path)
|
public Result NormalizeCaseOfPath(out bool isSupported, Span<byte> path)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
|
}
|
@ -7,6 +7,7 @@ namespace LibHac.FsSrv
|
|||||||
public FileSystemCreators FsCreatorInterfaces { get; set; }
|
public FileSystemCreators FsCreatorInterfaces { get; set; }
|
||||||
public BaseFileSystemServiceImpl BaseFileSystemService { get; set; }
|
public BaseFileSystemServiceImpl BaseFileSystemService { get; set; }
|
||||||
public NcaFileSystemServiceImpl NcaFileSystemService { get; set; }
|
public NcaFileSystemServiceImpl NcaFileSystemService { get; set; }
|
||||||
|
public SaveDataFileSystemServiceImpl SaveDataFileSystemService { get; set; }
|
||||||
public ProgramRegistryServiceImpl ProgramRegistryService { get; set; }
|
public ProgramRegistryServiceImpl ProgramRegistryService { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ using LibHac.Common;
|
|||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
using LibHac.Fs.Shim;
|
using LibHac.Fs.Shim;
|
||||||
using LibHac.FsSystem;
|
|
||||||
using LibHac.FsSrv.Creators;
|
using LibHac.FsSrv.Creators;
|
||||||
|
|
||||||
namespace LibHac.FsSrv
|
namespace LibHac.FsSrv
|
||||||
@ -21,7 +20,6 @@ namespace LibHac.FsSrv
|
|||||||
private const string NintendoDirectoryName = "Nintendo";
|
private const string NintendoDirectoryName = "Nintendo";
|
||||||
|
|
||||||
private GlobalAccessLogMode LogMode { get; set; }
|
private GlobalAccessLogMode LogMode { get; set; }
|
||||||
public bool IsSdCardAccessible { get; set; }
|
|
||||||
|
|
||||||
internal ISaveDataIndexerManager SaveDataIndexerManager { get; private set; }
|
internal ISaveDataIndexerManager SaveDataIndexerManager { get; private set; }
|
||||||
|
|
||||||
@ -32,16 +30,6 @@ namespace LibHac.FsSrv
|
|||||||
DeviceOperator = deviceOperator;
|
DeviceOperator = deviceOperator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result OpenBisFileSystem(out IFileSystem fileSystem, string rootPath, BisPartitionId partitionId)
|
|
||||||
{
|
|
||||||
return FsCreators.BuiltInStorageFileSystemCreator.Create(out fileSystem, rootPath, partitionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result OpenSdCardFileSystem(out IFileSystem fileSystem)
|
|
||||||
{
|
|
||||||
return FsCreators.SdCardFileSystemCreator.Create(out fileSystem, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result OpenGameCardStorage(out IStorage storage, GameCardHandle handle, GameCardPartitionRaw partitionId)
|
public Result OpenGameCardStorage(out IStorage storage, GameCardHandle handle, GameCardPartitionRaw partitionId)
|
||||||
{
|
{
|
||||||
switch (partitionId)
|
switch (partitionId)
|
||||||
@ -146,242 +134,12 @@ namespace LibHac.FsSrv
|
|||||||
seed.Value.CopyTo(SdEncryptionSeed);
|
seed.Value.CopyTo(SdEncryptionSeed);
|
||||||
// todo: FsCreators.SaveDataFileSystemCreator.SetSdCardEncryptionSeed(seed);
|
// todo: FsCreators.SaveDataFileSystemCreator.SetSdCardEncryptionSeed(seed);
|
||||||
|
|
||||||
SaveDataIndexerManager.InvalidateSdCardIndexer(SaveDataSpaceId.SdSystem);
|
SaveDataIndexerManager.InvalidateIndexer(SaveDataSpaceId.SdSystem);
|
||||||
SaveDataIndexerManager.InvalidateSdCardIndexer(SaveDataSpaceId.SdCache);
|
SaveDataIndexerManager.InvalidateIndexer(SaveDataSpaceId.SdCache);
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool AllowDirectorySaveData(SaveDataSpaceId spaceId, string saveDataRootPath)
|
|
||||||
{
|
|
||||||
return spaceId == SaveDataSpaceId.User && !string.IsNullOrWhiteSpace(saveDataRootPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result DoesSaveDataExist(out bool exists, SaveDataSpaceId spaceId, ulong saveDataId)
|
|
||||||
{
|
|
||||||
exists = false;
|
|
||||||
|
|
||||||
Result rc = OpenSaveDataDirectory(out IFileSystem fileSystem, spaceId, string.Empty, true);
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
string saveDataPath = $"/{saveDataId:x16}";
|
|
||||||
|
|
||||||
rc = fileSystem.GetEntryType(out _, saveDataPath.ToU8Span());
|
|
||||||
|
|
||||||
if (rc.IsFailure())
|
|
||||||
{
|
|
||||||
if (ResultFs.PathNotFound.Includes(rc))
|
|
||||||
{
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
exists = true;
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result OpenSaveDataFileSystem(out IFileSystem fileSystem, SaveDataSpaceId spaceId, ulong saveDataId,
|
|
||||||
string saveDataRootPath, bool openReadOnly, SaveDataType type, bool cacheExtraData)
|
|
||||||
{
|
|
||||||
fileSystem = default;
|
|
||||||
|
|
||||||
Result rc = OpenSaveDataDirectory(out IFileSystem saveDirFs, spaceId, saveDataRootPath, true);
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
// ReSharper disable once RedundantAssignment
|
|
||||||
bool allowDirectorySaveData = AllowDirectorySaveData(spaceId, saveDataRootPath);
|
|
||||||
bool useDeviceUniqueMac = Util.UseDeviceUniqueSaveMac(spaceId);
|
|
||||||
|
|
||||||
// Always allow directory savedata because we don't support transaction with file savedata yet
|
|
||||||
allowDirectorySaveData = true;
|
|
||||||
|
|
||||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
|
||||||
if (allowDirectorySaveData)
|
|
||||||
{
|
|
||||||
rc = saveDirFs.EnsureDirectoryExists(GetSaveDataIdPath(saveDataId));
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Missing save FS cache lookup
|
|
||||||
|
|
||||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
|
||||||
rc = FsCreators.SaveDataFileSystemCreator.Create(out IFileSystem saveFs, out _, saveDirFs, saveDataId,
|
|
||||||
allowDirectorySaveData, useDeviceUniqueMac, type, null);
|
|
||||||
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
if (cacheExtraData)
|
|
||||||
{
|
|
||||||
// todo: Missing extra data caching
|
|
||||||
}
|
|
||||||
|
|
||||||
fileSystem = openReadOnly ? new ReadOnlyFileSystem(saveFs) : saveFs;
|
|
||||||
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result OpenSaveDataDirectory(out IFileSystem fileSystem, SaveDataSpaceId spaceId, string saveDataRootPath, bool openOnHostFs)
|
|
||||||
{
|
|
||||||
if (openOnHostFs && AllowDirectorySaveData(spaceId, saveDataRootPath))
|
|
||||||
{
|
|
||||||
Result rc = FsCreators.TargetManagerFileSystemCreator.Create(out IFileSystem hostFs, false);
|
|
||||||
|
|
||||||
if (rc.IsFailure())
|
|
||||||
{
|
|
||||||
fileSystem = default;
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Util.CreateSubFileSystem(out fileSystem, hostFs, saveDataRootPath, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
string dirName = spaceId == SaveDataSpaceId.Temporary ? "/temp" : "/save";
|
|
||||||
|
|
||||||
return OpenSaveDataDirectoryImpl(out fileSystem, spaceId, dirName, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result OpenSaveDataDirectoryImpl(out IFileSystem fileSystem, SaveDataSpaceId spaceId, string saveDirName, bool createIfMissing)
|
|
||||||
{
|
|
||||||
fileSystem = default;
|
|
||||||
Result rc;
|
|
||||||
|
|
||||||
switch (spaceId)
|
|
||||||
{
|
|
||||||
case SaveDataSpaceId.System:
|
|
||||||
rc = OpenBisFileSystem(out IFileSystem sysFs, string.Empty, BisPartitionId.System);
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
return Util.CreateSubFileSystem(out fileSystem, sysFs, saveDirName, createIfMissing);
|
|
||||||
|
|
||||||
case SaveDataSpaceId.User:
|
|
||||||
case SaveDataSpaceId.Temporary:
|
|
||||||
rc = OpenBisFileSystem(out IFileSystem userFs, string.Empty, BisPartitionId.User);
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
return Util.CreateSubFileSystem(out fileSystem, userFs, saveDirName, createIfMissing);
|
|
||||||
|
|
||||||
case SaveDataSpaceId.SdSystem:
|
|
||||||
case SaveDataSpaceId.SdCache:
|
|
||||||
rc = OpenSdCardFileSystem(out IFileSystem sdFs);
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
string sdSaveDirPath = $"/{NintendoDirectoryName}{saveDirName}";
|
|
||||||
|
|
||||||
rc = Util.CreateSubFileSystem(out IFileSystem sdSubFs, sdFs, sdSaveDirPath, createIfMissing);
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
return FsCreators.EncryptedFileSystemCreator.Create(out fileSystem, sdSubFs,
|
|
||||||
EncryptedFsKeyId.Save, SdEncryptionSeed);
|
|
||||||
|
|
||||||
case SaveDataSpaceId.ProperSystem:
|
|
||||||
rc = OpenBisFileSystem(out IFileSystem sysProperFs, string.Empty, BisPartitionId.SystemProperPartition);
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
return Util.CreateSubFileSystem(out fileSystem, sysProperFs, saveDirName, createIfMissing);
|
|
||||||
|
|
||||||
case SaveDataSpaceId.SafeMode:
|
|
||||||
rc = OpenBisFileSystem(out IFileSystem safeFs, string.Empty, BisPartitionId.SafeMode);
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
return Util.CreateSubFileSystem(out fileSystem, safeFs, saveDirName, createIfMissing);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return ResultFs.InvalidArgument.Log();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result OpenSaveDataMetaFile(out IFile file, ulong saveDataId, SaveDataSpaceId spaceId, SaveDataMetaType type)
|
|
||||||
{
|
|
||||||
file = default;
|
|
||||||
|
|
||||||
string metaDirPath = $"/saveMeta/{saveDataId:x16}";
|
|
||||||
|
|
||||||
Result rc = OpenSaveDataDirectoryImpl(out IFileSystem tmpMetaDirFs, spaceId, metaDirPath, true);
|
|
||||||
using IFileSystem metaDirFs = tmpMetaDirFs;
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
string metaFilePath = $"/{(int)type:x8}.meta";
|
|
||||||
|
|
||||||
return metaDirFs.OpenFile(out file, metaFilePath.ToU8Span(), OpenMode.ReadWrite);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result DeleteSaveDataMetaFiles(ulong saveDataId, SaveDataSpaceId spaceId)
|
|
||||||
{
|
|
||||||
Result rc = OpenSaveDataDirectoryImpl(out IFileSystem metaDirFs, spaceId, "/saveMeta", false);
|
|
||||||
|
|
||||||
using (metaDirFs)
|
|
||||||
{
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
rc = metaDirFs.DeleteDirectoryRecursively($"/{saveDataId:x16}".ToU8Span());
|
|
||||||
|
|
||||||
if (rc.IsFailure() && !ResultFs.PathNotFound.Includes(rc))
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result CreateSaveDataMetaFile(ulong saveDataId, SaveDataSpaceId spaceId, SaveDataMetaType type, long size)
|
|
||||||
{
|
|
||||||
string metaDirPath = $"/saveMeta/{saveDataId:x16}";
|
|
||||||
|
|
||||||
Result rc = OpenSaveDataDirectoryImpl(out IFileSystem tmpMetaDirFs, spaceId, metaDirPath, true);
|
|
||||||
using IFileSystem metaDirFs = tmpMetaDirFs;
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
string metaFilePath = $"/{(int)type:x8}.meta";
|
|
||||||
|
|
||||||
if (size < 0) return ResultFs.OutOfRange.Log();
|
|
||||||
|
|
||||||
return metaDirFs.CreateFile(metaFilePath.ToU8Span(), size, CreateFileOptions.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result CreateSaveDataFileSystem(ulong saveDataId, ref SaveDataAttribute attribute,
|
|
||||||
ref SaveDataCreationInfo creationInfo, U8Span rootPath, OptionalHashSalt hashSalt, bool something)
|
|
||||||
{
|
|
||||||
// Use directory save data for now
|
|
||||||
|
|
||||||
Result rc = OpenSaveDataDirectory(out IFileSystem fileSystem, creationInfo.SpaceId, string.Empty, false);
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
return fileSystem.EnsureDirectoryExists(GetSaveDataIdPath(saveDataId));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result DeleteSaveDataFileSystem(SaveDataSpaceId spaceId, ulong saveDataId, bool doSecureDelete)
|
|
||||||
{
|
|
||||||
Result rc = OpenSaveDataDirectory(out IFileSystem fileSystem, spaceId, string.Empty, false);
|
|
||||||
|
|
||||||
using (fileSystem)
|
|
||||||
{
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
var saveDataPath = GetSaveDataIdPath(saveDataId).ToU8Span();
|
|
||||||
|
|
||||||
rc = fileSystem.GetEntryType(out DirectoryEntryType entryType, saveDataPath);
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
if (entryType == DirectoryEntryType.Directory)
|
|
||||||
{
|
|
||||||
rc = fileSystem.DeleteDirectoryRecursively(saveDataPath);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (doSecureDelete)
|
|
||||||
{
|
|
||||||
// Overwrite file with garbage before deleting
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = fileSystem.DeleteFile(saveDataPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result SetGlobalAccessLogMode(GlobalAccessLogMode mode)
|
public Result SetGlobalAccessLogMode(GlobalAccessLogMode mode)
|
||||||
{
|
{
|
||||||
LogMode = mode;
|
LogMode = mode;
|
||||||
@ -398,15 +156,5 @@ namespace LibHac.FsSrv
|
|||||||
{
|
{
|
||||||
SaveDataIndexerManager = manager;
|
SaveDataIndexerManager = manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Result OpenSaveDataIndexerAccessor(out SaveDataIndexerAccessor accessor, out bool neededInit, SaveDataSpaceId spaceId)
|
|
||||||
{
|
|
||||||
return SaveDataIndexerManager.OpenAccessor(out accessor, out neededInit, spaceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetSaveDataIdPath(ulong saveDataId)
|
|
||||||
{
|
|
||||||
return $"/{saveDataId:x16}";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -50,8 +50,16 @@ namespace LibHac.FsSrv
|
|||||||
new ArrayPoolMemoryResource(), new SdHandleManager(), false));
|
new ArrayPoolMemoryResource(), new SdHandleManager(), false));
|
||||||
|
|
||||||
FileSystemProxyImpl fsProxy = GetFileSystemProxyServiceObject();
|
FileSystemProxyImpl fsProxy = GetFileSystemProxyServiceObject();
|
||||||
fsProxy.SetCurrentProcess(Hos.Os.GetCurrentProcessId().Value).IgnoreResult();
|
ulong processId = Hos.Os.GetCurrentProcessId().Value;
|
||||||
fsProxy.CleanUpTemporaryStorage().IgnoreResult();
|
fsProxy.SetCurrentProcess(processId).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 FileSystemProxyService(this), "fsp-srv").IgnoreResult();
|
||||||
Hos.Sm.RegisterService(new FileSystemProxyForLoaderService(this), "fsp-ldr").IgnoreResult();
|
Hos.Sm.RegisterService(new FileSystemProxyForLoaderService(this), "fsp-ldr").IgnoreResult();
|
||||||
@ -82,6 +90,9 @@ namespace LibHac.FsSrv
|
|||||||
|
|
||||||
private FileSystemProxyConfiguration InitializeFileSystemProxyConfiguration(FileSystemServerConfig config)
|
private FileSystemProxyConfiguration InitializeFileSystemProxyConfiguration(FileSystemServerConfig config)
|
||||||
{
|
{
|
||||||
|
var saveDataIndexerManager = new SaveDataIndexerManager(Hos.Fs, SaveIndexerId,
|
||||||
|
new ArrayPoolMemoryResource(), new SdHandleManager(), false);
|
||||||
|
|
||||||
var programRegistryService = new ProgramRegistryServiceImpl(this);
|
var programRegistryService = new ProgramRegistryServiceImpl(this);
|
||||||
var programRegistry = new ProgramRegistryImpl(programRegistryService);
|
var programRegistry = new ProgramRegistryImpl(programRegistryService);
|
||||||
|
|
||||||
@ -111,11 +122,26 @@ namespace LibHac.FsSrv
|
|||||||
|
|
||||||
var ncaFsService = new NcaFileSystemServiceImpl(in ncaFsServiceConfig, config.ExternalKeySet);
|
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 fspConfig = new FileSystemProxyConfiguration
|
var fspConfig = new FileSystemProxyConfiguration
|
||||||
{
|
{
|
||||||
FsCreatorInterfaces = config.FsCreators,
|
FsCreatorInterfaces = config.FsCreators,
|
||||||
BaseFileSystemService = baseFsService,
|
BaseFileSystemService = baseFsService,
|
||||||
NcaFileSystemService = ncaFsService,
|
NcaFileSystemService = ncaFsService,
|
||||||
|
SaveDataFileSystemService = saveFsService,
|
||||||
ProgramRegistryService = programRegistryService
|
ProgramRegistryService = programRegistryService
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -124,12 +150,12 @@ namespace LibHac.FsSrv
|
|||||||
|
|
||||||
private FileSystemProxyImpl GetFileSystemProxyServiceObject()
|
private FileSystemProxyImpl GetFileSystemProxyServiceObject()
|
||||||
{
|
{
|
||||||
return new FileSystemProxyImpl(Hos, FsProxyCore);
|
return new FileSystemProxyImpl(FsProxyCore);
|
||||||
}
|
}
|
||||||
|
|
||||||
private FileSystemProxyImpl GetFileSystemProxyForLoaderServiceObject()
|
private FileSystemProxyImpl GetFileSystemProxyForLoaderServiceObject()
|
||||||
{
|
{
|
||||||
return new FileSystemProxyImpl(Hos, FsProxyCore);
|
return new FileSystemProxyImpl(FsProxyCore);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ProgramRegistryImpl GetProgramRegistryServiceObject()
|
private ProgramRegistryImpl GetProgramRegistryServiceObject()
|
||||||
|
@ -25,38 +25,40 @@ namespace LibHac.FsSrv
|
|||||||
Result OpenSdCardFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem);
|
Result OpenSdCardFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem);
|
||||||
Result FormatSdCardFileSystem();
|
Result FormatSdCardFileSystem();
|
||||||
Result DeleteSaveDataFileSystem(ulong saveDataId);
|
Result DeleteSaveDataFileSystem(ulong saveDataId);
|
||||||
Result CreateSaveDataFileSystem(ref SaveDataAttribute attribute, ref SaveDataCreationInfo creationInfo, ref SaveMetaCreateInfo metaCreateInfo);
|
Result CreateSaveDataFileSystem(in SaveDataAttribute attribute, in SaveDataCreationInfo creationInfo, in SaveDataMetaInfo metaInfo);
|
||||||
Result CreateSaveDataFileSystemBySystemSaveDataId(ref SaveDataAttribute attribute, ref SaveDataCreationInfo creationInfo);
|
Result CreateSaveDataFileSystemBySystemSaveDataId(ref SaveDataAttribute attribute, ref SaveDataCreationInfo creationInfo);
|
||||||
Result RegisterSaveDataFileSystemAtomicDeletion(ReadOnlySpan<ulong> saveDataIds);
|
Result RegisterSaveDataFileSystemAtomicDeletion(InBuffer saveDataIds);
|
||||||
Result DeleteSaveDataFileSystemBySaveDataSpaceId(SaveDataSpaceId spaceId, ulong saveDataId);
|
Result DeleteSaveDataFileSystemBySaveDataSpaceId(SaveDataSpaceId spaceId, ulong saveDataId);
|
||||||
Result FormatSdCardDryRun();
|
Result FormatSdCardDryRun();
|
||||||
Result IsExFatSupported(out bool isSupported);
|
Result IsExFatSupported(out bool isSupported);
|
||||||
Result DeleteSaveDataFileSystemBySaveDataAttribute(SaveDataSpaceId spaceId, ref SaveDataAttribute attribute);
|
Result DeleteSaveDataFileSystemBySaveDataAttribute(SaveDataSpaceId spaceId, in SaveDataAttribute attribute);
|
||||||
Result OpenGameCardStorage(out IStorage storage, GameCardHandle handle, GameCardPartitionRaw partitionId);
|
Result OpenGameCardStorage(out IStorage storage, GameCardHandle handle, GameCardPartitionRaw partitionId);
|
||||||
Result OpenGameCardFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, GameCardHandle handle, GameCardPartition partitionId);
|
Result OpenGameCardFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, GameCardHandle handle, GameCardPartition partitionId);
|
||||||
Result ExtendSaveDataFileSystem(SaveDataSpaceId spaceId, ulong saveDataId, long dataSize, long journalSize);
|
Result ExtendSaveDataFileSystem(SaveDataSpaceId spaceId, ulong saveDataId, long dataSize, long journalSize);
|
||||||
Result DeleteCacheStorage(short index);
|
Result DeleteCacheStorage(ushort index);
|
||||||
Result GetCacheStorageSize(out long dataSize, out long journalSize, short index);
|
Result GetCacheStorageSize(out long dataSize, out long journalSize, ushort index);
|
||||||
Result CreateSaveDataFileSystemWithHashSalt(ref SaveDataAttribute attribute, ref SaveDataCreationInfo creationInfo, ref SaveMetaCreateInfo metaCreateInfo, ref HashSalt hashSalt);
|
Result CreateSaveDataFileSystemWithHashSalt(in SaveDataAttribute attribute, in SaveDataCreationInfo creationInfo, in SaveDataMetaInfo metaInfo, in HashSalt hashSalt);
|
||||||
Result OpenSaveDataFileSystem(out IFileSystem fileSystem, SaveDataSpaceId spaceId, ref SaveDataAttribute attribute);
|
Result OpenSaveDataFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, SaveDataSpaceId spaceId, in SaveDataAttribute attribute);
|
||||||
Result OpenSaveDataFileSystemBySystemSaveDataId(out IFileSystem fileSystem, SaveDataSpaceId spaceId, ref SaveDataAttribute attribute);
|
Result OpenSaveDataFileSystemBySystemSaveDataId(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, SaveDataSpaceId spaceId, in SaveDataAttribute attribute);
|
||||||
Result OpenReadOnlySaveDataFileSystem(out IFileSystem fileSystem, SaveDataSpaceId spaceId, ref SaveDataAttribute attribute);
|
Result OpenReadOnlySaveDataFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, SaveDataSpaceId spaceId, in SaveDataAttribute attribute);
|
||||||
Result ReadSaveDataFileSystemExtraDataBySaveDataSpaceId(Span<byte> extraDataBuffer, SaveDataSpaceId spaceId, ulong saveDataId);
|
Result ReadSaveDataFileSystemExtraDataBySaveDataSpaceId(OutBuffer extraDataBuffer, SaveDataSpaceId spaceId, ulong saveDataId);
|
||||||
Result ReadSaveDataFileSystemExtraData(Span<byte> extraDataBuffer, ulong saveDataId);
|
Result ReadSaveDataFileSystemExtraData(OutBuffer extraDataBuffer, ulong saveDataId);
|
||||||
Result WriteSaveDataFileSystemExtraData(ulong saveDataId, SaveDataSpaceId spaceId, ReadOnlySpan<byte> extraDataBuffer);
|
Result WriteSaveDataFileSystemExtraData(ulong saveDataId, SaveDataSpaceId spaceId, InBuffer extraDataBuffer);
|
||||||
Result OpenSaveDataInfoReader(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader);
|
Result OpenSaveDataInfoReader(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader);
|
||||||
Result OpenSaveDataInfoReaderBySaveDataSpaceId(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader, SaveDataSpaceId spaceId);
|
Result OpenSaveDataInfoReaderBySaveDataSpaceId(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader, SaveDataSpaceId spaceId);
|
||||||
Result OpenSaveDataInfoReaderOnlyCacheStorage(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader);
|
Result OpenSaveDataInfoReaderOnlyCacheStorage(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader);
|
||||||
Result OpenSaveDataInternalStorageFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, SaveDataSpaceId spaceId, ulong saveDataId);
|
Result OpenSaveDataInternalStorageFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, SaveDataSpaceId spaceId, ulong saveDataId);
|
||||||
Result UpdateSaveDataMacForDebug(SaveDataSpaceId spaceId, ulong saveDataId);
|
Result UpdateSaveDataMacForDebug(SaveDataSpaceId spaceId, ulong saveDataId);
|
||||||
Result WriteSaveDataFileSystemExtraDataWithMask(ulong saveDataId, SaveDataSpaceId spaceId, ReadOnlySpan<byte> extraDataBuffer, ReadOnlySpan<byte> maskBuffer);
|
Result WriteSaveDataFileSystemExtraDataWithMask(ulong saveDataId, SaveDataSpaceId spaceId, InBuffer extraDataBuffer, InBuffer maskBuffer);
|
||||||
Result FindSaveDataWithFilter(out long count, Span<byte> saveDataInfoBuffer, SaveDataSpaceId spaceId, ref SaveDataFilter filter);
|
Result FindSaveDataWithFilter(out long count, OutBuffer saveDataInfoBuffer, SaveDataSpaceId spaceId, in SaveDataFilter filter);
|
||||||
Result OpenSaveDataInfoReaderWithFilter(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader, SaveDataSpaceId spaceId, ref SaveDataFilter filter);
|
Result OpenSaveDataInfoReaderWithFilter(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader, SaveDataSpaceId spaceId, in SaveDataFilter filter);
|
||||||
Result ReadSaveDataFileSystemExtraDataBySaveDataAttribute(Span<byte> extraDataBuffer, SaveDataSpaceId spaceId, ref SaveDataAttribute attribute);
|
Result ReadSaveDataFileSystemExtraDataBySaveDataAttribute(OutBuffer extraDataBuffer, SaveDataSpaceId spaceId, in SaveDataAttribute attribute);
|
||||||
Result WriteSaveDataFileSystemExtraDataBySaveDataAttribute(ref SaveDataAttribute attribute, SaveDataSpaceId spaceId, ReadOnlySpan<byte> extraDataBuffer, ReadOnlySpan<byte> maskBuffer);
|
Result WriteSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(in SaveDataAttribute attribute, SaveDataSpaceId spaceId, InBuffer extraDataBuffer, InBuffer maskBuffer);
|
||||||
Result OpenSaveDataMetaFile(out IFile file, SaveDataSpaceId spaceId, ref SaveDataAttribute attribute, SaveDataMetaType type);
|
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 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 OpenImageDirectoryFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, ImageDirectoryId directoryId);
|
||||||
Result OpenContentStorageFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, ContentStorageId storageId);
|
Result OpenContentStorageFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, ContentStorageId storageId);
|
||||||
Result OpenCloudBackupWorkStorageFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, CloudBackupWorkStorageId storageId);
|
Result OpenCloudBackupWorkStorageFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, CloudBackupWorkStorageId storageId);
|
||||||
@ -73,7 +75,7 @@ namespace LibHac.FsSrv
|
|||||||
Result NotifySystemDataUpdateEvent();
|
Result NotifySystemDataUpdateEvent();
|
||||||
|
|
||||||
Result QuerySaveDataTotalSize(out long totalSize, long dataSize, long journalSize);
|
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 CorruptSaveDataFileSystem(ulong saveDataId);
|
||||||
Result CreatePaddingFile(long size);
|
Result CreatePaddingFile(long size);
|
||||||
Result DeleteAllPaddingFiles();
|
Result DeleteAllPaddingFiles();
|
||||||
@ -84,7 +86,7 @@ namespace LibHac.FsSrv
|
|||||||
Result GetRightsIdAndKeyGenerationByPath(out RightsId rightsId, out byte keyGeneration, in FspPath path);
|
Result GetRightsIdAndKeyGenerationByPath(out RightsId rightsId, out byte keyGeneration, in FspPath path);
|
||||||
Result SetCurrentPosixTimeWithTimeDifference(long time, int difference);
|
Result SetCurrentPosixTimeWithTimeDifference(long time, int difference);
|
||||||
Result GetFreeSpaceSizeForSaveData(out long freeSpaceSize, SaveDataSpaceId spaceId);
|
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 CorruptSaveDataFileSystemBySaveDataSpaceId(SaveDataSpaceId spaceId, ulong saveDataId);
|
||||||
Result QuerySaveDataInternalStorageTotalSize(out long size, SaveDataSpaceId spaceId, ulong saveDataId);
|
Result QuerySaveDataInternalStorageTotalSize(out long size, SaveDataSpaceId spaceId, ulong saveDataId);
|
||||||
Result GetSaveDataCommitId(out long commitId, SaveDataSpaceId spaceId, ulong saveDataId);
|
Result GetSaveDataCommitId(out long commitId, SaveDataSpaceId spaceId, ulong saveDataId);
|
||||||
@ -93,10 +95,10 @@ namespace LibHac.FsSrv
|
|||||||
Result SetSdCardAccessibility(bool isAccessible);
|
Result SetSdCardAccessibility(bool isAccessible);
|
||||||
Result IsSdCardAccessible(out bool isAccessible);
|
Result IsSdCardAccessible(out bool isAccessible);
|
||||||
|
|
||||||
Result RegisterProgramIndexMapInfo(ReadOnlySpan<byte> programIndexMapInfoBuffer, int programCount);
|
Result RegisterProgramIndexMapInfo(InBuffer programIndexMapInfoBuffer, int programCount);
|
||||||
Result SetBisRootForHost(BisPartitionId partitionId, ref FsPath path);
|
Result SetBisRootForHost(BisPartitionId partitionId, ref FsPath path);
|
||||||
Result SetSaveDataSize(long saveDataSize, long saveDataJournalSize);
|
Result SetSaveDataSize(long saveDataSize, long saveDataJournalSize);
|
||||||
Result SetSaveDataRootPath(ref FsPath path);
|
Result SetSaveDataRootPath(in FspPath path);
|
||||||
Result DisableAutoSaveDataCreation();
|
Result DisableAutoSaveDataCreation();
|
||||||
Result SetGlobalAccessLogMode(GlobalAccessLogMode mode);
|
Result SetGlobalAccessLogMode(GlobalAccessLogMode mode);
|
||||||
Result GetGlobalAccessLogMode(out GlobalAccessLogMode mode);
|
Result GetGlobalAccessLogMode(out GlobalAccessLogMode mode);
|
||||||
@ -105,9 +107,10 @@ namespace LibHac.FsSrv
|
|||||||
Result OpenRegisteredUpdatePartition(out ReferenceCountedDisposable<IFileSystemSf> fileSystem);
|
Result OpenRegisteredUpdatePartition(out ReferenceCountedDisposable<IFileSystemSf> fileSystem);
|
||||||
|
|
||||||
Result GetProgramIndexForAccessLog(out int programIndex, out int programCount);
|
Result GetProgramIndexForAccessLog(out int programIndex, out int programCount);
|
||||||
|
Result UnsetSaveDataRootPath();
|
||||||
Result OverrideSaveDataTransferTokenSignVerificationKey(ReadOnlySpan<byte> key);
|
Result OverrideSaveDataTransferTokenSignVerificationKey(ReadOnlySpan<byte> key);
|
||||||
Result CorruptSaveDataFileSystemByOffset(SaveDataSpaceId spaceId, ulong saveDataId, long offset);
|
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);
|
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();
|
int GetIndexCount();
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </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>
|
/// <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
|
public interface ISaveDataIndexerManager
|
||||||
{
|
{
|
||||||
Result OpenAccessor(out SaveDataIndexerAccessor accessor, out bool neededInit, SaveDataSpaceId spaceId);
|
Result OpenSaveDataIndexerAccessor(out SaveDataIndexerAccessor accessor, out bool neededInit, SaveDataSpaceId spaceId);
|
||||||
void ResetTemporaryStorageIndexer(SaveDataSpaceId spaceId);
|
void ResetIndexer(SaveDataSpaceId spaceId);
|
||||||
void InvalidateSdCardIndexer(SaveDataSpaceId spaceId);
|
void InvalidateIndexer(SaveDataSpaceId spaceId);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -830,6 +830,7 @@ namespace LibHac.FsSrv.Impl
|
|||||||
RegisterProgramIndexMapInfo,
|
RegisterProgramIndexMapInfo,
|
||||||
ChallengeCardExistence,
|
ChallengeCardExistence,
|
||||||
CreateOwnSaveData,
|
CreateOwnSaveData,
|
||||||
|
DeleteOwnSaveData,
|
||||||
ReadOwnSaveDataFileSystemExtraData,
|
ReadOwnSaveDataFileSystemExtraData,
|
||||||
ExtendOwnSaveData,
|
ExtendOwnSaveData,
|
||||||
OpenOwnSaveDataTransferProhibiter,
|
OpenOwnSaveDataTransferProhibiter,
|
||||||
|
37
src/LibHac/FsSrv/Impl/AsynchronousAccessFileSystem.cs
Normal file
37
src/LibHac/FsSrv/Impl/AsynchronousAccessFileSystem.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.Fs.Fsa;
|
||||||
|
using LibHac.FsSystem;
|
||||||
|
|
||||||
|
namespace LibHac.FsSrv.Impl
|
||||||
|
{
|
||||||
|
public class AsynchronousAccessFileSystem : ForwardingFileSystem
|
||||||
|
{
|
||||||
|
public AsynchronousAccessFileSystem(ReferenceCountedDisposable<IFileSystem> baseFileSystem) : base(
|
||||||
|
baseFileSystem)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
protected AsynchronousAccessFileSystem(ref ReferenceCountedDisposable<IFileSystem> baseFileSystem) : base(
|
||||||
|
ref baseFileSystem)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public static ReferenceCountedDisposable<IFileSystem> CreateShared(
|
||||||
|
ReferenceCountedDisposable<IFileSystem> baseFileSystem)
|
||||||
|
{
|
||||||
|
return new ReferenceCountedDisposable<IFileSystem>(new AsynchronousAccessFileSystem(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -63,7 +63,7 @@ namespace LibHac.FsSrv.Impl
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="baseFileSystem">The base file system. Will be null upon returning.</param>
|
/// <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>
|
/// <param name="isHostFsRoot">Does the base file system come from the root directory of a host file system?</param>
|
||||||
public static ReferenceCountedDisposable<IFileSystemSf> CreateSharedSfFileSystem(
|
public static ReferenceCountedDisposable<IFileSystemSf> CreateShared(
|
||||||
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, bool isHostFsRoot = false)
|
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, bool isHostFsRoot = false)
|
||||||
{
|
{
|
||||||
var adapter = new FileSystemInterfaceAdapter(ref baseFileSystem, isHostFsRoot);
|
var adapter = new FileSystemInterfaceAdapter(ref baseFileSystem, isHostFsRoot);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
14
src/LibHac/FsSrv/Impl/ISaveDataMultiCommitCoreInterface.cs
Normal file
14
src/LibHac/FsSrv/Impl/ISaveDataMultiCommitCoreInterface.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.Fs.Fsa;
|
||||||
|
|
||||||
|
namespace LibHac.FsSrv.Impl
|
||||||
|
{
|
||||||
|
public interface ISaveDataMultiCommitCoreInterface : IDisposable
|
||||||
|
{
|
||||||
|
Result RecoverMultiCommit();
|
||||||
|
Result IsProvisionallyCommittedSaveData(out bool isProvisionallyCommitted, in SaveDataInfo saveInfo);
|
||||||
|
Result RecoverProvisionallyCommittedSaveData(in SaveDataInfo saveInfo, bool doRollback);
|
||||||
|
Result OpenMultiCommitContext(out ReferenceCountedDisposable<IFileSystem> contextFileSystem);
|
||||||
|
}
|
||||||
|
}
|
30
src/LibHac/FsSrv/Impl/ISaveDataTransferCoreInterface.cs
Normal file
30
src/LibHac/FsSrv/Impl/ISaveDataTransferCoreInterface.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.Fs.Fsa;
|
||||||
|
using LibHac.FsSrv.Sf;
|
||||||
|
using LibHac.Util;
|
||||||
|
|
||||||
|
namespace LibHac.FsSrv.Impl
|
||||||
|
{
|
||||||
|
public interface ISaveDataTransferCoreInterface : IDisposable
|
||||||
|
{
|
||||||
|
Result GetFreeSpaceSizeForSaveData(out long freeSpaceSize, SaveDataSpaceId spaceId);
|
||||||
|
Result QuerySaveDataTotalSize(out long totalSize, long dataSize, long journalSize);
|
||||||
|
Result CheckSaveDataFile(long saveDataId, SaveDataSpaceId spaceId);
|
||||||
|
Result CreateSaveDataFileSystemCore(in SaveDataAttribute attribute, in SaveDataCreationInfo creationInfo, in SaveDataMetaInfo metaInfo, in Optional<HashSalt> hashSalt, bool leaveUnfinalized);
|
||||||
|
Result GetSaveDataInfo(out SaveDataInfo saveInfo, SaveDataSpaceId spaceId, in SaveDataAttribute attribute);
|
||||||
|
Result ReadSaveDataFileSystemExtraDataCore(out SaveDataExtraData extraData, SaveDataSpaceId spaceId, ulong saveDataId, bool isTemporarySaveData);
|
||||||
|
Result WriteSaveDataFileSystemExtraDataCore(SaveDataSpaceId spaceId, ulong saveDataId, in SaveDataExtraData extraData, SaveDataType type, bool updateTimeStamp);
|
||||||
|
Result FinalizeSaveDataCreation(ulong saveDataId, SaveDataSpaceId spaceId);
|
||||||
|
Result CancelSaveDataCreation(ulong saveDataId, SaveDataSpaceId spaceId);
|
||||||
|
Result OpenSaveDataFile(out ReferenceCountedDisposable<IFileSf> file, SaveDataSpaceId spaceId, in SaveDataAttribute attribute, SaveDataMetaType metaType);
|
||||||
|
Result OpenSaveDataMetaFileRaw(out ReferenceCountedDisposable<IFile> file, SaveDataSpaceId spaceId, ulong saveDataId, SaveDataMetaType metaType, OpenMode mode);
|
||||||
|
Result OpenSaveDataInternalStorageFileSystemCore(out ReferenceCountedDisposable<IFileSystem> fileSystem, SaveDataSpaceId spaceId, ulong saveDataId, bool useSecondMacKey);
|
||||||
|
Result ExtendSaveDataFileSystem(SaveDataSpaceId spaceId, ulong saveDataId, long dataSize, long journalSize);
|
||||||
|
Result DeleteSaveDataFileSystemBySaveDataSpaceId(SaveDataSpaceId spaceId, ulong saveDataId);
|
||||||
|
Result SwapSaveDataKeyAndState(SaveDataSpaceId spaceId, ulong saveDataId1, ulong saveDataId2);
|
||||||
|
Result SetSaveDataState(SaveDataSpaceId spaceId, ulong saveDataId, SaveDataState state);
|
||||||
|
Result SetSaveDataRank(SaveDataSpaceId spaceId, ulong saveDataId, SaveDataRank rank);
|
||||||
|
Result OpenSaveDataIndexerAccessor(out SaveDataIndexerAccessor accessor, SaveDataSpaceId spaceId);
|
||||||
|
}
|
||||||
|
}
|
@ -1,93 +1,154 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
using LibHac.Fs.Shim;
|
using LibHac.Fs.Shim;
|
||||||
|
using LibHac.FsSrv.Sf;
|
||||||
|
using LibHac.Sf;
|
||||||
|
|
||||||
namespace LibHac.FsSrv.Impl
|
namespace LibHac.FsSrv.Impl
|
||||||
{
|
{
|
||||||
internal class MultiCommitManager : IMultiCommitManager
|
internal class MultiCommitManager : IMultiCommitManager
|
||||||
{
|
{
|
||||||
private const int MaxFileSystemCount = 10;
|
private const int MaxFileSystemCount = 10;
|
||||||
private const int CurrentContextVersion = 0x10000;
|
|
||||||
|
|
||||||
public const ulong ProgramId = 0x100000000000000;
|
public const ulong ProgramId = 0x0100000000000000;
|
||||||
public const ulong SaveDataId = 0x8000000000000001;
|
public const ulong SaveDataId = 0x8000000000000001;
|
||||||
private const long SaveDataSize = 0xC000;
|
private const long SaveDataSize = 0xC000;
|
||||||
private const long SaveJournalSize = 0xC000;
|
private const long SaveJournalSize = 0xC000;
|
||||||
|
|
||||||
private const long ContextFileSize = 0x200;
|
private const int CurrentCommitContextVersion = 0x10000;
|
||||||
|
private const long CommitContextFileSize = 0x200;
|
||||||
|
|
||||||
// /commitinfo
|
// /commitinfo
|
||||||
private static U8Span ContextFileName =>
|
private static U8Span CommitContextFileName =>
|
||||||
new U8Span(new[] { (byte)'/', (byte)'c', (byte)'o', (byte)'m', (byte)'m', (byte)'i', (byte)'t', (byte)'i', (byte)'n', (byte)'f', (byte)'o' });
|
new U8Span(new[] { (byte)'/', (byte)'c', (byte)'o', (byte)'m', (byte)'m', (byte)'i', (byte)'t', (byte)'i', (byte)'n', (byte)'f', (byte)'o' });
|
||||||
|
|
||||||
|
// Todo: Don't use global lock object
|
||||||
private static readonly object Locker = new object();
|
private static readonly object Locker = new object();
|
||||||
|
|
||||||
private FileSystemProxyImpl FsProxy { get; }
|
private ReferenceCountedDisposable<ISaveDataMultiCommitCoreInterface> MultiCommitInterface { get; }
|
||||||
private List<IFileSystem> FileSystems { get; } = new List<IFileSystem>(MaxFileSystemCount);
|
|
||||||
private long CommitCount { get; set; }
|
|
||||||
|
|
||||||
public MultiCommitManager(FileSystemProxyImpl fsProxy)
|
private List<ReferenceCountedDisposable<IFileSystem>> FileSystems { get; } =
|
||||||
|
new List<ReferenceCountedDisposable<IFileSystem>>(MaxFileSystemCount);
|
||||||
|
|
||||||
|
private long Counter { get; set; }
|
||||||
|
private HorizonClient Hos { get; }
|
||||||
|
|
||||||
|
public MultiCommitManager(
|
||||||
|
ref ReferenceCountedDisposable<ISaveDataMultiCommitCoreInterface> multiCommitInterface,
|
||||||
|
HorizonClient client)
|
||||||
{
|
{
|
||||||
FsProxy = fsProxy;
|
Hos = client;
|
||||||
|
MultiCommitInterface = Shared.Move(ref multiCommitInterface);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result Add(IFileSystem fileSystem)
|
public static ReferenceCountedDisposable<IMultiCommitManager> CreateShared(
|
||||||
|
ref ReferenceCountedDisposable<ISaveDataMultiCommitCoreInterface> multiCommitInterface,
|
||||||
|
HorizonClient client)
|
||||||
|
{
|
||||||
|
var manager = new MultiCommitManager(ref multiCommitInterface, client);
|
||||||
|
return new ReferenceCountedDisposable<IMultiCommitManager>(manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
foreach (ReferenceCountedDisposable<IFileSystem> fs in FileSystems)
|
||||||
|
{
|
||||||
|
fs.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensures the save data used to store the commit context exists.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||||
|
private Result EnsureSaveDataForContext()
|
||||||
|
{
|
||||||
|
Result rc = MultiCommitInterface.Target.OpenMultiCommitContext(
|
||||||
|
out ReferenceCountedDisposable<IFileSystem> contextFs);
|
||||||
|
|
||||||
|
if (rc.IsFailure())
|
||||||
|
{
|
||||||
|
if (!ResultFs.TargetNotFound.Includes(rc))
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
rc = Hos.Fs.CreateSystemSaveData(SaveDataId, SaveDataSize, SaveJournalSize, SaveDataFlags.None);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
contextFs?.Dispose();
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a file system to the list of file systems to be committed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fileSystem">The file system to be committed.</param>
|
||||||
|
/// <returns><see cref="Result.Success"/>: The operation was successful.<br/>
|
||||||
|
/// <see cref="ResultFs.MultiCommitFileSystemLimit"/>: The maximum number of file systems have been added.
|
||||||
|
/// <see cref="MaxFileSystemCount"/> file systems may be added to a single multi-commit.<br/>
|
||||||
|
/// <see cref="ResultFs.MultiCommitFileSystemAlreadyAdded"/>: The provided file system has already been added.</returns>
|
||||||
|
public Result Add(ReferenceCountedDisposable<IFileSystemSf> fileSystem)
|
||||||
{
|
{
|
||||||
if (FileSystems.Count >= MaxFileSystemCount)
|
if (FileSystems.Count >= MaxFileSystemCount)
|
||||||
return ResultFs.MultiCommitFileSystemLimit.Log();
|
return ResultFs.MultiCommitFileSystemLimit.Log();
|
||||||
|
|
||||||
// Check that the file system hasn't already been added
|
ReferenceCountedDisposable<IFileSystem> fsaFileSystem = null;
|
||||||
for (int i = 0; i < FileSystems.Count; i++)
|
try
|
||||||
{
|
{
|
||||||
if (ReferenceEquals(FileSystems[i], fileSystem))
|
Result rc = fileSystem.Target.GetImpl(out fsaFileSystem);
|
||||||
return ResultFs.MultiCommitFileSystemAlreadyAdded.Log();
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
// Check that the file system hasn't already been added
|
||||||
|
foreach (ReferenceCountedDisposable<IFileSystem> fs in FileSystems)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(fs.Target, fsaFileSystem.Target))
|
||||||
|
return ResultFs.MultiCommitFileSystemAlreadyAdded.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSystems.Add(fsaFileSystem);
|
||||||
|
fsaFileSystem = null;
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
FileSystems.Add(fileSystem);
|
|
||||||
return Result.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result Commit()
|
|
||||||
{
|
|
||||||
lock (Locker)
|
|
||||||
{
|
{
|
||||||
Result rc = CreateSave();
|
fsaFileSystem?.Dispose();
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
rc = FsProxy.OpenMultiCommitContextSaveData(out IFileSystem contextFs);
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
return CommitImpl(contextFs);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result CommitImpl(IFileSystem contextFileSystem)
|
/// <summary>
|
||||||
|
/// Commits all added file systems using <paramref name="contextFileSystem"/> to
|
||||||
|
/// store the <see cref="Context"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="contextFileSystem">The file system where the commit context will be stored.</param>
|
||||||
|
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||||
|
private Result Commit(IFileSystem contextFileSystem)
|
||||||
{
|
{
|
||||||
var context = new CommitContextManager(contextFileSystem);
|
ContextUpdater context = default;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
CommitCount = 1;
|
Counter = 1;
|
||||||
|
|
||||||
Result rc = context.Initialize(CommitCount, FileSystems.Count);
|
context = new ContextUpdater(contextFileSystem);
|
||||||
|
Result rc = context.Create(Counter, FileSystems.Count);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = CommitProvisionally();
|
rc = CommitProvisionallyFileSystem(Counter);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = context.SetCommittedProvisionally();
|
rc = context.CommitProvisionallyDone();
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
foreach (IFileSystem fs in FileSystems)
|
rc = CommitFileSystem();
|
||||||
{
|
if (rc.IsFailure()) return rc;
|
||||||
rc = fs.Commit();
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = context.Close();
|
rc = context.CommitDone();
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@ -98,34 +159,45 @@ namespace LibHac.FsSrv.Impl
|
|||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result CreateSave()
|
/// <summary>
|
||||||
|
/// Commits all added file systems.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||||
|
public Result Commit()
|
||||||
{
|
{
|
||||||
Result rc = FsProxy.OpenMultiCommitContextSaveData(out IFileSystem contextFs);
|
lock (Locker)
|
||||||
|
|
||||||
if (rc.IsFailure())
|
|
||||||
{
|
{
|
||||||
if (!ResultFs.TargetNotFound.Includes(rc))
|
Result rc = EnsureSaveDataForContext();
|
||||||
{
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = FsProxy.Hos.Fs.CreateSystemSaveData(SaveDataId, SaveDataSize, SaveJournalSize,
|
|
||||||
SaveDataFlags.None);
|
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
}
|
|
||||||
|
|
||||||
contextFs?.Dispose();
|
ReferenceCountedDisposable<IFileSystem> contextFs = null;
|
||||||
return Result.Success;
|
try
|
||||||
|
{
|
||||||
|
rc = MultiCommitInterface.Target.OpenMultiCommitContext(out contextFs);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
return Commit(contextFs.Target);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
contextFs?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result CommitProvisionally()
|
/// <summary>
|
||||||
|
/// Tries to provisionally commit all the added file systems.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="counter">The provisional commit counter.</param>
|
||||||
|
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||||
|
private Result CommitProvisionallyFileSystem(long counter)
|
||||||
{
|
{
|
||||||
Result rc = Result.Success;
|
Result rc = Result.Success;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < FileSystems.Count; i++)
|
for (i = 0; i < FileSystems.Count; i++)
|
||||||
{
|
{
|
||||||
rc = FileSystems[i].CommitProvisionally(CommitCount);
|
rc = FileSystems[i].Target.CommitProvisionally(counter);
|
||||||
|
|
||||||
if (rc.IsFailure())
|
if (rc.IsFailure())
|
||||||
break;
|
break;
|
||||||
@ -136,20 +208,294 @@ namespace LibHac.FsSrv.Impl
|
|||||||
// Rollback all provisional commits including the failed commit
|
// Rollback all provisional commits including the failed commit
|
||||||
for (int j = 0; j <= i; j++)
|
for (int j = 0; j <= i; j++)
|
||||||
{
|
{
|
||||||
FileSystems[j].Rollback().IgnoreResult();
|
FileSystems[j].Target.Rollback().IgnoreResult();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to fully commit all the added file systems.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||||
|
private Result CommitFileSystem()
|
||||||
|
{
|
||||||
|
// All file systems will try to be recovered committed, even if one fails.
|
||||||
|
// If any commits fail, the result from the first failed recovery will be returned.
|
||||||
|
Result result = Result.Success;
|
||||||
|
|
||||||
|
foreach (ReferenceCountedDisposable<IFileSystem> fs in FileSystems)
|
||||||
|
{
|
||||||
|
Result rc = fs.Target.Commit();
|
||||||
|
|
||||||
|
if (result.IsSuccess() && rc.IsFailure())
|
||||||
|
{
|
||||||
|
result = rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Recovers a multi-commit that was interrupted after all file systems had been provisionally committed.
|
||||||
|
/// The recovery will finish committing any file systems that are still provisionally committed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="multiCommitInterface">The core interface used for multi-commits.</param>
|
||||||
|
/// <param name="contextFs">The file system containing the multi-commit context file.</param>
|
||||||
|
/// <param name="saveService">The save data service.</param>
|
||||||
|
/// <returns><see cref="Result.Success"/>: The operation was successful.<br/>
|
||||||
|
/// <see cref="ResultFs.InvalidMultiCommitContextVersion"/>: The version of the commit context
|
||||||
|
/// file isn't supported.<br/>
|
||||||
|
/// <see cref="ResultFs.InvalidMultiCommitContextState"/>: The multi-commit hadn't finished
|
||||||
|
/// provisionally committing all the file systems.</returns>
|
||||||
|
private static Result RecoverCommit(ISaveDataMultiCommitCoreInterface multiCommitInterface,
|
||||||
|
IFileSystem contextFs, SaveDataFileSystemServiceImpl saveService)
|
||||||
|
{
|
||||||
|
IFile contextFile = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Read the multi-commit context
|
||||||
|
Result rc = contextFs.OpenFile(out contextFile, CommitContextFileName, OpenMode.ReadWrite);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
Unsafe.SkipInit(out Context context);
|
||||||
|
rc = contextFile.Read(out _, 0, SpanHelpers.AsByteSpan(ref context), ReadOption.None);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
// Note: Nintendo doesn't check if the proper amount of bytes were read, but it
|
||||||
|
// doesn't really matter since the context is validated.
|
||||||
|
if (context.Version > CurrentCommitContextVersion)
|
||||||
|
return ResultFs.InvalidMultiCommitContextVersion.Log();
|
||||||
|
|
||||||
|
// All the file systems in the multi-commit must have been at least provisionally committed
|
||||||
|
// before we can try to recover the commit.
|
||||||
|
if (context.State != CommitState.ProvisionallyCommitted)
|
||||||
|
return ResultFs.InvalidMultiCommitContextState.Log();
|
||||||
|
|
||||||
|
// Keep track of the first error that occurs during the recovery
|
||||||
|
Result recoveryResult = Result.Success;
|
||||||
|
|
||||||
|
int saveCount = 0;
|
||||||
|
Span<SaveDataInfo> savesToRecover = stackalloc SaveDataInfo[MaxFileSystemCount];
|
||||||
|
|
||||||
|
SaveDataIndexerAccessor accessor = null;
|
||||||
|
ReferenceCountedDisposable<SaveDataInfoReaderImpl> infoReader = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
rc = saveService.OpenSaveDataIndexerAccessor(out accessor, out _, SaveDataSpaceId.User);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
rc = accessor.Indexer.OpenSaveDataInfoReader(out infoReader);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
// Iterate through all the saves to find any provisionally committed save data
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
Unsafe.SkipInit(out SaveDataInfo info);
|
||||||
|
|
||||||
|
rc = infoReader.Target.Read(out long readCount, OutBuffer.FromStruct(ref info));
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
// Break once we're done iterating all save data
|
||||||
|
if (readCount == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
rc = multiCommitInterface.IsProvisionallyCommittedSaveData(out bool isProvisionallyCommitted,
|
||||||
|
in info);
|
||||||
|
|
||||||
|
// Note: Some saves could be missed if there are more than MaxFileSystemCount
|
||||||
|
// provisionally committed saves. Not sure why Nintendo doesn't catch this.
|
||||||
|
if (rc.IsSuccess() && isProvisionallyCommitted && saveCount < MaxFileSystemCount)
|
||||||
|
{
|
||||||
|
savesToRecover[saveCount] = info;
|
||||||
|
saveCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
accessor?.Dispose();
|
||||||
|
infoReader?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recover the saves by finishing their commits.
|
||||||
|
// All file systems will try to be recovered, even if one fails.
|
||||||
|
// If any commits fail, the result from the first failed recovery will be returned.
|
||||||
|
for (int i = 0; i < saveCount; i++)
|
||||||
|
{
|
||||||
|
rc = multiCommitInterface.RecoverProvisionallyCommittedSaveData(in savesToRecover[i], false);
|
||||||
|
|
||||||
|
if (recoveryResult.IsSuccess() && rc.IsFailure())
|
||||||
|
{
|
||||||
|
recoveryResult = rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return recoveryResult;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
contextFile?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to recover a multi-commit using the context in the provided file system.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="multiCommitInterface">The core interface used for multi-commits.</param>
|
||||||
|
/// <param name="contextFs">The file system containing the multi-commit context file.</param>
|
||||||
|
/// <param name="saveService">The save data service.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static Result Recover(ISaveDataMultiCommitCoreInterface multiCommitInterface, IFileSystem contextFs,
|
||||||
|
SaveDataFileSystemServiceImpl saveService)
|
||||||
|
{
|
||||||
|
if (multiCommitInterface is null)
|
||||||
|
return ResultFs.InvalidArgument.Log();
|
||||||
|
|
||||||
|
if (contextFs is null)
|
||||||
|
return ResultFs.InvalidArgument.Log();
|
||||||
|
|
||||||
|
// Keep track of the first error that occurs during the recovery
|
||||||
|
Result recoveryResult = Result.Success;
|
||||||
|
|
||||||
|
Result rc = RecoverCommit(multiCommitInterface, contextFs, saveService);
|
||||||
|
|
||||||
|
if (rc.IsFailure())
|
||||||
|
{
|
||||||
|
// Note: Yes, the next ~50 lines are exactly the same as the code in RecoverCommit except
|
||||||
|
// for a single bool value. No, Nintendo doesn't split it out into its own function.
|
||||||
|
int saveCount = 0;
|
||||||
|
Span<SaveDataInfo> savesToRecover = stackalloc SaveDataInfo[MaxFileSystemCount];
|
||||||
|
|
||||||
|
SaveDataIndexerAccessor accessor = null;
|
||||||
|
ReferenceCountedDisposable<SaveDataInfoReaderImpl> infoReader = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
rc = saveService.OpenSaveDataIndexerAccessor(out accessor, out _, SaveDataSpaceId.User);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
rc = accessor.Indexer.OpenSaveDataInfoReader(out infoReader);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
// Iterate through all the saves to find any provisionally committed save data
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
Unsafe.SkipInit(out SaveDataInfo info);
|
||||||
|
|
||||||
|
rc = infoReader.Target.Read(out long readCount, OutBuffer.FromStruct(ref info));
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
// Break once we're done iterating all save data
|
||||||
|
if (readCount == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
rc = multiCommitInterface.IsProvisionallyCommittedSaveData(out bool isProvisionallyCommitted,
|
||||||
|
in info);
|
||||||
|
|
||||||
|
// Note: Some saves could be missed if there are more than MaxFileSystemCount
|
||||||
|
// provisionally committed saves. Not sure why Nintendo doesn't catch this.
|
||||||
|
if (rc.IsSuccess() && isProvisionallyCommitted && saveCount < MaxFileSystemCount)
|
||||||
|
{
|
||||||
|
savesToRecover[saveCount] = info;
|
||||||
|
saveCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
accessor?.Dispose();
|
||||||
|
infoReader?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recover the saves by rolling them back to the previous commit.
|
||||||
|
// All file systems will try to be recovered, even if one fails.
|
||||||
|
// If any commits fail, the result from the first failed recovery will be returned.
|
||||||
|
for (int i = 0; i < saveCount; i++)
|
||||||
|
{
|
||||||
|
rc = multiCommitInterface.RecoverProvisionallyCommittedSaveData(in savesToRecover[i], true);
|
||||||
|
|
||||||
|
if (recoveryResult.IsSuccess() && rc.IsFailure())
|
||||||
|
{
|
||||||
|
recoveryResult = rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the commit context file
|
||||||
|
rc = contextFs.DeleteFile(CommitContextFileName);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
rc = contextFs.Commit();
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
return recoveryResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Recovers an interrupted multi-commit. The commit will either be completed or rolled back depending on
|
||||||
|
/// where in the commit process it was interrupted. Does nothing if there is no commit to recover.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="multiCommitInterface">The core interface used for multi-commits.</param>
|
||||||
|
/// <param name="saveService">The save data service.</param>
|
||||||
|
/// <returns>The <see cref="Result"/> of the operation.<br/>
|
||||||
|
/// <see cref="Result.Success"/>: The recovery was successful or there was no multi-commit to recover.</returns>
|
||||||
|
public static Result Recover(ISaveDataMultiCommitCoreInterface multiCommitInterface,
|
||||||
|
SaveDataFileSystemServiceImpl saveService)
|
||||||
|
{
|
||||||
|
lock (Locker)
|
||||||
|
{
|
||||||
|
bool needsRecover = true;
|
||||||
|
ReferenceCountedDisposable<IFileSystem> fileSystem = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Check if a multi-commit was interrupted by checking if there's a commit context file.
|
||||||
|
Result rc = multiCommitInterface.OpenMultiCommitContext(out fileSystem);
|
||||||
|
|
||||||
|
if (rc.IsFailure())
|
||||||
|
{
|
||||||
|
if (!ResultFs.PathNotFound.Includes(rc) && !ResultFs.TargetNotFound.Includes(rc))
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
// Unable to open the multi-commit context file system, so there's nothing to recover
|
||||||
|
needsRecover = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsRecover)
|
||||||
|
{
|
||||||
|
rc = fileSystem.Target.OpenFile(out IFile file, CommitContextFileName, OpenMode.Read);
|
||||||
|
file?.Dispose();
|
||||||
|
|
||||||
|
if (rc.IsFailure())
|
||||||
|
{
|
||||||
|
// Unable to open the context file. No multi-commit to recover.
|
||||||
|
if (ResultFs.PathNotFound.Includes(rc))
|
||||||
|
needsRecover = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!needsRecover)
|
||||||
|
return Result.Success;
|
||||||
|
|
||||||
|
// There was a context file. Recover the unfinished commit.
|
||||||
|
return Recover(multiCommitInterface, fileSystem.Target, saveService);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
fileSystem?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x18)]
|
[StructLayout(LayoutKind.Explicit, Size = 0x18)]
|
||||||
private struct CommitContext
|
private struct Context
|
||||||
{
|
{
|
||||||
[FieldOffset(0x00)] public int Version;
|
[FieldOffset(0x00)] public int Version;
|
||||||
[FieldOffset(0x04)] public CommitState State;
|
[FieldOffset(0x04)] public CommitState State;
|
||||||
[FieldOffset(0x08)] public int FileSystemCount;
|
[FieldOffset(0x08)] public int FileSystemCount;
|
||||||
[FieldOffset(0x10)] public long CommitCount; // I think?
|
[FieldOffset(0x10)] public long Counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum CommitState
|
private enum CommitState
|
||||||
@ -160,35 +506,41 @@ namespace LibHac.FsSrv.Impl
|
|||||||
ProvisionallyCommitted = 2
|
ProvisionallyCommitted = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct CommitContextManager
|
private struct ContextUpdater
|
||||||
{
|
{
|
||||||
private IFileSystem _fileSystem;
|
private IFileSystem _fileSystem;
|
||||||
private CommitContext _context;
|
private Context _context;
|
||||||
|
|
||||||
public CommitContextManager(IFileSystem contextFileSystem)
|
public ContextUpdater(IFileSystem contextFileSystem)
|
||||||
{
|
{
|
||||||
_fileSystem = contextFileSystem;
|
_fileSystem = contextFileSystem;
|
||||||
_context = default;
|
_context = default;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result Initialize(long commitCount, int fileSystemCount)
|
/// <summary>
|
||||||
|
/// Creates and writes the initial commit context to a file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="commitCount">The counter.</param>
|
||||||
|
/// <param name="fileSystemCount">The number of file systems being committed.</param>
|
||||||
|
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||||
|
public Result Create(long commitCount, int fileSystemCount)
|
||||||
{
|
{
|
||||||
IFile contextFile = null;
|
IFile contextFile = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Open context file and create if it doesn't exist
|
// Open context file and create if it doesn't exist
|
||||||
Result rc = _fileSystem.OpenFile(out contextFile, ContextFileName, OpenMode.Read);
|
Result rc = _fileSystem.OpenFile(out contextFile, CommitContextFileName, OpenMode.Read);
|
||||||
|
|
||||||
if (rc.IsFailure())
|
if (rc.IsFailure())
|
||||||
{
|
{
|
||||||
if (!ResultFs.PathNotFound.Includes(rc))
|
if (!ResultFs.PathNotFound.Includes(rc))
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
rc = _fileSystem.CreateFile(ContextFileName, ContextFileSize, CreateFileOptions.None);
|
rc = _fileSystem.CreateFile(CommitContextFileName, CommitContextFileSize, CreateFileOptions.None);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = _fileSystem.OpenFile(out contextFile, ContextFileName, OpenMode.Read);
|
rc = _fileSystem.OpenFile(out contextFile, CommitContextFileName, OpenMode.Read);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -199,13 +551,13 @@ namespace LibHac.FsSrv.Impl
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Result rc = _fileSystem.OpenFile(out contextFile, ContextFileName, OpenMode.ReadWrite);
|
Result rc = _fileSystem.OpenFile(out contextFile, CommitContextFileName, OpenMode.ReadWrite);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
_context.Version = CurrentContextVersion;
|
_context.Version = CurrentCommitContextVersion;
|
||||||
_context.State = CommitState.NotCommitted;
|
_context.State = CommitState.NotCommitted;
|
||||||
_context.FileSystemCount = fileSystemCount;
|
_context.FileSystemCount = fileSystemCount;
|
||||||
_context.CommitCount = commitCount;
|
_context.Counter = commitCount;
|
||||||
|
|
||||||
// Write the initial context to the file
|
// Write the initial context to the file
|
||||||
rc = contextFile.Write(0, SpanHelpers.AsByteSpan(ref _context), WriteOption.None);
|
rc = contextFile.Write(0, SpanHelpers.AsByteSpan(ref _context), WriteOption.None);
|
||||||
@ -222,13 +574,18 @@ namespace LibHac.FsSrv.Impl
|
|||||||
return _fileSystem.Commit();
|
return _fileSystem.Commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result SetCommittedProvisionally()
|
/// <summary>
|
||||||
|
/// Updates the commit context and writes it to a file, signifying that all
|
||||||
|
/// the file systems have been provisionally committed.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||||
|
public Result CommitProvisionallyDone()
|
||||||
{
|
{
|
||||||
IFile contextFile = null;
|
IFile contextFile = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Result rc = _fileSystem.OpenFile(out contextFile, ContextFileName, OpenMode.ReadWrite);
|
Result rc = _fileSystem.OpenFile(out contextFile, CommitContextFileName, OpenMode.ReadWrite);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
_context.State = CommitState.ProvisionallyCommitted;
|
_context.State = CommitState.ProvisionallyCommitted;
|
||||||
@ -247,9 +604,13 @@ namespace LibHac.FsSrv.Impl
|
|||||||
return _fileSystem.Commit();
|
return _fileSystem.Commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result Close()
|
/// <summary>
|
||||||
|
/// To be called once the multi-commit has been successfully completed. Deletes the commit context file.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||||
|
public Result CommitDone()
|
||||||
{
|
{
|
||||||
Result rc = _fileSystem.DeleteFile(ContextFileName);
|
Result rc = _fileSystem.DeleteFile(CommitContextFileName);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = _fileSystem.Commit();
|
rc = _fileSystem.Commit();
|
||||||
@ -263,7 +624,7 @@ namespace LibHac.FsSrv.Impl
|
|||||||
{
|
{
|
||||||
if (_fileSystem is null) return;
|
if (_fileSystem is null) return;
|
||||||
|
|
||||||
_fileSystem.DeleteFile(ContextFileName).IgnoreResult();
|
_fileSystem.DeleteFile(CommitContextFileName).IgnoreResult();
|
||||||
_fileSystem.Commit().IgnoreResult();
|
_fileSystem.Commit().IgnoreResult();
|
||||||
|
|
||||||
_fileSystem = null;
|
_fileSystem = null;
|
||||||
|
74
src/LibHac/FsSrv/Impl/OpenCountFileSystem.cs
Normal file
74
src/LibHac/FsSrv/Impl/OpenCountFileSystem.cs
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.Fs.Fsa;
|
||||||
|
using LibHac.FsSystem;
|
||||||
|
|
||||||
|
namespace LibHac.FsSrv.Impl
|
||||||
|
{
|
||||||
|
internal class OpenCountFileSystem : ForwardingFileSystem
|
||||||
|
{
|
||||||
|
private ReferenceCountedDisposable<IEntryOpenCountSemaphoreManager> _entryCountSemaphore;
|
||||||
|
private IUniqueLock _mountCountSemaphore;
|
||||||
|
|
||||||
|
protected OpenCountFileSystem(ref ReferenceCountedDisposable<IFileSystem> baseFileSystem,
|
||||||
|
ref ReferenceCountedDisposable<IEntryOpenCountSemaphoreManager> entryCountSemaphore) : base(
|
||||||
|
ref baseFileSystem)
|
||||||
|
{
|
||||||
|
Shared.Move(out _entryCountSemaphore, ref entryCountSemaphore);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected OpenCountFileSystem(ref ReferenceCountedDisposable<IFileSystem> baseFileSystem,
|
||||||
|
ref ReferenceCountedDisposable<IEntryOpenCountSemaphoreManager> entryCountSemaphore,
|
||||||
|
ref IUniqueLock mountCountSemaphore) : base(ref baseFileSystem)
|
||||||
|
{
|
||||||
|
Shared.Move(out _entryCountSemaphore, ref entryCountSemaphore);
|
||||||
|
Shared.Move(out _mountCountSemaphore, ref mountCountSemaphore);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ReferenceCountedDisposable<IFileSystem> CreateShared(
|
||||||
|
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem,
|
||||||
|
ref ReferenceCountedDisposable<IEntryOpenCountSemaphoreManager> entryCountSemaphore,
|
||||||
|
ref IUniqueLock mountCountSemaphore)
|
||||||
|
{
|
||||||
|
var filesystem =
|
||||||
|
new OpenCountFileSystem(ref baseFileSystem, ref entryCountSemaphore, ref mountCountSemaphore);
|
||||||
|
|
||||||
|
return new ReferenceCountedDisposable<IFileSystem>(filesystem);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ReferenceCountedDisposable<IFileSystem> CreateShared(
|
||||||
|
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem,
|
||||||
|
ref ReferenceCountedDisposable<IEntryOpenCountSemaphoreManager> entryCountSemaphore)
|
||||||
|
{
|
||||||
|
var filesystem =
|
||||||
|
new OpenCountFileSystem(ref baseFileSystem, ref entryCountSemaphore);
|
||||||
|
|
||||||
|
return new ReferenceCountedDisposable<IFileSystem>(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReSharper disable once RedundantOverriddenMember
|
||||||
|
protected override Result DoOpenDirectory(out IDirectory directory, U8Span path, OpenDirectoryMode mode)
|
||||||
|
{
|
||||||
|
// Todo: Implement
|
||||||
|
return base.DoOpenDirectory(out directory, path, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
_entryCountSemaphore?.Dispose();
|
||||||
|
_mountCountSemaphore?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -158,6 +158,8 @@ namespace LibHac.FsSrv.Impl
|
|||||||
public StorageId StorageId { get; }
|
public StorageId StorageId { get; }
|
||||||
public AccessControl AccessControl { get; }
|
public AccessControl AccessControl { get; }
|
||||||
|
|
||||||
|
public ulong ProgramIdValue => ProgramId.Value;
|
||||||
|
|
||||||
public ProgramInfo(FileSystemServer fsServer, ulong processId, ProgramId programId, StorageId storageId,
|
public ProgramInfo(FileSystemServer fsServer, ulong processId, ProgramId programId, StorageId storageId,
|
||||||
ReadOnlySpan<byte> accessControlData, ReadOnlySpan<byte> accessControlDescriptor)
|
ReadOnlySpan<byte> accessControlData, ReadOnlySpan<byte> accessControlDescriptor)
|
||||||
{
|
{
|
||||||
|
100
src/LibHac/FsSrv/Impl/SaveDataProperties.cs
Normal file
100
src/LibHac/FsSrv/Impl/SaveDataProperties.cs
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
using LibHac.Diag;
|
||||||
|
using LibHac.Fs;
|
||||||
|
|
||||||
|
namespace LibHac.FsSrv.Impl
|
||||||
|
{
|
||||||
|
public static class SaveDataProperties
|
||||||
|
{
|
||||||
|
public static bool IsJournalingSupported(SaveDataType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case SaveDataType.System:
|
||||||
|
case SaveDataType.Account:
|
||||||
|
case SaveDataType.Bcat:
|
||||||
|
case SaveDataType.Device:
|
||||||
|
case SaveDataType.Cache:
|
||||||
|
return true;
|
||||||
|
case SaveDataType.Temporary:
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
Abort.UnexpectedDefault();
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsMultiCommitSupported(SaveDataType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case SaveDataType.System:
|
||||||
|
case SaveDataType.Account:
|
||||||
|
case SaveDataType.Device:
|
||||||
|
return true;
|
||||||
|
case SaveDataType.Bcat:
|
||||||
|
case SaveDataType.Temporary:
|
||||||
|
case SaveDataType.Cache:
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
Abort.UnexpectedDefault();
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsSharedOpenNeeded(SaveDataType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case SaveDataType.System:
|
||||||
|
case SaveDataType.Bcat:
|
||||||
|
case SaveDataType.Temporary:
|
||||||
|
case SaveDataType.Cache:
|
||||||
|
return false;
|
||||||
|
case SaveDataType.Account:
|
||||||
|
case SaveDataType.Device:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
Abort.UnexpectedDefault();
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool CanUseIndexerReservedArea(SaveDataType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case SaveDataType.System:
|
||||||
|
case SaveDataType.SystemBcat:
|
||||||
|
return true;
|
||||||
|
case SaveDataType.Account:
|
||||||
|
case SaveDataType.Bcat:
|
||||||
|
case SaveDataType.Device:
|
||||||
|
case SaveDataType.Temporary:
|
||||||
|
case SaveDataType.Cache:
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
Abort.UnexpectedDefault();
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsSystemSaveData(SaveDataType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case SaveDataType.System:
|
||||||
|
case SaveDataType.SystemBcat:
|
||||||
|
return true;
|
||||||
|
case SaveDataType.Account:
|
||||||
|
case SaveDataType.Bcat:
|
||||||
|
case SaveDataType.Device:
|
||||||
|
case SaveDataType.Temporary:
|
||||||
|
case SaveDataType.Cache:
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
Abort.UnexpectedDefault();
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -12,7 +12,7 @@ namespace LibHac.FsSrv.Impl
|
|||||||
{
|
{
|
||||||
public static Result EnsureDirectory(IFileSystem fileSystem, U8Span path)
|
public static Result EnsureDirectory(IFileSystem fileSystem, U8Span path)
|
||||||
{
|
{
|
||||||
FsPath.FromSpan(out FsPath pathBuffer, ReadOnlySpan<byte>.Empty).IgnoreResult();
|
FsPath.FromSpan(out FsPath pathBuffer, path.Value).IgnoreResult();
|
||||||
|
|
||||||
int pathLength = StringUtils.GetLength(path);
|
int pathLength = StringUtils.GetLength(path);
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ namespace LibHac.FsSrv.Impl
|
|||||||
private static Result EnsureDirectoryImpl(IFileSystem fileSystem, Span<byte> path)
|
private static Result EnsureDirectoryImpl(IFileSystem fileSystem, Span<byte> path)
|
||||||
{
|
{
|
||||||
// Double check the trailing separators have been trimmed
|
// Double check the trailing separators have been trimmed
|
||||||
Assert.AssertTrue(path.Length == 0 || path[path.Length - 1] != StringTraits.DirectorySeparator);
|
Assert.AssertTrue(path.Length <= 1 || path[path.Length - 1] != StringTraits.DirectorySeparator);
|
||||||
|
|
||||||
// Use the root path if the input path is empty
|
// Use the root path if the input path is empty
|
||||||
var pathToCheck = new U8Span(path.IsEmpty ? FileSystemRootPath : path);
|
var pathToCheck = new U8Span(path.IsEmpty ? FileSystemRootPath : path);
|
||||||
@ -99,8 +99,8 @@ namespace LibHac.FsSrv.Impl
|
|||||||
return ResultFs.TooLongPath.Log();
|
return ResultFs.TooLongPath.Log();
|
||||||
|
|
||||||
// Iterate until we run out of path or find the next separator
|
// Iterate until we run out of path or find the next separator
|
||||||
int length = path.Length;
|
int length = path.Length - 1;
|
||||||
while (length > 0 && path[length - 1] != StringTraits.DirectorySeparator)
|
while (length > 0 && path[length] != StringTraits.DirectorySeparator)
|
||||||
{
|
{
|
||||||
length--;
|
length--;
|
||||||
}
|
}
|
||||||
@ -112,25 +112,22 @@ namespace LibHac.FsSrv.Impl
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We found the length of the parent directory. Ensure it exists
|
// We found the length of the parent directory. Ensure it exists
|
||||||
path[length - 1] = StringTraits.NullTerminator;
|
path[length] = StringTraits.NullTerminator;
|
||||||
Result rc = EnsureDirectoryImpl(fileSystem, path.Slice(0, length));
|
Result rc = EnsureDirectoryImpl(fileSystem, path.Slice(0, length));
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
// Restore the separator
|
// Restore the separator
|
||||||
path[length - 1] = StringTraits.DirectorySeparator;
|
path[length] = StringTraits.DirectorySeparator;
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Result CreateSubDirectoryFileSystem(out ReferenceCountedDisposable<IFileSystem> subDirFileSystem,
|
public static Result CreateSubDirectoryFileSystem(out ReferenceCountedDisposable<IFileSystem> subDirFileSystem,
|
||||||
ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path, bool preserveUnc = false)
|
ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span subPath, bool preserveUnc = false)
|
||||||
{
|
{
|
||||||
subDirFileSystem = default;
|
subDirFileSystem = default;
|
||||||
|
|
||||||
if (path.IsNull())
|
|
||||||
return ResultFs.NullptrArgument.Log();
|
|
||||||
|
|
||||||
// Check if the directory exists
|
// Check if the directory exists
|
||||||
Result rc = baseFileSystem.Target.OpenDirectory(out IDirectory dir, path, OpenDirectoryMode.Directory);
|
Result rc = baseFileSystem.Target.OpenDirectory(out IDirectory dir, subPath, OpenDirectoryMode.Directory);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
dir.Dispose();
|
dir.Dispose();
|
||||||
@ -138,7 +135,7 @@ namespace LibHac.FsSrv.Impl
|
|||||||
var fs = new SubdirectoryFileSystem(baseFileSystem, preserveUnc);
|
var fs = new SubdirectoryFileSystem(baseFileSystem, preserveUnc);
|
||||||
using (var subDirFs = new ReferenceCountedDisposable<SubdirectoryFileSystem>(fs))
|
using (var subDirFs = new ReferenceCountedDisposable<SubdirectoryFileSystem>(fs))
|
||||||
{
|
{
|
||||||
rc = subDirFs.Target.Initialize(path);
|
rc = subDirFs.Target.Initialize(subPath);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
subDirFileSystem = subDirFs.AddReference<IFileSystem>();
|
subDirFileSystem = subDirFs.AddReference<IFileSystem>();
|
||||||
|
@ -30,8 +30,8 @@ namespace LibHac.FsSrv
|
|||||||
RomMountCountSemaphore = new SemaphoreAdaptor(RomSemaphoreCount, RomSemaphoreCount);
|
RomMountCountSemaphore = new SemaphoreAdaptor(RomSemaphoreCount, RomSemaphoreCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReferenceCountedDisposable<NcaFileSystemService> Create(NcaFileSystemServiceImpl serviceImpl,
|
public static ReferenceCountedDisposable<NcaFileSystemService> CreateShared(
|
||||||
ulong processId)
|
NcaFileSystemServiceImpl serviceImpl, ulong processId)
|
||||||
{
|
{
|
||||||
// Create the service
|
// Create the service
|
||||||
var ncaService = new NcaFileSystemService(serviceImpl, processId);
|
var ncaService = new NcaFileSystemService(serviceImpl, processId);
|
||||||
@ -140,7 +140,7 @@ namespace LibHac.FsSrv
|
|||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
// Create an SF adapter for the file system
|
// Create an SF adapter for the file system
|
||||||
fileSystem = FileSystemInterfaceAdapter.CreateSharedSfFileSystem(ref fs);
|
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref fs);
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
@ -212,7 +212,7 @@ namespace LibHac.FsSrv
|
|||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
// Create an SF adapter for the file system
|
// Create an SF adapter for the file system
|
||||||
fileSystem = FileSystemInterfaceAdapter.CreateSharedSfFileSystem(ref fs);
|
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref fs);
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
@ -341,10 +341,10 @@ namespace LibHac.FsSrv
|
|||||||
|
|
||||||
private bool IsHostFs(U8Span path)
|
private bool IsHostFs(U8Span path)
|
||||||
{
|
{
|
||||||
int hostMountLength = StringUtils.GetLength(CommonMountNames.HostRootFileSystemMountName,
|
int hostMountLength = StringUtils.GetLength(CommonPaths.HostRootFileSystemMountName,
|
||||||
PathTools.MountNameLengthMax);
|
PathTools.MountNameLengthMax);
|
||||||
|
|
||||||
return StringUtils.Compare(path, CommonMountNames.HostRootFileSystemMountName, hostMountLength) == 0;
|
return StringUtils.Compare(path, CommonPaths.HostRootFileSystemMountName, hostMountLength) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
@ -173,10 +173,10 @@ namespace LibHac.FsSrv
|
|||||||
info = new MountNameInfo();
|
info = new MountNameInfo();
|
||||||
shouldContinue = true;
|
shouldContinue = true;
|
||||||
|
|
||||||
if (StringUtils.Compare(path, CommonMountNames.GameCardFileSystemMountName,
|
if (StringUtils.Compare(path, CommonPaths.GameCardFileSystemMountName,
|
||||||
CommonMountNames.GameCardFileSystemMountName.Length) == 0)
|
CommonPaths.GameCardFileSystemMountName.Length) == 0)
|
||||||
{
|
{
|
||||||
path = path.Slice(CommonMountNames.GameCardFileSystemMountName.Length);
|
path = path.Slice(CommonPaths.GameCardFileSystemMountName.Length);
|
||||||
|
|
||||||
if (StringUtils.GetLength(path.Value, 9) < 9)
|
if (StringUtils.GetLength(path.Value, 9) < 9)
|
||||||
return ResultFs.InvalidPath.Log();
|
return ResultFs.InvalidPath.Log();
|
||||||
@ -184,13 +184,13 @@ namespace LibHac.FsSrv
|
|||||||
GameCardPartition partition;
|
GameCardPartition partition;
|
||||||
switch ((char)path[0])
|
switch ((char)path[0])
|
||||||
{
|
{
|
||||||
case CommonMountNames.GameCardFileSystemMountNameUpdateSuffix:
|
case CommonPaths.GameCardFileSystemMountNameUpdateSuffix:
|
||||||
partition = GameCardPartition.Update;
|
partition = GameCardPartition.Update;
|
||||||
break;
|
break;
|
||||||
case CommonMountNames.GameCardFileSystemMountNameNormalSuffix:
|
case CommonPaths.GameCardFileSystemMountNameNormalSuffix:
|
||||||
partition = GameCardPartition.Normal;
|
partition = GameCardPartition.Normal;
|
||||||
break;
|
break;
|
||||||
case CommonMountNames.GameCardFileSystemMountNameSecureSuffix:
|
case CommonPaths.GameCardFileSystemMountNameSecureSuffix:
|
||||||
partition = GameCardPartition.Secure;
|
partition = GameCardPartition.Secure;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -214,10 +214,10 @@ namespace LibHac.FsSrv
|
|||||||
info.CanMountNca = true;
|
info.CanMountNca = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (StringUtils.Compare(path, CommonMountNames.ContentStorageSystemMountName,
|
else if (StringUtils.Compare(path, CommonPaths.ContentStorageSystemMountName,
|
||||||
CommonMountNames.ContentStorageSystemMountName.Length) == 0)
|
CommonPaths.ContentStorageSystemMountName.Length) == 0)
|
||||||
{
|
{
|
||||||
path = path.Slice(CommonMountNames.ContentStorageSystemMountName.Length);
|
path = path.Slice(CommonPaths.ContentStorageSystemMountName.Length);
|
||||||
|
|
||||||
Result rc = OpenContentStorageFileSystem(out fileSystem, ContentStorageId.System);
|
Result rc = OpenContentStorageFileSystem(out fileSystem, ContentStorageId.System);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
@ -225,10 +225,10 @@ namespace LibHac.FsSrv
|
|||||||
info.CanMountNca = true;
|
info.CanMountNca = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (StringUtils.Compare(path, CommonMountNames.ContentStorageUserMountName,
|
else if (StringUtils.Compare(path, CommonPaths.ContentStorageUserMountName,
|
||||||
CommonMountNames.ContentStorageUserMountName.Length) == 0)
|
CommonPaths.ContentStorageUserMountName.Length) == 0)
|
||||||
{
|
{
|
||||||
path = path.Slice(CommonMountNames.ContentStorageUserMountName.Length);
|
path = path.Slice(CommonPaths.ContentStorageUserMountName.Length);
|
||||||
|
|
||||||
Result rc = OpenContentStorageFileSystem(out fileSystem, ContentStorageId.User);
|
Result rc = OpenContentStorageFileSystem(out fileSystem, ContentStorageId.User);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
@ -236,10 +236,10 @@ namespace LibHac.FsSrv
|
|||||||
info.CanMountNca = true;
|
info.CanMountNca = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (StringUtils.Compare(path, CommonMountNames.ContentStorageSdCardMountName,
|
else if (StringUtils.Compare(path, CommonPaths.ContentStorageSdCardMountName,
|
||||||
CommonMountNames.ContentStorageSdCardMountName.Length) == 0)
|
CommonPaths.ContentStorageSdCardMountName.Length) == 0)
|
||||||
{
|
{
|
||||||
path = path.Slice(CommonMountNames.ContentStorageSdCardMountName.Length);
|
path = path.Slice(CommonPaths.ContentStorageSdCardMountName.Length);
|
||||||
|
|
||||||
Result rc = OpenContentStorageFileSystem(out fileSystem, ContentStorageId.SdCard);
|
Result rc = OpenContentStorageFileSystem(out fileSystem, ContentStorageId.SdCard);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
@ -247,58 +247,58 @@ namespace LibHac.FsSrv
|
|||||||
info.CanMountNca = true;
|
info.CanMountNca = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (StringUtils.Compare(path, CommonMountNames.BisCalibrationFilePartitionMountName,
|
else if (StringUtils.Compare(path, CommonPaths.BisCalibrationFilePartitionMountName,
|
||||||
CommonMountNames.BisCalibrationFilePartitionMountName.Length) == 0)
|
CommonPaths.BisCalibrationFilePartitionMountName.Length) == 0)
|
||||||
{
|
{
|
||||||
path = path.Slice(CommonMountNames.BisCalibrationFilePartitionMountName.Length);
|
path = path.Slice(CommonPaths.BisCalibrationFilePartitionMountName.Length);
|
||||||
|
|
||||||
Result rc = _config.BaseFsService.OpenBisFileSystem(out fileSystem, U8Span.Empty,
|
Result rc = _config.BaseFsService.OpenBisFileSystem(out fileSystem, U8Span.Empty,
|
||||||
BisPartitionId.CalibrationFile);
|
BisPartitionId.CalibrationFile);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (StringUtils.Compare(path, CommonMountNames.BisSafeModePartitionMountName,
|
else if (StringUtils.Compare(path, CommonPaths.BisSafeModePartitionMountName,
|
||||||
CommonMountNames.BisSafeModePartitionMountName.Length) == 0)
|
CommonPaths.BisSafeModePartitionMountName.Length) == 0)
|
||||||
{
|
{
|
||||||
path = path.Slice(CommonMountNames.BisSafeModePartitionMountName.Length);
|
path = path.Slice(CommonPaths.BisSafeModePartitionMountName.Length);
|
||||||
|
|
||||||
Result rc = _config.BaseFsService.OpenBisFileSystem(out fileSystem, U8Span.Empty,
|
Result rc = _config.BaseFsService.OpenBisFileSystem(out fileSystem, U8Span.Empty,
|
||||||
BisPartitionId.SafeMode);
|
BisPartitionId.SafeMode);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (StringUtils.Compare(path, CommonMountNames.BisUserPartitionMountName,
|
else if (StringUtils.Compare(path, CommonPaths.BisUserPartitionMountName,
|
||||||
CommonMountNames.BisUserPartitionMountName.Length) == 0)
|
CommonPaths.BisUserPartitionMountName.Length) == 0)
|
||||||
{
|
{
|
||||||
path = path.Slice(CommonMountNames.BisUserPartitionMountName.Length);
|
path = path.Slice(CommonPaths.BisUserPartitionMountName.Length);
|
||||||
|
|
||||||
Result rc = _config.BaseFsService.OpenBisFileSystem(out fileSystem, U8Span.Empty, BisPartitionId.User);
|
Result rc = _config.BaseFsService.OpenBisFileSystem(out fileSystem, U8Span.Empty, BisPartitionId.User);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (StringUtils.Compare(path, CommonMountNames.BisSystemPartitionMountName,
|
else if (StringUtils.Compare(path, CommonPaths.BisSystemPartitionMountName,
|
||||||
CommonMountNames.BisSystemPartitionMountName.Length) == 0)
|
CommonPaths.BisSystemPartitionMountName.Length) == 0)
|
||||||
{
|
{
|
||||||
path = path.Slice(CommonMountNames.BisSystemPartitionMountName.Length);
|
path = path.Slice(CommonPaths.BisSystemPartitionMountName.Length);
|
||||||
|
|
||||||
Result rc = _config.BaseFsService.OpenBisFileSystem(out fileSystem, U8Span.Empty,
|
Result rc = _config.BaseFsService.OpenBisFileSystem(out fileSystem, U8Span.Empty,
|
||||||
BisPartitionId.System);
|
BisPartitionId.System);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (StringUtils.Compare(path, CommonMountNames.SdCardFileSystemMountName,
|
else if (StringUtils.Compare(path, CommonPaths.SdCardFileSystemMountName,
|
||||||
CommonMountNames.SdCardFileSystemMountName.Length) == 0)
|
CommonPaths.SdCardFileSystemMountName.Length) == 0)
|
||||||
{
|
{
|
||||||
path = path.Slice(CommonMountNames.SdCardFileSystemMountName.Length);
|
path = path.Slice(CommonPaths.SdCardFileSystemMountName.Length);
|
||||||
|
|
||||||
Result rc = _config.BaseFsService.OpenSdCardProxyFileSystem(out fileSystem);
|
Result rc = _config.BaseFsService.OpenSdCardProxyFileSystem(out fileSystem);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (StringUtils.Compare(path, CommonMountNames.HostRootFileSystemMountName,
|
else if (StringUtils.Compare(path, CommonPaths.HostRootFileSystemMountName,
|
||||||
CommonMountNames.HostRootFileSystemMountName.Length) == 0)
|
CommonPaths.HostRootFileSystemMountName.Length) == 0)
|
||||||
{
|
{
|
||||||
path = path.Slice(CommonMountNames.HostRootFileSystemMountName.Length);
|
path = path.Slice(CommonPaths.HostRootFileSystemMountName.Length);
|
||||||
|
|
||||||
info.IsHostFs = true;
|
info.IsHostFs = true;
|
||||||
info.CanMountNca = true;
|
info.CanMountNca = true;
|
||||||
@ -307,10 +307,10 @@ namespace LibHac.FsSrv
|
|||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (StringUtils.Compare(path, CommonMountNames.RegisteredUpdatePartitionMountName,
|
else if (StringUtils.Compare(path, CommonPaths.RegisteredUpdatePartitionMountName,
|
||||||
CommonMountNames.RegisteredUpdatePartitionMountName.Length) == 0)
|
CommonPaths.RegisteredUpdatePartitionMountName.Length) == 0)
|
||||||
{
|
{
|
||||||
path = path.Slice(CommonMountNames.RegisteredUpdatePartitionMountName.Length);
|
path = path.Slice(CommonPaths.RegisteredUpdatePartitionMountName.Length);
|
||||||
|
|
||||||
info.CanMountNca = true;
|
info.CanMountNca = true;
|
||||||
|
|
||||||
@ -396,7 +396,7 @@ namespace LibHac.FsSrv
|
|||||||
if (sb.Overflowed)
|
if (sb.Overflowed)
|
||||||
return ResultFs.TooLongPath.Log();
|
return ResultFs.TooLongPath.Log();
|
||||||
|
|
||||||
Result rc = _config.TargetManagerFsCreator.GetCaseSensitivePath(out bool success, fullPath.Str);
|
Result rc = _config.TargetManagerFsCreator.NormalizeCaseOfPath(out bool success, fullPath.Str);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
// Reopen the host filesystem as case sensitive
|
// Reopen the host filesystem as case sensitive
|
||||||
|
@ -69,7 +69,7 @@ namespace LibHac.FsSrv
|
|||||||
PreserveUnc = (1 << 0),
|
PreserveUnc = (1 << 0),
|
||||||
PreserveTailSeparator = (1 << 1),
|
PreserveTailSeparator = (1 << 1),
|
||||||
HasMountName = (1 << 2),
|
HasMountName = (1 << 2),
|
||||||
AcceptEmpty = (1 << 3),
|
AcceptEmpty = (1 << 3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,169 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace LibHac.FsSrv
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Permissions that control which filesystems or storages can be mounted or opened.
|
|
||||||
/// </summary>
|
|
||||||
public enum AccessPermissions
|
|
||||||
{
|
|
||||||
MountLogo = 0x0,
|
|
||||||
MountContentMeta = 0x1,
|
|
||||||
MountContentControl = 0x2,
|
|
||||||
MountContentManual = 0x3,
|
|
||||||
MountContentData = 0x4,
|
|
||||||
MountApplicationPackage = 0x5,
|
|
||||||
MountSaveDataStorage = 0x6,
|
|
||||||
MountContentStorage = 0x7,
|
|
||||||
MountImageAndVideoStorage = 0x8,
|
|
||||||
MountCloudBackupWorkStorage = 0x9,
|
|
||||||
MountCustomStorage = 0xA,
|
|
||||||
MountBisCalibrationFile = 0xB,
|
|
||||||
MountBisSafeMode = 0xC,
|
|
||||||
MountBisUser = 0xD,
|
|
||||||
MountBisSystem = 0xE,
|
|
||||||
MountBisSystemProperEncryption = 0xF,
|
|
||||||
MountBisSystemProperPartition = 0x10,
|
|
||||||
MountSdCard = 0x11,
|
|
||||||
MountGameCard = 0x12,
|
|
||||||
MountDeviceSaveData = 0x13,
|
|
||||||
MountSystemSaveData = 0x14,
|
|
||||||
MountOthersSaveData = 0x15,
|
|
||||||
MountOthersSystemSaveData = 0x16,
|
|
||||||
OpenBisPartitionBootPartition1Root = 0x17,
|
|
||||||
OpenBisPartitionBootPartition2Root = 0x18,
|
|
||||||
OpenBisPartitionUserDataRoot = 0x19,
|
|
||||||
OpenBisPartitionBootConfigAndPackage2Part1 = 0x1A,
|
|
||||||
OpenBisPartitionBootConfigAndPackage2Part2 = 0x1B,
|
|
||||||
OpenBisPartitionBootConfigAndPackage2Part3 = 0x1C,
|
|
||||||
OpenBisPartitionBootConfigAndPackage2Part4 = 0x1D,
|
|
||||||
OpenBisPartitionBootConfigAndPackage2Part5 = 0x1E,
|
|
||||||
OpenBisPartitionBootConfigAndPackage2Part6 = 0x1F,
|
|
||||||
OpenBisPartitionCalibrationBinary = 0x20,
|
|
||||||
OpenBisPartitionCalibrationFile = 0x21,
|
|
||||||
OpenBisPartitionSafeMode = 0x22,
|
|
||||||
OpenBisPartitionUser = 0x23,
|
|
||||||
OpenBisPartitionSystem = 0x24,
|
|
||||||
OpenBisPartitionSystemProperEncryption = 0x25,
|
|
||||||
OpenBisPartitionSystemProperPartition = 0x26,
|
|
||||||
OpenSdCardStorage = 0x27,
|
|
||||||
OpenGameCardStorage = 0x28,
|
|
||||||
MountSystemDataPrivate = 0x29,
|
|
||||||
MountHost = 0x2A,
|
|
||||||
MountRegisteredUpdatePartition = 0x2B,
|
|
||||||
MountSaveDataInternalStorage = 0x2C,
|
|
||||||
NotMountCustomStorage = 0x2D
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Permissions that control which actions can be performed.
|
|
||||||
/// </summary>
|
|
||||||
public enum ActionPermissions
|
|
||||||
{
|
|
||||||
// Todo
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class PermissionUtils
|
|
||||||
{
|
|
||||||
public static ulong GetPermissionMask(AccessPermissions id)
|
|
||||||
{
|
|
||||||
switch (id)
|
|
||||||
{
|
|
||||||
case AccessPermissions.MountLogo:
|
|
||||||
return 0x8000000000000801;
|
|
||||||
case AccessPermissions.MountContentMeta:
|
|
||||||
return 0x8000000000000801;
|
|
||||||
case AccessPermissions.MountContentControl:
|
|
||||||
return 0x8000000000000801;
|
|
||||||
case AccessPermissions.MountContentManual:
|
|
||||||
return 0x8000000000000801;
|
|
||||||
case AccessPermissions.MountContentData:
|
|
||||||
return 0x8000000000000801;
|
|
||||||
case AccessPermissions.MountApplicationPackage:
|
|
||||||
return 0x8000000000000801;
|
|
||||||
case AccessPermissions.MountSaveDataStorage:
|
|
||||||
return 0x8000000000000000;
|
|
||||||
case AccessPermissions.MountContentStorage:
|
|
||||||
return 0x8000000000000800;
|
|
||||||
case AccessPermissions.MountImageAndVideoStorage:
|
|
||||||
return 0x8000000000001000;
|
|
||||||
case AccessPermissions.MountCloudBackupWorkStorage:
|
|
||||||
return 0x8000000200000000;
|
|
||||||
case AccessPermissions.MountCustomStorage:
|
|
||||||
return 0x8000000000000000;
|
|
||||||
case AccessPermissions.MountBisCalibrationFile:
|
|
||||||
return 0x8000000000000084;
|
|
||||||
case AccessPermissions.MountBisSafeMode:
|
|
||||||
return 0x8000000000000080;
|
|
||||||
case AccessPermissions.MountBisUser:
|
|
||||||
return 0x8000000000008080;
|
|
||||||
case AccessPermissions.MountBisSystem:
|
|
||||||
return 0x8000000000008080;
|
|
||||||
case AccessPermissions.MountBisSystemProperEncryption:
|
|
||||||
return 0x8000000000000080;
|
|
||||||
case AccessPermissions.MountBisSystemProperPartition:
|
|
||||||
return 0x8000000000000080;
|
|
||||||
case AccessPermissions.MountSdCard:
|
|
||||||
return 0xC000000000200000;
|
|
||||||
case AccessPermissions.MountGameCard:
|
|
||||||
return 0x8000000000000010;
|
|
||||||
case AccessPermissions.MountDeviceSaveData:
|
|
||||||
return 0x8000000000040020;
|
|
||||||
case AccessPermissions.MountSystemSaveData:
|
|
||||||
return 0x8000000000000028;
|
|
||||||
case AccessPermissions.MountOthersSaveData:
|
|
||||||
return 0x8000000000000020;
|
|
||||||
case AccessPermissions.MountOthersSystemSaveData:
|
|
||||||
return 0x8000000000000020;
|
|
||||||
case AccessPermissions.OpenBisPartitionBootPartition1Root:
|
|
||||||
return 0x8000000000010082;
|
|
||||||
case AccessPermissions.OpenBisPartitionBootPartition2Root:
|
|
||||||
return 0x8000000000010080;
|
|
||||||
case AccessPermissions.OpenBisPartitionUserDataRoot:
|
|
||||||
return 0x8000000000000080;
|
|
||||||
case AccessPermissions.OpenBisPartitionBootConfigAndPackage2Part1:
|
|
||||||
return 0x8000000000010080;
|
|
||||||
case AccessPermissions.OpenBisPartitionBootConfigAndPackage2Part2:
|
|
||||||
return 0x8000000000010080;
|
|
||||||
case AccessPermissions.OpenBisPartitionBootConfigAndPackage2Part3:
|
|
||||||
return 0x8000000000010080;
|
|
||||||
case AccessPermissions.OpenBisPartitionBootConfigAndPackage2Part4:
|
|
||||||
return 0x8000000000010080;
|
|
||||||
case AccessPermissions.OpenBisPartitionBootConfigAndPackage2Part5:
|
|
||||||
return 0x8000000000010080;
|
|
||||||
case AccessPermissions.OpenBisPartitionBootConfigAndPackage2Part6:
|
|
||||||
return 0x8000000000010080;
|
|
||||||
case AccessPermissions.OpenBisPartitionCalibrationBinary:
|
|
||||||
return 0x8000000000000084;
|
|
||||||
case AccessPermissions.OpenBisPartitionCalibrationFile:
|
|
||||||
return 0x8000000000000084;
|
|
||||||
case AccessPermissions.OpenBisPartitionSafeMode:
|
|
||||||
return 0x8000000000000080;
|
|
||||||
case AccessPermissions.OpenBisPartitionUser:
|
|
||||||
return 0x8000000000000080;
|
|
||||||
case AccessPermissions.OpenBisPartitionSystem:
|
|
||||||
return 0x8000000000000080;
|
|
||||||
case AccessPermissions.OpenBisPartitionSystemProperEncryption:
|
|
||||||
return 0x8000000000000080;
|
|
||||||
case AccessPermissions.OpenBisPartitionSystemProperPartition:
|
|
||||||
return 0x8000000000000080;
|
|
||||||
case AccessPermissions.OpenSdCardStorage:
|
|
||||||
return 0xC000000000200000;
|
|
||||||
case AccessPermissions.OpenGameCardStorage:
|
|
||||||
return 0x8000000000000100;
|
|
||||||
case AccessPermissions.MountSystemDataPrivate:
|
|
||||||
return 0x8000000000100008;
|
|
||||||
case AccessPermissions.MountHost:
|
|
||||||
return 0xC000000000400000;
|
|
||||||
case AccessPermissions.MountRegisteredUpdatePartition:
|
|
||||||
return 0x8000000000010000;
|
|
||||||
case AccessPermissions.MountSaveDataInternalStorage:
|
|
||||||
return 0x8000000000000000;
|
|
||||||
case AccessPermissions.NotMountCustomStorage:
|
|
||||||
return 0x0;
|
|
||||||
default:
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(id), id, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Ncm;
|
using LibHac.Ncm;
|
||||||
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.FsSrv
|
namespace LibHac.FsSrv
|
||||||
{
|
{
|
||||||
@ -50,7 +51,7 @@ namespace LibHac.FsSrv
|
|||||||
/// <param name="programId">The program ID of the map info to get.</param>
|
/// <param name="programId">The program ID of the map info to get.</param>
|
||||||
/// <returns>If the program ID was found, the <see cref="ProgramIndexMapInfo"/> associated
|
/// <returns>If the program ID was found, the <see cref="ProgramIndexMapInfo"/> associated
|
||||||
/// with that ID; otherwise, <see langword="null"/>.</returns>
|
/// with that ID; otherwise, <see langword="null"/>.</returns>
|
||||||
public ProgramIndexMapInfo? Get(ProgramId programId)
|
public Optional<ProgramIndexMapInfo> Get(ProgramId programId)
|
||||||
{
|
{
|
||||||
lock (MapEntries)
|
lock (MapEntries)
|
||||||
{
|
{
|
||||||
@ -70,12 +71,13 @@ namespace LibHac.FsSrv
|
|||||||
{
|
{
|
||||||
lock (MapEntries)
|
lock (MapEntries)
|
||||||
{
|
{
|
||||||
ProgramIndexMapInfo? mainProgramMapInfo = GetImpl((in ProgramIndexMapInfo x) => x.ProgramId == programId);
|
Optional<ProgramIndexMapInfo> mainProgramMapInfo =
|
||||||
|
GetImpl((in ProgramIndexMapInfo x) => x.ProgramId == programId);
|
||||||
|
|
||||||
if(!mainProgramMapInfo.HasValue)
|
if(!mainProgramMapInfo.HasValue)
|
||||||
return ProgramId.InvalidId;
|
return ProgramId.InvalidId;
|
||||||
|
|
||||||
ProgramIndexMapInfo? requestedMapInfo = GetImpl((in ProgramIndexMapInfo x) =>
|
Optional<ProgramIndexMapInfo> requestedMapInfo = GetImpl((in ProgramIndexMapInfo x) =>
|
||||||
x.MainProgramId == mainProgramMapInfo.Value.MainProgramId && x.ProgramIndex == programIndex);
|
x.MainProgramId == mainProgramMapInfo.Value.MainProgramId && x.ProgramIndex == programIndex);
|
||||||
|
|
||||||
if (!requestedMapInfo.HasValue)
|
if (!requestedMapInfo.HasValue)
|
||||||
@ -99,17 +101,17 @@ namespace LibHac.FsSrv
|
|||||||
|
|
||||||
private delegate bool EntrySelector(in ProgramIndexMapInfo candidate);
|
private delegate bool EntrySelector(in ProgramIndexMapInfo candidate);
|
||||||
|
|
||||||
private ProgramIndexMapInfo? GetImpl(EntrySelector selector)
|
private Optional<ProgramIndexMapInfo> GetImpl(EntrySelector selector)
|
||||||
{
|
{
|
||||||
foreach (ProgramIndexMapInfo entry in MapEntries)
|
foreach (ProgramIndexMapInfo entry in MapEntries)
|
||||||
{
|
{
|
||||||
if (selector(in entry))
|
if (selector(in entry))
|
||||||
{
|
{
|
||||||
return entry;
|
return new Optional<ProgramIndexMapInfo>(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return new Optional<ProgramIndexMapInfo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ClearImpl()
|
private void ClearImpl()
|
||||||
|
@ -3,21 +3,23 @@ using System.Runtime.CompilerServices;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.FsSrv.Impl;
|
using LibHac.FsSrv.Impl;
|
||||||
|
using LibHac.Sf;
|
||||||
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.FsSrv
|
namespace LibHac.FsSrv
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used to perform operations on the Program Index Map.
|
/// Used to perform operations on the program index registry.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Appropriate methods calls on IFileSystemProxy are forwarded to this class
|
/// <remarks>Appropriate methods calls on IFileSystemProxy are forwarded to this class
|
||||||
/// which then checks the calling process' permissions and performs the requested operation.
|
/// which then checks the calling process' permissions and performs the requested operation.
|
||||||
/// <br/>Based on FS 10.0.0 (nnSdk 10.4.0)</remarks>
|
/// <br/>Based on FS 10.0.0 (nnSdk 10.4.0)</remarks>
|
||||||
internal readonly struct ProgramRegistryService
|
internal readonly struct ProgramIndexRegistryService
|
||||||
{
|
{
|
||||||
private ProgramRegistryServiceImpl ServiceImpl { get; }
|
private ProgramRegistryServiceImpl ServiceImpl { get; }
|
||||||
private ulong ProcessId { get; }
|
private ulong ProcessId { get; }
|
||||||
|
|
||||||
public ProgramRegistryService(ProgramRegistryServiceImpl serviceImpl, ulong processId)
|
public ProgramIndexRegistryService(ProgramRegistryServiceImpl serviceImpl, ulong processId)
|
||||||
{
|
{
|
||||||
ServiceImpl = serviceImpl;
|
ServiceImpl = serviceImpl;
|
||||||
ProcessId = processId;
|
ProcessId = processId;
|
||||||
@ -33,7 +35,7 @@ namespace LibHac.FsSrv
|
|||||||
/// <see cref="ResultFs.PermissionDenied"/>: Insufficient permissions.<br/>
|
/// <see cref="ResultFs.PermissionDenied"/>: Insufficient permissions.<br/>
|
||||||
/// <see cref="ResultFs.InvalidSize"/>: The buffer was too small to hold the specified
|
/// <see cref="ResultFs.InvalidSize"/>: The buffer was too small to hold the specified
|
||||||
/// number of <see cref="ProgramIndexMapInfo"/> entries.</returns>
|
/// number of <see cref="ProgramIndexMapInfo"/> entries.</returns>
|
||||||
public Result RegisterProgramIndexMapInfo(ReadOnlySpan<byte> programIndexMapInfo, int programCount)
|
public Result RegisterProgramIndexMapInfo(InBuffer programIndexMapInfo, int programCount)
|
||||||
{
|
{
|
||||||
// Verify the caller's permissions
|
// Verify the caller's permissions
|
||||||
Result rc = GetProgramInfo(out ProgramInfo programInfo, ProcessId);
|
Result rc = GetProgramInfo(out ProgramInfo programInfo, ProcessId);
|
||||||
@ -49,7 +51,7 @@ namespace LibHac.FsSrv
|
|||||||
|
|
||||||
// Verify that the provided buffer is large enough to hold "programCount" entries
|
// Verify that the provided buffer is large enough to hold "programCount" entries
|
||||||
ReadOnlySpan<ProgramIndexMapInfo>
|
ReadOnlySpan<ProgramIndexMapInfo>
|
||||||
mapInfo = MemoryMarshal.Cast<byte, ProgramIndexMapInfo>(programIndexMapInfo);
|
mapInfo = MemoryMarshal.Cast<byte, ProgramIndexMapInfo>(programIndexMapInfo.Buffer);
|
||||||
|
|
||||||
if (mapInfo.Length < programCount)
|
if (mapInfo.Length < programCount)
|
||||||
return ResultFs.InvalidSize.Log();
|
return ResultFs.InvalidSize.Log();
|
||||||
@ -69,7 +71,7 @@ namespace LibHac.FsSrv
|
|||||||
/// <returns><see cref="Result.Success"/>: The operation was successful.<br/>
|
/// <returns><see cref="Result.Success"/>: The operation was successful.<br/>
|
||||||
/// <see cref="ResultFs.TargetProgramNotFound"/>: The calling program was not found
|
/// <see cref="ResultFs.TargetProgramNotFound"/>: The calling program was not found
|
||||||
/// in the program registry. Something's wrong with Loader if this happens.</returns>
|
/// in the program registry. Something's wrong with Loader if this happens.</returns>
|
||||||
public Result GetProgramIndexForAccessLog(out int programIndex, out int programCount)
|
public Result GetProgramIndex(out int programIndex, out int programCount)
|
||||||
{
|
{
|
||||||
Unsafe.SkipInit(out programIndex);
|
Unsafe.SkipInit(out programIndex);
|
||||||
Unsafe.SkipInit(out programCount);
|
Unsafe.SkipInit(out programCount);
|
||||||
@ -79,10 +81,10 @@ namespace LibHac.FsSrv
|
|||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
// Try to get map info for this process
|
// Try to get map info for this process
|
||||||
ProgramIndexMapInfo? mapInfo = ServiceImpl.GetProgramIndexMapInfo(programInfo.ProgramId);
|
Optional<ProgramIndexMapInfo> mapInfo = ServiceImpl.GetProgramIndexMapInfo(programInfo.ProgramId);
|
||||||
|
|
||||||
// Set the output program index if map info was found
|
// Set the output program index if map info was found
|
||||||
programIndex = mapInfo?.ProgramIndex ?? 0;
|
programIndex = mapInfo.HasValue ? mapInfo.ValueRo.ProgramIndex : 0;
|
||||||
|
|
||||||
// Set the number of programs in the current application
|
// Set the number of programs in the current application
|
||||||
programCount = ServiceImpl.GetProgramIndexMapInfoCount();
|
programCount = ServiceImpl.GetProgramIndexMapInfoCount();
|
@ -43,7 +43,7 @@ namespace LibHac.FsSrv
|
|||||||
if (!ProgramInfo.IsInitialProgram(_processId))
|
if (!ProgramInfo.IsInitialProgram(_processId))
|
||||||
return ResultFs.PermissionDenied.Log();
|
return ResultFs.PermissionDenied.Log();
|
||||||
|
|
||||||
return _registryService.RegisterProgram(processId, programId, storageId, accessControlData,
|
return _registryService.RegisterProgramInfo(processId, programId, storageId, accessControlData,
|
||||||
accessControlDescriptor);
|
accessControlDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ namespace LibHac.FsSrv
|
|||||||
if (!ProgramInfo.IsInitialProgram(_processId))
|
if (!ProgramInfo.IsInitialProgram(_processId))
|
||||||
return ResultFs.PermissionDenied.Log();
|
return ResultFs.PermissionDenied.Log();
|
||||||
|
|
||||||
return _registryService.UnregisterProgram(processId);
|
return _registryService.UnregisterProgramInfo(processId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="ProgramRegistryManager.GetProgramInfo"/>
|
/// <inheritdoc cref="ProgramRegistryManager.GetProgramInfo"/>
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.FsSrv.Impl;
|
using LibHac.FsSrv.Impl;
|
||||||
using LibHac.Ncm;
|
using LibHac.Ncm;
|
||||||
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.FsSrv
|
namespace LibHac.FsSrv
|
||||||
{
|
{
|
||||||
@ -21,7 +22,7 @@ namespace LibHac.FsSrv
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="ProgramRegistryManager.RegisterProgram"/>
|
/// <inheritdoc cref="ProgramRegistryManager.RegisterProgram"/>
|
||||||
public Result RegisterProgram(ulong processId, ProgramId programId, StorageId storageId,
|
public Result RegisterProgramInfo(ulong processId, ProgramId programId, StorageId storageId,
|
||||||
ReadOnlySpan<byte> accessControlData, ReadOnlySpan<byte> accessControlDescriptor)
|
ReadOnlySpan<byte> accessControlData, ReadOnlySpan<byte> accessControlDescriptor)
|
||||||
{
|
{
|
||||||
return RegistryManager.RegisterProgram(processId, programId, storageId, accessControlData,
|
return RegistryManager.RegisterProgram(processId, programId, storageId, accessControlData,
|
||||||
@ -29,7 +30,7 @@ namespace LibHac.FsSrv
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="ProgramRegistryManager.UnregisterProgram" />
|
/// <inheritdoc cref="ProgramRegistryManager.UnregisterProgram" />
|
||||||
public Result UnregisterProgram(ulong processId)
|
public Result UnregisterProgramInfo(ulong processId)
|
||||||
{
|
{
|
||||||
return RegistryManager.UnregisterProgram(processId);
|
return RegistryManager.UnregisterProgram(processId);
|
||||||
}
|
}
|
||||||
@ -47,13 +48,13 @@ namespace LibHac.FsSrv
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="ProgramIndexMapInfoManager.GetProgramId"/>
|
/// <inheritdoc cref="ProgramIndexMapInfoManager.GetProgramId"/>
|
||||||
public ProgramId GetProgramId(ProgramId programId, byte programIndex)
|
public ProgramId GetProgramIdByIndex(ProgramId programId, byte programIndex)
|
||||||
{
|
{
|
||||||
return ProgramIndexManager.GetProgramId(programId, programIndex);
|
return ProgramIndexManager.GetProgramId(programId, programIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="ProgramIndexMapInfoManager.Get"/>
|
/// <inheritdoc cref="ProgramIndexMapInfoManager.Get"/>
|
||||||
public ProgramIndexMapInfo? GetProgramIndexMapInfo(ProgramId programId)
|
public Optional<ProgramIndexMapInfo> GetProgramIndexMapInfo(ProgramId programId)
|
||||||
{
|
{
|
||||||
return ProgramIndexManager.Get(programId);
|
return ProgramIndexManager.Get(programId);
|
||||||
}
|
}
|
||||||
|
1938
src/LibHac/FsSrv/SaveDataFileSystemService.cs
Normal file
1938
src/LibHac/FsSrv/SaveDataFileSystemService.cs
Normal file
File diff suppressed because it is too large
Load Diff
625
src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs
Normal file
625
src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs
Normal file
@ -0,0 +1,625 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.Fs.Fsa;
|
||||||
|
using LibHac.FsSrv.Creators;
|
||||||
|
using LibHac.FsSrv.Impl;
|
||||||
|
using LibHac.FsSystem;
|
||||||
|
using LibHac.FsSystem.Save;
|
||||||
|
using LibHac.Ncm;
|
||||||
|
using LibHac.Util;
|
||||||
|
using Utility = LibHac.FsSrv.Impl.Utility;
|
||||||
|
|
||||||
|
namespace LibHac.FsSrv
|
||||||
|
{
|
||||||
|
public class SaveDataFileSystemServiceImpl
|
||||||
|
{
|
||||||
|
private Configuration _config;
|
||||||
|
private EncryptionSeed _encryptionSeed;
|
||||||
|
|
||||||
|
private ISaveDataFileSystemCacheManager _saveCacheManager;
|
||||||
|
// Save data extra data cache
|
||||||
|
// Save data porter manager
|
||||||
|
private bool _isSdCardAccessible;
|
||||||
|
// Timestamp getter
|
||||||
|
|
||||||
|
internal HorizonClient Hos => _config.HorizonClient;
|
||||||
|
|
||||||
|
public SaveDataFileSystemServiceImpl(in Configuration configuration)
|
||||||
|
{
|
||||||
|
_config = configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Configuration
|
||||||
|
{
|
||||||
|
public BaseFileSystemServiceImpl BaseFsService;
|
||||||
|
// Time service
|
||||||
|
public IHostFileSystemCreator HostFsCreator;
|
||||||
|
public ITargetManagerFileSystemCreator TargetManagerFsCreator;
|
||||||
|
public ISaveDataFileSystemCreator SaveFsCreator;
|
||||||
|
public IEncryptedFileSystemCreator EncryptedFsCreator;
|
||||||
|
public ProgramRegistryServiceImpl ProgramRegistryService;
|
||||||
|
// Buffer manager
|
||||||
|
public RandomDataGenerator GenerateRandomData;
|
||||||
|
public SaveDataTransferCryptoConfiguration SaveTransferCryptoConfig;
|
||||||
|
// Max save FS cache size
|
||||||
|
public Func<bool> ShouldCreateDirectorySaveData;
|
||||||
|
public ISaveDataIndexerManager SaveIndexerManager;
|
||||||
|
|
||||||
|
// LibHac additions
|
||||||
|
public HorizonClient HorizonClient;
|
||||||
|
public ProgramRegistryImpl ProgramRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Result GetProgramInfo(out ProgramInfo programInfo, ulong processId)
|
||||||
|
{
|
||||||
|
return _config.ProgramRegistry.GetProgramInfo(out programInfo, processId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result DoesSaveDataEntityExist(out bool exists, SaveDataSpaceId spaceId, ulong saveDataId)
|
||||||
|
{
|
||||||
|
Unsafe.SkipInit(out exists);
|
||||||
|
|
||||||
|
ReferenceCountedDisposable<IFileSystem> fileSystem = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Result rc = OpenSaveDataDirectoryFileSystem(out fileSystem, spaceId, U8Span.Empty, true);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
// Get the path of the save data
|
||||||
|
Span<byte> saveDataPath = stackalloc byte[0x12];
|
||||||
|
var sb = new U8StringBuilder(saveDataPath);
|
||||||
|
sb.Append((byte)'/').AppendFormat(saveDataId, 'x', 16);
|
||||||
|
|
||||||
|
rc = fileSystem.Target.GetEntryType(out _, new U8Span(saveDataPath));
|
||||||
|
|
||||||
|
if (rc.IsSuccess())
|
||||||
|
{
|
||||||
|
exists = true;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
else if (ResultFs.PathNotFound.Includes(rc))
|
||||||
|
{
|
||||||
|
exists = false;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
fileSystem?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result OpenSaveDataFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||||
|
SaveDataSpaceId spaceId, ulong saveDataId, U8Span saveDataRootPath, bool openReadOnly, SaveDataType type,
|
||||||
|
bool cacheExtraData)
|
||||||
|
{
|
||||||
|
fileSystem = default;
|
||||||
|
|
||||||
|
ReferenceCountedDisposable<IFileSystem> saveDirectoryFs = null;
|
||||||
|
ReferenceCountedDisposable<SaveDataFileSystem> cachedSaveDataFs = null;
|
||||||
|
ReferenceCountedDisposable<IFileSystem> saveDataFs = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Result rc = OpenSaveDataDirectoryFileSystem(out saveDirectoryFs, spaceId, saveDataRootPath, true);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
bool allowDirectorySaveData = IsAllowedDirectorySaveData2(spaceId, saveDataRootPath);
|
||||||
|
|
||||||
|
if (allowDirectorySaveData)
|
||||||
|
{
|
||||||
|
Span<byte> saveDirectoryPath = stackalloc byte[0x12];
|
||||||
|
var sb = new U8StringBuilder(saveDirectoryPath);
|
||||||
|
sb.Append((byte)'/').AppendFormat(saveDataId, 'x', 16);
|
||||||
|
|
||||||
|
rc = Utility.EnsureDirectory(saveDirectoryFs.Target, new U8Span(saveDirectoryPath));
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!allowDirectorySaveData)
|
||||||
|
{
|
||||||
|
// Todo: Missing save FS cache lookup
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
||||||
|
if (saveDataFs is null)
|
||||||
|
{
|
||||||
|
ReferenceCountedDisposable<ISaveDataExtraDataAccessor> extraDataAccessor = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Todo: Update ISaveDataFileSystemCreator
|
||||||
|
bool useDeviceUniqueMac = IsDeviceUniqueMac(spaceId);
|
||||||
|
|
||||||
|
rc = _config.SaveFsCreator.Create(out IFileSystem saveFs,
|
||||||
|
out extraDataAccessor, saveDirectoryFs.Target, saveDataId,
|
||||||
|
allowDirectorySaveData, useDeviceUniqueMac, type, null);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
saveDataFs = new ReferenceCountedDisposable<IFileSystem>(saveFs);
|
||||||
|
|
||||||
|
if (cacheExtraData)
|
||||||
|
{
|
||||||
|
// Todo: Missing extra data caching
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
extraDataAccessor?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (openReadOnly)
|
||||||
|
{
|
||||||
|
saveDataFs = ReadOnlyFileSystem.CreateShared(ref saveDataFs);
|
||||||
|
}
|
||||||
|
|
||||||
|
Shared.Move(out fileSystem, ref saveDataFs);
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
saveDirectoryFs?.Dispose();
|
||||||
|
// ReSharper disable once ExpressionIsAlwaysNull
|
||||||
|
cachedSaveDataFs?.Dispose();
|
||||||
|
saveDataFs?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result OpenSaveDataMetaDirectoryFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||||
|
SaveDataSpaceId spaceId, ulong saveDataId)
|
||||||
|
{
|
||||||
|
var metaDirPath = // /saveMeta/
|
||||||
|
new U8Span(new[]
|
||||||
|
{
|
||||||
|
(byte) '/', (byte) 's', (byte) 'a', (byte) 'v', (byte) 'e', (byte) 'M', (byte) 'e', (byte) 't',
|
||||||
|
(byte) 'a', (byte) '/'
|
||||||
|
});
|
||||||
|
|
||||||
|
Span<byte> directoryName = stackalloc byte[0x1B];
|
||||||
|
var sb = new U8StringBuilder(directoryName);
|
||||||
|
sb.Append(metaDirPath).AppendFormat(saveDataId, 'x', 16);
|
||||||
|
|
||||||
|
return OpenSaveDataDirectoryFileSystemImpl(out fileSystem, spaceId, new U8Span(directoryName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result OpenSaveDataInternalStorageFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||||
|
SaveDataSpaceId spaceId, ulong saveDataId, U8Span saveDataRootPath, bool useSecondMacKey)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result QuerySaveDataTotalSize(out long totalSize, int blockSize, long dataSize, long journalSize)
|
||||||
|
{
|
||||||
|
// Todo: Implement
|
||||||
|
totalSize = 0;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result CreateSaveDataMeta(ulong saveDataId, SaveDataSpaceId spaceId, SaveDataMetaType metaType,
|
||||||
|
long metaSize)
|
||||||
|
{
|
||||||
|
ReferenceCountedDisposable<IFileSystem> metaDirFs = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Result rc = OpenSaveDataMetaDirectoryFileSystem(out metaDirFs, spaceId, saveDataId);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
Span<byte> saveMetaPathBuffer = stackalloc byte[0xF];
|
||||||
|
var sb = new U8StringBuilder(saveMetaPathBuffer);
|
||||||
|
sb.Append((byte)'/').AppendFormat((int)metaType, 'x', 8);
|
||||||
|
|
||||||
|
return metaDirFs.Target.CreateFile(new U8Span(saveMetaPathBuffer), metaSize);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
metaDirFs?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result DeleteSaveDataMeta(ulong saveDataId, SaveDataSpaceId spaceId, SaveDataMetaType metaType)
|
||||||
|
{
|
||||||
|
ReferenceCountedDisposable<IFileSystem> metaDirFs = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Result rc = OpenSaveDataMetaDirectoryFileSystem(out metaDirFs, spaceId, saveDataId);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
Span<byte> saveMetaPathBuffer = stackalloc byte[0xF];
|
||||||
|
var sb = new U8StringBuilder(saveMetaPathBuffer);
|
||||||
|
sb.Append((byte)'/').AppendFormat((int)metaType, 'x', 8);
|
||||||
|
|
||||||
|
return metaDirFs.Target.DeleteFile(new U8Span(saveMetaPathBuffer));
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
metaDirFs?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result DeleteAllSaveDataMetas(ulong saveDataId, SaveDataSpaceId spaceId)
|
||||||
|
{
|
||||||
|
var metaDirPath = // /saveMeta
|
||||||
|
new U8Span(new[]
|
||||||
|
{
|
||||||
|
(byte) '/', (byte) 's', (byte) 'a', (byte) 'v', (byte) 'e', (byte) 'M', (byte) 'e', (byte) 't',
|
||||||
|
(byte) 'a'
|
||||||
|
});
|
||||||
|
|
||||||
|
ReferenceCountedDisposable<IFileSystem> fileSystem = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Result rc = OpenSaveDataDirectoryFileSystemImpl(out fileSystem, spaceId, metaDirPath, false);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
// Get the path of the save data meta
|
||||||
|
Span<byte> saveDataPathBuffer = stackalloc byte[0x12];
|
||||||
|
var sb = new U8StringBuilder(saveDataPathBuffer);
|
||||||
|
sb.Append((byte)'/').AppendFormat(saveDataId, 'x', 16);
|
||||||
|
|
||||||
|
// Delete the save data's meta directory, ignoring the error if the directory is already gone
|
||||||
|
rc = fileSystem.Target.DeleteDirectoryRecursively(new U8Span(saveDataPathBuffer));
|
||||||
|
|
||||||
|
if (rc.IsFailure() && !ResultFs.PathNotFound.Includes(rc))
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
fileSystem?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result OpenSaveDataMeta(out IFile metaFile, ulong saveDataId, SaveDataSpaceId spaceId,
|
||||||
|
SaveDataMetaType metaType)
|
||||||
|
{
|
||||||
|
metaFile = default;
|
||||||
|
|
||||||
|
ReferenceCountedDisposable<IFileSystem> metaDirFs = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Result rc = OpenSaveDataMetaDirectoryFileSystem(out metaDirFs, spaceId, saveDataId);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
Span<byte> saveMetaPathBuffer = stackalloc byte[0xF];
|
||||||
|
var sb = new U8StringBuilder(saveMetaPathBuffer);
|
||||||
|
sb.Append((byte)'/').AppendFormat((int)metaType, 'x', 8);
|
||||||
|
|
||||||
|
return metaDirFs.Target.OpenFile(out metaFile, new U8Span(saveMetaPathBuffer), OpenMode.ReadWrite);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
metaDirFs?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result CreateSaveDataFileSystem(ulong saveDataId, in SaveDataAttribute attribute,
|
||||||
|
in SaveDataCreationInfo creationInfo, U8Span saveDataRootPath, in Optional<HashSalt> hashSalt,
|
||||||
|
bool skipFormat)
|
||||||
|
{
|
||||||
|
// Use directory save data for now
|
||||||
|
|
||||||
|
ReferenceCountedDisposable<IFileSystem> fileSystem = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Result rc = OpenSaveDataDirectoryFileSystem(out fileSystem, creationInfo.SpaceId, saveDataRootPath,
|
||||||
|
false);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
Span<byte> saveDataPathBuffer = stackalloc byte[0x12];
|
||||||
|
var sb = new U8StringBuilder(saveDataPathBuffer);
|
||||||
|
sb.Append((byte)'/').AppendFormat(saveDataId, 'x', 16);
|
||||||
|
|
||||||
|
if (_config.ShouldCreateDirectorySaveData())
|
||||||
|
{
|
||||||
|
return Utility.EnsureDirectory(fileSystem.Target, new U8Span(saveDataPathBuffer));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
fileSystem?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result WipeData(IFileSystem fileSystem, U8Span filePath, RandomDataGenerator random)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result DeleteSaveDataFileSystem(SaveDataSpaceId spaceId, ulong saveDataId, bool wipeSaveFile,
|
||||||
|
U8Span saveDataRootPath)
|
||||||
|
{
|
||||||
|
ReferenceCountedDisposable<IFileSystem> fileSystem = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Open the directory containing the save data
|
||||||
|
Result rc = OpenSaveDataDirectoryFileSystem(out fileSystem, spaceId, saveDataRootPath, false);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
// Get the path of the save data
|
||||||
|
Span<byte> saveDataPathBuffer = stackalloc byte[0x12];
|
||||||
|
var saveDataPath = new U8Span(saveDataPathBuffer);
|
||||||
|
|
||||||
|
var sb = new U8StringBuilder(saveDataPathBuffer);
|
||||||
|
sb.Append((byte)'/').AppendFormat(saveDataId, 'x', 16);
|
||||||
|
|
||||||
|
// Check if the save data is a file or a directory
|
||||||
|
rc = fileSystem.Target.GetEntryType(out DirectoryEntryType entryType, saveDataPath);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
// Delete the save data, wiping the file if needed
|
||||||
|
if (entryType == DirectoryEntryType.Directory)
|
||||||
|
{
|
||||||
|
rc = fileSystem.Target.DeleteDirectoryRecursively(saveDataPath);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (wipeSaveFile)
|
||||||
|
{
|
||||||
|
WipeData(fileSystem.Target, saveDataPath, _config.GenerateRandomData).IgnoreResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = fileSystem.Target.DeleteDirectoryRecursively(saveDataPath);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
fileSystem?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result ReadSaveDataFileSystemExtraData(out SaveDataExtraData extraData, SaveDataSpaceId spaceId,
|
||||||
|
ulong saveDataId, SaveDataType type, U8Span saveDataRootPath)
|
||||||
|
{
|
||||||
|
// Todo: Find a way to store extra data for directory save data
|
||||||
|
extraData = default;
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result WriteSaveDataFileSystemExtraData(SaveDataSpaceId spaceId, ulong saveDataId,
|
||||||
|
in SaveDataExtraData extraData, U8Span saveDataRootPath, SaveDataType type, bool updateTimeStamp)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result CorruptSaveDataFileSystem(SaveDataSpaceId spaceId, ulong saveDataId, long offset,
|
||||||
|
U8Span saveDataRootPath)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsSaveEmulated(U8Span saveDataRootPath)
|
||||||
|
{
|
||||||
|
return !saveDataRootPath.IsEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result OpenSaveDataDirectoryFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||||
|
SaveDataSpaceId spaceId, U8Span saveDataRootPath, bool allowEmulatedSave)
|
||||||
|
{
|
||||||
|
if (allowEmulatedSave && IsAllowedDirectorySaveData(spaceId, saveDataRootPath))
|
||||||
|
{
|
||||||
|
fileSystem = default;
|
||||||
|
ReferenceCountedDisposable<IFileSystem> tmFs = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Ensure the target save data directory exists
|
||||||
|
Result rc = _config.TargetManagerFsCreator.Create(out tmFs, false);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
rc = Utility.EnsureDirectory(tmFs.Target, saveDataRootPath);
|
||||||
|
if (rc.IsFailure())
|
||||||
|
return ResultFs.SaveDataRootPathUnavailable.LogConverted(rc);
|
||||||
|
|
||||||
|
tmFs.Dispose();
|
||||||
|
|
||||||
|
// Nintendo does a straight copy here without checking the input path length,
|
||||||
|
// stopping before the null terminator.
|
||||||
|
// FsPath used instead of stackalloc for convenience
|
||||||
|
FsPath.FromSpan(out FsPath path, saveDataRootPath).IgnoreResult();
|
||||||
|
|
||||||
|
rc = _config.TargetManagerFsCreator.NormalizeCaseOfPath(out bool isTargetFsCaseSensitive, path.Str);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
rc = _config.TargetManagerFsCreator.Create(out tmFs, isTargetFsCaseSensitive);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
return Utility.CreateSubDirectoryFileSystem(out fileSystem, tmFs, new U8Span(path.Str), true);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
tmFs?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlySpan<byte> saveDirName;
|
||||||
|
|
||||||
|
if (spaceId == SaveDataSpaceId.Temporary)
|
||||||
|
{
|
||||||
|
saveDirName = new[] { (byte)'/', (byte)'t', (byte)'e', (byte)'m', (byte)'p' }; // /temp
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
saveDirName = new[] { (byte)'/', (byte)'s', (byte)'a', (byte)'v', (byte)'e' }; // /save
|
||||||
|
}
|
||||||
|
|
||||||
|
return OpenSaveDataDirectoryFileSystemImpl(out fileSystem, spaceId, new U8Span(saveDirName), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result OpenSaveDataDirectoryFileSystemImpl(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||||
|
SaveDataSpaceId spaceId, U8Span basePath)
|
||||||
|
{
|
||||||
|
return OpenSaveDataDirectoryFileSystemImpl(out fileSystem, spaceId, basePath, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result OpenSaveDataDirectoryFileSystemImpl(out ReferenceCountedDisposable<IFileSystem> fileSystem,
|
||||||
|
SaveDataSpaceId spaceId, U8Span basePath, bool createIfMissing)
|
||||||
|
{
|
||||||
|
fileSystem = default;
|
||||||
|
|
||||||
|
ReferenceCountedDisposable<IFileSystem> tempFs = null;
|
||||||
|
ReferenceCountedDisposable<IFileSystem> tempSubFs = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Result rc;
|
||||||
|
|
||||||
|
switch (spaceId)
|
||||||
|
{
|
||||||
|
case SaveDataSpaceId.System:
|
||||||
|
rc = _config.BaseFsService.OpenBisFileSystem(out tempFs, U8Span.Empty, BisPartitionId.System, true);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
return Utility.WrapSubDirectory(out fileSystem, tempFs, basePath, createIfMissing);
|
||||||
|
|
||||||
|
case SaveDataSpaceId.User:
|
||||||
|
case SaveDataSpaceId.Temporary:
|
||||||
|
rc = _config.BaseFsService.OpenBisFileSystem(out tempFs, U8Span.Empty, BisPartitionId.User, true);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
return Utility.WrapSubDirectory(out fileSystem, tempFs, basePath, createIfMissing);
|
||||||
|
|
||||||
|
case SaveDataSpaceId.SdSystem:
|
||||||
|
case SaveDataSpaceId.SdCache:
|
||||||
|
rc = _config.BaseFsService.OpenSdCardProxyFileSystem(out tempFs, true);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
Unsafe.SkipInit(out FsPath path);
|
||||||
|
var sb = new U8StringBuilder(path.Str);
|
||||||
|
sb.Append((byte)'/')
|
||||||
|
.Append(basePath.Value)
|
||||||
|
.Append((byte)'/')
|
||||||
|
.Append(CommonPaths.SdCardNintendoRootDirectoryName);
|
||||||
|
|
||||||
|
rc = Utility.WrapSubDirectory(out tempSubFs, tempFs, path, createIfMissing);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
return _config.EncryptedFsCreator.Create(out fileSystem, tempSubFs, EncryptedFsKeyId.Save,
|
||||||
|
in _encryptionSeed);
|
||||||
|
|
||||||
|
case SaveDataSpaceId.ProperSystem:
|
||||||
|
rc = _config.BaseFsService.OpenBisFileSystem(out tempFs, U8Span.Empty, BisPartitionId.SystemProperPartition, true);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
return Utility.WrapSubDirectory(out fileSystem, tempFs, basePath, createIfMissing);
|
||||||
|
|
||||||
|
case SaveDataSpaceId.SafeMode:
|
||||||
|
rc = _config.BaseFsService.OpenBisFileSystem(out tempFs, U8Span.Empty, BisPartitionId.SafeMode, true);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
return Utility.WrapSubDirectory(out fileSystem, tempFs, basePath, createIfMissing);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return ResultFs.InvalidArgument.Log();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
tempFs?.Dispose();
|
||||||
|
tempSubFs?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result SetSdCardEncryptionSeed(in EncryptionSeed seed)
|
||||||
|
{
|
||||||
|
_encryptionSeed = seed;
|
||||||
|
|
||||||
|
_config.SaveFsCreator.SetSdCardEncryptionSeed(seed.Value);
|
||||||
|
_config.SaveIndexerManager.InvalidateIndexer(SaveDataSpaceId.SdSystem);
|
||||||
|
_config.SaveIndexerManager.InvalidateIndexer(SaveDataSpaceId.SdCache);
|
||||||
|
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result IsProvisionallyCommittedSaveData(out bool isProvisionallyCommitted, in SaveDataInfo saveInfo)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsAllowedDirectorySaveData(SaveDataSpaceId spaceId, U8Span saveDataRootPath)
|
||||||
|
{
|
||||||
|
return spaceId == SaveDataSpaceId.User && IsSaveEmulated(saveDataRootPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Todo: remove once file save data is supported
|
||||||
|
// Used to always allow directory save data in OpenSaveDataFileSystem
|
||||||
|
public bool IsAllowedDirectorySaveData2(SaveDataSpaceId spaceId, U8Span saveDataRootPath)
|
||||||
|
{
|
||||||
|
// Todo: remove "|| true" once file save data is supported
|
||||||
|
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
||||||
|
return spaceId == SaveDataSpaceId.User && IsSaveEmulated(saveDataRootPath) || true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsDeviceUniqueMac(SaveDataSpaceId spaceId)
|
||||||
|
{
|
||||||
|
return spaceId == SaveDataSpaceId.System ||
|
||||||
|
spaceId == SaveDataSpaceId.User ||
|
||||||
|
spaceId == SaveDataSpaceId.Temporary ||
|
||||||
|
spaceId == SaveDataSpaceId.ProperSystem ||
|
||||||
|
spaceId == SaveDataSpaceId.SafeMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetSdCardAccessibility(bool isAccessible)
|
||||||
|
{
|
||||||
|
_isSdCardAccessible = isAccessible;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsSdCardAccessible()
|
||||||
|
{
|
||||||
|
return _isSdCardAccessible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the program ID of the save data associated with the specified programID.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>In a standard application the program ID will be the same as the input program ID.
|
||||||
|
/// In multi-program applications all sub-programs use the program ID of the main program
|
||||||
|
/// for their save data. The main program always has a program index of 0.</remarks>
|
||||||
|
/// <param name="programId">The program ID to get the save data program ID for.</param>
|
||||||
|
/// <returns>The program ID of the save data.</returns>
|
||||||
|
public ProgramId ResolveDefaultSaveDataReferenceProgramId(in ProgramId programId)
|
||||||
|
{
|
||||||
|
// First check if there's an entry in the program index map with the program ID and program index 0
|
||||||
|
ProgramId mainProgramId = _config.ProgramRegistryService.GetProgramIdByIndex(programId, 0);
|
||||||
|
|
||||||
|
if (mainProgramId != ProgramId.InvalidId)
|
||||||
|
{
|
||||||
|
return mainProgramId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if there's an entry with the program ID, ignoring program index
|
||||||
|
Optional<ProgramIndexMapInfo> mapInfo = _config.ProgramRegistryService.GetProgramIndexMapInfo(programId);
|
||||||
|
|
||||||
|
if (mapInfo.HasValue)
|
||||||
|
{
|
||||||
|
return mapInfo.Value.MainProgramId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The program ID isn't in the program index map. Probably running a single-program application
|
||||||
|
return programId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ResetTemporaryStorageIndexer()
|
||||||
|
{
|
||||||
|
_config.SaveIndexerManager.ResetIndexer(SaveDataSpaceId.Temporary);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result OpenSaveDataIndexerAccessor(out SaveDataIndexerAccessor accessor, out bool neededInit,
|
||||||
|
SaveDataSpaceId spaceId)
|
||||||
|
{
|
||||||
|
return _config.SaveIndexerManager.OpenSaveDataIndexerAccessor(out accessor, out neededInit, spaceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,9 @@ using LibHac.Common;
|
|||||||
using LibHac.Diag;
|
using LibHac.Diag;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Shim;
|
using LibHac.Fs.Shim;
|
||||||
|
using LibHac.FsSrv.Sf;
|
||||||
using LibHac.Kvdb;
|
using LibHac.Kvdb;
|
||||||
|
using LibHac.Sf;
|
||||||
|
|
||||||
namespace LibHac.FsSrv
|
namespace LibHac.FsSrv
|
||||||
{
|
{
|
||||||
@ -504,7 +506,7 @@ namespace LibHac.FsSrv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result OpenSaveDataInfoReader(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader)
|
public Result OpenSaveDataInfoReader(out ReferenceCountedDisposable<SaveDataInfoReaderImpl> infoReader)
|
||||||
{
|
{
|
||||||
infoReader = default;
|
infoReader = default;
|
||||||
|
|
||||||
@ -522,7 +524,7 @@ namespace LibHac.FsSrv
|
|||||||
rc = RegisterReader(reader);
|
rc = RegisterReader(reader);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
infoReader = reader.AddReference<ISaveDataInfoReader>();
|
infoReader = reader.AddReference<SaveDataInfoReaderImpl>();
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -802,7 +804,7 @@ namespace LibHac.FsSrv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Reader : ISaveDataInfoReader
|
private class Reader : SaveDataInfoReaderImpl, ISaveDataInfoReader
|
||||||
{
|
{
|
||||||
private readonly SaveDataIndexer _indexer;
|
private readonly SaveDataIndexer _indexer;
|
||||||
private FlatMapKeyValueStore<SaveDataAttribute>.Iterator _iterator;
|
private FlatMapKeyValueStore<SaveDataAttribute>.Iterator _iterator;
|
||||||
@ -816,7 +818,7 @@ namespace LibHac.FsSrv
|
|||||||
_iterator = indexer.GetBeginIterator();
|
_iterator = indexer.GetBeginIterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result Read(out long readCount, Span<byte> saveDataInfoBuffer)
|
public Result Read(out long readCount, OutBuffer saveDataInfoBuffer)
|
||||||
{
|
{
|
||||||
Unsafe.SkipInit(out readCount);
|
Unsafe.SkipInit(out readCount);
|
||||||
|
|
||||||
@ -828,7 +830,7 @@ namespace LibHac.FsSrv
|
|||||||
return ResultFs.InvalidSaveDataInfoReader.Log();
|
return ResultFs.InvalidSaveDataInfoReader.Log();
|
||||||
}
|
}
|
||||||
|
|
||||||
Span<SaveDataInfo> outInfo = MemoryMarshal.Cast<byte, SaveDataInfo>(saveDataInfoBuffer);
|
Span<SaveDataInfo> outInfo = MemoryMarshal.Cast<byte, SaveDataInfo>(saveDataInfoBuffer.Buffer);
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; !_iterator.IsEnd() && i < outInfo.Length; i++)
|
for (i = 0; !_iterator.IsEnd() && i < outInfo.Length; i++)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
|
using LibHac.FsSrv.Sf;
|
||||||
|
using LibHac.Sf;
|
||||||
|
|
||||||
namespace LibHac.FsSrv
|
namespace LibHac.FsSrv
|
||||||
{
|
{
|
||||||
@ -207,7 +209,7 @@ namespace LibHac.FsSrv
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result OpenSaveDataInfoReader(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader)
|
public Result OpenSaveDataInfoReader(out ReferenceCountedDisposable<SaveDataInfoReaderImpl> infoReader)
|
||||||
{
|
{
|
||||||
SaveDataIndexerLiteInfoReader reader;
|
SaveDataIndexerLiteInfoReader reader;
|
||||||
|
|
||||||
@ -220,12 +222,12 @@ namespace LibHac.FsSrv
|
|||||||
reader = new SaveDataIndexerLiteInfoReader();
|
reader = new SaveDataIndexerLiteInfoReader();
|
||||||
}
|
}
|
||||||
|
|
||||||
infoReader = new ReferenceCountedDisposable<ISaveDataInfoReader>(reader);
|
infoReader = new ReferenceCountedDisposable<SaveDataInfoReaderImpl>(reader);
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SaveDataIndexerLiteInfoReader : ISaveDataInfoReader
|
private class SaveDataIndexerLiteInfoReader : SaveDataInfoReaderImpl, ISaveDataInfoReader
|
||||||
{
|
{
|
||||||
private bool _finishedIterating;
|
private bool _finishedIterating;
|
||||||
private SaveDataInfo _info;
|
private SaveDataInfo _info;
|
||||||
@ -240,9 +242,9 @@ namespace LibHac.FsSrv
|
|||||||
SaveDataIndexer.GenerateSaveDataInfo(out _info, in key, in value);
|
SaveDataIndexer.GenerateSaveDataInfo(out _info, in key, in value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result Read(out long readCount, Span<byte> saveDataInfoBuffer)
|
public Result Read(out long readCount, OutBuffer saveDataInfoBuffer)
|
||||||
{
|
{
|
||||||
Span<SaveDataInfo> outInfo = MemoryMarshal.Cast<byte, SaveDataInfo>(saveDataInfoBuffer);
|
Span<SaveDataInfo> outInfo = MemoryMarshal.Cast<byte, SaveDataInfo>(saveDataInfoBuffer.Buffer);
|
||||||
|
|
||||||
// Note: Nintendo doesn't check if the buffer is large enough here
|
// Note: Nintendo doesn't check if the buffer is large enough here
|
||||||
if (_finishedIterating || outInfo.IsEmpty)
|
if (_finishedIterating || outInfo.IsEmpty)
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Diag;
|
using LibHac.Diag;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.FsSrv.Storage;
|
using LibHac.FsSrv.Storage;
|
||||||
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.FsSrv
|
namespace LibHac.FsSrv
|
||||||
{
|
{
|
||||||
@ -54,97 +54,112 @@ namespace LibHac.FsSrv
|
|||||||
/// if the indexer needed to be initialized.</param>
|
/// if the indexer needed to be initialized.</param>
|
||||||
/// <param name="spaceId">The <see cref="SaveDataSpaceId"/> of the indexer to open.</param>
|
/// <param name="spaceId">The <see cref="SaveDataSpaceId"/> of the indexer to open.</param>
|
||||||
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||||
public Result OpenAccessor(out SaveDataIndexerAccessor accessor, out bool neededInit, SaveDataSpaceId spaceId)
|
public Result OpenSaveDataIndexerAccessor(out SaveDataIndexerAccessor accessor, out bool neededInit,
|
||||||
|
SaveDataSpaceId spaceId)
|
||||||
{
|
{
|
||||||
neededInit = false;
|
neededInit = false;
|
||||||
|
UniqueLock indexerLock = default;
|
||||||
|
|
||||||
if (IsBisUserRedirectionEnabled && spaceId == SaveDataSpaceId.User)
|
if (IsBisUserRedirectionEnabled && spaceId == SaveDataSpaceId.User)
|
||||||
{
|
{
|
||||||
spaceId = SaveDataSpaceId.ProperSystem;
|
spaceId = SaveDataSpaceId.ProperSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (spaceId)
|
try
|
||||||
{
|
{
|
||||||
case SaveDataSpaceId.System:
|
ISaveDataIndexer indexer;
|
||||||
case SaveDataSpaceId.User:
|
switch (spaceId)
|
||||||
Monitor.Enter(_bisIndexer.Locker);
|
{
|
||||||
|
case SaveDataSpaceId.System:
|
||||||
|
case SaveDataSpaceId.User:
|
||||||
|
indexerLock = new UniqueLock(_bisIndexer.Locker);
|
||||||
|
|
||||||
if (!_bisIndexer.IsInitialized)
|
if (!_bisIndexer.IsInitialized)
|
||||||
{
|
{
|
||||||
_bisIndexer.Indexer = new SaveDataIndexer(FsClient, new U8Span(SystemIndexerMountName),
|
_bisIndexer.Indexer = new SaveDataIndexer(FsClient, new U8Span(SystemIndexerMountName),
|
||||||
SaveDataSpaceId.System, SaveDataId, MemoryResource);
|
SaveDataSpaceId.System, SaveDataId, MemoryResource);
|
||||||
|
|
||||||
neededInit = true;
|
neededInit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
accessor = new SaveDataIndexerAccessor(_bisIndexer.Indexer, _bisIndexer.Locker);
|
indexer = _bisIndexer.Indexer;
|
||||||
return Result.Success;
|
break;
|
||||||
|
case SaveDataSpaceId.SdSystem:
|
||||||
|
case SaveDataSpaceId.SdCache:
|
||||||
|
// ReSharper doesn't realize that UniqueLock locks the indexer's lock object
|
||||||
|
// ReSharper disable InconsistentlySynchronizedField
|
||||||
|
indexerLock = new UniqueLock(_sdCardIndexer.Locker);
|
||||||
|
|
||||||
case SaveDataSpaceId.SdSystem:
|
// We need to reinitialize the indexer if the SD card has changed
|
||||||
case SaveDataSpaceId.SdCache:
|
if (!_sdCardHandleManager.IsValid(in _sdCardHandle) && _sdCardIndexer.IsInitialized)
|
||||||
Monitor.Enter(_sdCardIndexer.Locker);
|
{
|
||||||
|
_sdCardIndexer.Indexer.Dispose();
|
||||||
|
_sdCardIndexer.Indexer = null;
|
||||||
|
}
|
||||||
|
|
||||||
// We need to reinitialize the indexer if the SD card has changed
|
if (!_sdCardIndexer.IsInitialized)
|
||||||
if (!_sdCardHandleManager.IsValid(in _sdCardHandle) && _sdCardIndexer.IsInitialized)
|
{
|
||||||
{
|
_sdCardIndexer.Indexer = new SaveDataIndexer(FsClient, new U8Span(SdCardIndexerMountName),
|
||||||
_sdCardIndexer.Indexer.Dispose();
|
SaveDataSpaceId.SdSystem, SaveDataId, MemoryResource);
|
||||||
_sdCardIndexer.Indexer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_sdCardIndexer.IsInitialized)
|
_sdCardHandleManager.GetHandle(out _sdCardHandle).IgnoreResult();
|
||||||
{
|
|
||||||
_sdCardIndexer.Indexer = new SaveDataIndexer(FsClient, new U8Span(SdCardIndexerMountName),
|
|
||||||
SaveDataSpaceId.SdSystem, SaveDataId, MemoryResource);
|
|
||||||
|
|
||||||
_sdCardHandleManager.GetHandle(out _sdCardHandle).IgnoreResult();
|
neededInit = true;
|
||||||
|
}
|
||||||
|
|
||||||
neededInit = true;
|
indexer = _sdCardIndexer.Indexer;
|
||||||
}
|
// ReSharper restore InconsistentlySynchronizedField
|
||||||
|
|
||||||
accessor = new SaveDataIndexerAccessor(_sdCardIndexer.Indexer, _sdCardIndexer.Locker);
|
break;
|
||||||
return Result.Success;
|
case SaveDataSpaceId.Temporary:
|
||||||
|
indexerLock = new UniqueLock(_tempIndexer.Locker);
|
||||||
|
|
||||||
case SaveDataSpaceId.Temporary:
|
indexer = _tempIndexer.Indexer;
|
||||||
Monitor.Enter(_tempIndexer.Locker);
|
break;
|
||||||
|
case SaveDataSpaceId.ProperSystem:
|
||||||
|
indexerLock = new UniqueLock(_properSystemIndexer.Locker);
|
||||||
|
|
||||||
accessor = new SaveDataIndexerAccessor(_tempIndexer.Indexer, _tempIndexer.Locker);
|
if (!_properSystemIndexer.IsInitialized)
|
||||||
return Result.Success;
|
{
|
||||||
|
_properSystemIndexer.Indexer = new SaveDataIndexer(FsClient,
|
||||||
|
new U8Span(ProperSystemIndexerMountName),
|
||||||
|
SaveDataSpaceId.ProperSystem, SaveDataId, MemoryResource);
|
||||||
|
|
||||||
case SaveDataSpaceId.ProperSystem:
|
neededInit = true;
|
||||||
Monitor.Enter(_properSystemIndexer.Locker);
|
}
|
||||||
|
|
||||||
if (!_properSystemIndexer.IsInitialized)
|
indexer = _properSystemIndexer.Indexer;
|
||||||
{
|
break;
|
||||||
_properSystemIndexer.Indexer = new SaveDataIndexer(FsClient, new U8Span(ProperSystemIndexerMountName),
|
case SaveDataSpaceId.SafeMode:
|
||||||
SaveDataSpaceId.ProperSystem, SaveDataId, MemoryResource);
|
indexerLock = new UniqueLock(_safeIndexer.Locker);
|
||||||
|
|
||||||
neededInit = true;
|
if (!_safeIndexer.IsInitialized)
|
||||||
}
|
{
|
||||||
|
_safeIndexer.Indexer = new SaveDataIndexer(FsClient, new U8Span(SafeModeIndexerMountName),
|
||||||
|
SaveDataSpaceId.SafeMode, SaveDataId, MemoryResource);
|
||||||
|
|
||||||
accessor = new SaveDataIndexerAccessor(_properSystemIndexer.Indexer, _properSystemIndexer.Locker);
|
neededInit = true;
|
||||||
return Result.Success;
|
}
|
||||||
|
|
||||||
case SaveDataSpaceId.SafeMode:
|
indexer = _safeIndexer.Indexer;
|
||||||
Monitor.Enter(_safeIndexer.Locker);
|
break;
|
||||||
|
|
||||||
if (!_safeIndexer.IsInitialized)
|
default:
|
||||||
{
|
accessor = default;
|
||||||
_safeIndexer.Indexer = new SaveDataIndexer(FsClient, new U8Span(SafeModeIndexerMountName),
|
return ResultFs.InvalidArgument.Log();
|
||||||
SaveDataSpaceId.SafeMode, SaveDataId, MemoryResource);
|
}
|
||||||
|
|
||||||
neededInit = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
accessor = new SaveDataIndexerAccessor(_safeIndexer.Indexer, _safeIndexer.Locker);
|
accessor = new SaveDataIndexerAccessor(indexer, ref indexerLock);
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
|
}
|
||||||
default:
|
finally
|
||||||
accessor = default;
|
{
|
||||||
return ResultFs.InvalidArgument.Log();
|
indexerLock.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetTemporaryStorageIndexer(SaveDataSpaceId spaceId)
|
public void ResetIndexer(SaveDataSpaceId spaceId)
|
||||||
{
|
{
|
||||||
if (spaceId != SaveDataSpaceId.Temporary)
|
if (spaceId != SaveDataSpaceId.Temporary)
|
||||||
{
|
{
|
||||||
@ -156,7 +171,7 @@ namespace LibHac.FsSrv
|
|||||||
Assert.AssertTrue(rc.IsSuccess());
|
Assert.AssertTrue(rc.IsSuccess());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InvalidateSdCardIndexer(SaveDataSpaceId spaceId)
|
public void InvalidateIndexer(SaveDataSpaceId spaceId)
|
||||||
{
|
{
|
||||||
// Note: Nintendo doesn't lock when doing this operation
|
// Note: Nintendo doesn't lock when doing this operation
|
||||||
lock (_sdCardIndexer.Locker)
|
lock (_sdCardIndexer.Locker)
|
||||||
@ -225,24 +240,17 @@ namespace LibHac.FsSrv
|
|||||||
public class SaveDataIndexerAccessor : IDisposable
|
public class SaveDataIndexerAccessor : IDisposable
|
||||||
{
|
{
|
||||||
public ISaveDataIndexer Indexer { get; }
|
public ISaveDataIndexer Indexer { get; }
|
||||||
private object Locker { get; }
|
private UniqueLock _locker;
|
||||||
private bool HasLock { get; set; }
|
|
||||||
|
|
||||||
public SaveDataIndexerAccessor(ISaveDataIndexer indexer, object locker)
|
public SaveDataIndexerAccessor(ISaveDataIndexer indexer, ref UniqueLock locker)
|
||||||
{
|
{
|
||||||
Indexer = indexer;
|
Indexer = indexer;
|
||||||
Locker = locker;
|
_locker = new UniqueLock(ref locker);
|
||||||
HasLock = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (HasLock)
|
_locker.Dispose();
|
||||||
{
|
|
||||||
Monitor.Exit(Locker);
|
|
||||||
|
|
||||||
HasLock = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,41 +2,45 @@
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
|
using LibHac.FsSrv.Sf;
|
||||||
using LibHac.Ncm;
|
using LibHac.Ncm;
|
||||||
|
using LibHac.Sf;
|
||||||
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.FsSrv
|
namespace LibHac.FsSrv
|
||||||
{
|
{
|
||||||
internal class SaveDataInfoFilterReader : ISaveDataInfoReader
|
internal class SaveDataInfoFilterReader : SaveDataInfoReaderImpl, ISaveDataInfoReader
|
||||||
{
|
{
|
||||||
private ReferenceCountedDisposable<ISaveDataInfoReader> Reader { get; }
|
private ReferenceCountedDisposable<SaveDataInfoReaderImpl> Reader { get; }
|
||||||
private SaveDataFilterInternal Filter { get; }
|
private SaveDataInfoFilter InfoFilter { get; }
|
||||||
|
|
||||||
public SaveDataInfoFilterReader(ReferenceCountedDisposable<ISaveDataInfoReader> reader, ref SaveDataFilterInternal filter)
|
public SaveDataInfoFilterReader(ReferenceCountedDisposable<SaveDataInfoReaderImpl> reader,
|
||||||
|
in SaveDataInfoFilter infoFilter)
|
||||||
{
|
{
|
||||||
Reader = reader;
|
Reader = reader.AddReference();
|
||||||
Filter = filter;
|
InfoFilter = infoFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result Read(out long readCount, Span<byte> saveDataInfoBuffer)
|
public Result Read(out long readCount, OutBuffer saveDataInfoBuffer)
|
||||||
{
|
{
|
||||||
readCount = default;
|
readCount = default;
|
||||||
|
|
||||||
Span<SaveDataInfo> outInfo = MemoryMarshal.Cast<byte, SaveDataInfo>(saveDataInfoBuffer);
|
Span<SaveDataInfo> outInfo = MemoryMarshal.Cast<byte, SaveDataInfo>(saveDataInfoBuffer.Buffer);
|
||||||
|
|
||||||
SaveDataInfo tempInfo = default;
|
SaveDataInfo tempInfo = default;
|
||||||
Span<byte> tempInfoBytes = SpanHelpers.AsByteSpan(ref tempInfo);
|
Span<byte> tempInfoBytes = SpanHelpers.AsByteSpan(ref tempInfo);
|
||||||
|
|
||||||
ISaveDataInfoReader reader = Reader.Target;
|
SaveDataInfoReaderImpl reader = Reader.Target;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
while (count < outInfo.Length)
|
while (count < outInfo.Length)
|
||||||
{
|
{
|
||||||
Result rc = reader.Read(out long baseReadCount, tempInfoBytes);
|
Result rc = reader.Read(out long baseReadCount, new OutBuffer(tempInfoBytes));
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
if (baseReadCount == 0) break;
|
if (baseReadCount == 0) break;
|
||||||
|
|
||||||
if (Filter.Matches(ref tempInfo))
|
if (InfoFilter.Includes(in tempInfo))
|
||||||
{
|
{
|
||||||
outInfo[count] = tempInfo;
|
outInfo[count] = tempInfo;
|
||||||
|
|
||||||
@ -55,133 +59,97 @@ namespace LibHac.FsSrv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x50)]
|
[StructLayout(LayoutKind.Sequential, Size = 0x60)]
|
||||||
internal struct SaveDataFilterInternal
|
internal struct SaveDataInfoFilter
|
||||||
{
|
{
|
||||||
[FieldOffset(0x00)] public bool FilterBySaveDataSpaceId;
|
public Optional<SaveDataSpaceId> SpaceId;
|
||||||
[FieldOffset(0x01)] public SaveDataSpaceId SpaceId;
|
public Optional<ProgramId> ProgramId;
|
||||||
|
public Optional<SaveDataType> SaveDataType;
|
||||||
|
public Optional<UserId> UserId;
|
||||||
|
public Optional<ulong> SaveDataId;
|
||||||
|
public Optional<ushort> Index;
|
||||||
|
public int Rank;
|
||||||
|
|
||||||
[FieldOffset(0x08)] public bool FilterByProgramId;
|
public SaveDataInfoFilter(in SaveDataInfoFilter filter)
|
||||||
[FieldOffset(0x10)] public ProgramId ProgramId;
|
|
||||||
|
|
||||||
[FieldOffset(0x18)] public bool FilterBySaveDataType;
|
|
||||||
[FieldOffset(0x19)] public SaveDataType SaveDataType;
|
|
||||||
|
|
||||||
[FieldOffset(0x20)] public bool FilterByUserId;
|
|
||||||
[FieldOffset(0x28)] public UserId UserId;
|
|
||||||
|
|
||||||
[FieldOffset(0x38)] public bool FilterBySaveDataId;
|
|
||||||
[FieldOffset(0x40)] public ulong SaveDataId;
|
|
||||||
|
|
||||||
[FieldOffset(0x48)] public bool FilterByIndex;
|
|
||||||
[FieldOffset(0x4A)] public short Index;
|
|
||||||
|
|
||||||
[FieldOffset(0x4C)] public int Rank;
|
|
||||||
|
|
||||||
public SaveDataFilterInternal(ref SaveDataFilter filter, SaveDataSpaceId spaceId)
|
|
||||||
{
|
{
|
||||||
this = default;
|
this = filter;
|
||||||
|
}
|
||||||
|
|
||||||
FilterBySaveDataSpaceId = true;
|
public SaveDataInfoFilter(SaveDataSpaceId spaceId, in SaveDataFilter filter)
|
||||||
SpaceId = spaceId;
|
{
|
||||||
|
// Start out with no optional values
|
||||||
|
this = new SaveDataInfoFilter();
|
||||||
|
|
||||||
|
SpaceId = new Optional<SaveDataSpaceId>(spaceId);
|
||||||
Rank = (int)filter.Rank;
|
Rank = (int)filter.Rank;
|
||||||
|
|
||||||
if (filter.FilterByProgramId)
|
if (filter.FilterByProgramId)
|
||||||
{
|
{
|
||||||
FilterByProgramId = true;
|
ProgramId = new Optional<ProgramId>(in filter.Attribute.ProgramId);
|
||||||
ProgramId = filter.ProgramId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter.FilterBySaveDataType)
|
if (filter.FilterBySaveDataType)
|
||||||
{
|
{
|
||||||
FilterBySaveDataType = true;
|
SaveDataType = new Optional<SaveDataType>(in filter.Attribute.Type);
|
||||||
SaveDataType = filter.SaveDataType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter.FilterByUserId)
|
if (filter.FilterByUserId)
|
||||||
{
|
{
|
||||||
FilterByUserId = true;
|
UserId = new Optional<UserId>(in filter.Attribute.UserId);
|
||||||
UserId = filter.UserId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter.FilterBySaveDataId)
|
if (filter.FilterBySaveDataId)
|
||||||
{
|
{
|
||||||
FilterBySaveDataId = true;
|
SaveDataId = new Optional<ulong>(in filter.Attribute.StaticSaveDataId);
|
||||||
SaveDataId = filter.SaveDataId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filter.FilterByIndex)
|
if (filter.FilterByIndex)
|
||||||
{
|
{
|
||||||
FilterByIndex = true;
|
Index = new Optional<ushort>(in filter.Attribute.Index);
|
||||||
Index = filter.Index;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetSaveDataSpaceId(SaveDataSpaceId spaceId)
|
public SaveDataInfoFilter(Optional<SaveDataSpaceId> spaceId, Optional<ProgramId> programId,
|
||||||
|
Optional<SaveDataType> saveDataType, Optional<UserId> userId, Optional<ulong> saveDataId,
|
||||||
|
Optional<ushort> index, int rank)
|
||||||
{
|
{
|
||||||
FilterBySaveDataSpaceId = true;
|
|
||||||
SpaceId = spaceId;
|
SpaceId = spaceId;
|
||||||
|
ProgramId = programId;
|
||||||
|
SaveDataType = saveDataType;
|
||||||
|
UserId = userId;
|
||||||
|
SaveDataId = saveDataId;
|
||||||
|
Index = index;
|
||||||
|
Rank = rank;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetProgramId(ProgramId value)
|
public bool Includes(in SaveDataInfo saveInfo)
|
||||||
{
|
{
|
||||||
FilterByProgramId = true;
|
if (SpaceId.HasValue && saveInfo.SpaceId != SpaceId.Value)
|
||||||
ProgramId = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetSaveDataType(SaveDataType value)
|
|
||||||
{
|
|
||||||
FilterBySaveDataType = true;
|
|
||||||
SaveDataType = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetUserId(UserId value)
|
|
||||||
{
|
|
||||||
FilterByUserId = true;
|
|
||||||
UserId = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetSaveDataId(ulong value)
|
|
||||||
{
|
|
||||||
FilterBySaveDataId = true;
|
|
||||||
SaveDataId = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetIndex(short value)
|
|
||||||
{
|
|
||||||
FilterByIndex = true;
|
|
||||||
Index = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Matches(ref SaveDataInfo info)
|
|
||||||
{
|
|
||||||
if (FilterBySaveDataSpaceId && info.SpaceId != SpaceId)
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FilterByProgramId && info.ProgramId != ProgramId)
|
if (ProgramId.HasValue && saveInfo.ProgramId != ProgramId.Value)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FilterBySaveDataType && info.Type != SaveDataType)
|
if (SaveDataType.HasValue && saveInfo.Type != SaveDataType.Value)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FilterByUserId && info.UserId != UserId)
|
if (UserId.HasValue && saveInfo.UserId != UserId.Value)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FilterBySaveDataId && info.SaveDataId != SaveDataId)
|
if (SaveDataId.HasValue && saveInfo.SaveDataId != SaveDataId.Value)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FilterByIndex && info.Index != Index)
|
if (Index.HasValue && saveInfo.Index != Index.Value)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -189,7 +157,7 @@ namespace LibHac.FsSrv
|
|||||||
var filterRank = (SaveDataRank)(Rank & 1);
|
var filterRank = (SaveDataRank)(Rank & 1);
|
||||||
|
|
||||||
// When filtering by secondary rank, match on both primary and secondary ranks
|
// When filtering by secondary rank, match on both primary and secondary ranks
|
||||||
if (filterRank == SaveDataRank.Primary && info.Rank == SaveDataRank.Secondary)
|
if (filterRank == SaveDataRank.Primary && saveInfo.Rank == SaveDataRank.Secondary)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
28
src/LibHac/FsSrv/SaveDataInfoReaderImpl.cs
Normal file
28
src/LibHac/FsSrv/SaveDataInfoReaderImpl.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.Sf;
|
||||||
|
|
||||||
|
namespace LibHac.FsSrv
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Iterates through the <see cref="SaveDataInfo"/> of the save data
|
||||||
|
/// in a single save data space.
|
||||||
|
/// </summary>
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
// Kinda weird to name an interface / pure abstract class SaveDataInfoReaderImpl. Ask Nintendo, not me.
|
||||||
|
//
|
||||||
|
public interface SaveDataInfoReaderImpl : IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the next <see cref="SaveDataInfo"/> entries. This method will continue writing
|
||||||
|
/// entries to <paramref name="saveDataInfoBuffer"/> until there is either no more space
|
||||||
|
/// in the buffer, or until there are no more entries to iterate.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="readCount">If the method returns successfully, contains the number
|
||||||
|
/// of <see cref="SaveDataInfo"/> written to <paramref name="saveDataInfoBuffer"/>.
|
||||||
|
/// A value of 0 indicates that there are no more entries to iterate, or the buffer is too small.</param>
|
||||||
|
/// <param name="saveDataInfoBuffer">The buffer in which to write the <see cref="SaveDataInfo"/>.</param>
|
||||||
|
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||||
|
Result Read(out long readCount, OutBuffer saveDataInfoBuffer);
|
||||||
|
}
|
||||||
|
}
|
35
src/LibHac/FsSrv/SaveDataTransferCryptoConfiguration.cs
Normal file
35
src/LibHac/FsSrv/SaveDataTransferCryptoConfiguration.cs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
using System;
|
||||||
|
using LibHac.Crypto;
|
||||||
|
|
||||||
|
namespace LibHac.FsSrv
|
||||||
|
{
|
||||||
|
public class SaveDataTransferCryptoConfiguration
|
||||||
|
{
|
||||||
|
private Data100 _tokenSigningKeyModulus;
|
||||||
|
private Data100 _keySeedPackageSigningKeyModulus;
|
||||||
|
private Data100 _kekEncryptionKeyModulus;
|
||||||
|
private Data100 _keyPackageSigningModulus;
|
||||||
|
|
||||||
|
public Span<byte> TokenSigningKeyModulus => _tokenSigningKeyModulus.Data;
|
||||||
|
public Span<byte> KeySeedPackageSigningKeyModulus => _keySeedPackageSigningKeyModulus.Data;
|
||||||
|
public Span<byte> KekEncryptionKeyModulus => _kekEncryptionKeyModulus.Data;
|
||||||
|
public Span<byte> KeyPackageSigningModulus => _keyPackageSigningModulus.Data;
|
||||||
|
|
||||||
|
public SaveTransferAesKeyGenerator GenerateAesKey { get; set; }
|
||||||
|
public RandomDataGenerator GenerateRandomData { get; set; }
|
||||||
|
public SaveTransferCmacGenerator GenerateCmac { get; set; }
|
||||||
|
|
||||||
|
public enum KeyIndex
|
||||||
|
{
|
||||||
|
SaveDataTransferToken,
|
||||||
|
SaveDataTransfer,
|
||||||
|
SaveDataTransferKeySeedPackage,
|
||||||
|
CloudBackUpInitialData,
|
||||||
|
CloudBackUpImportContext,
|
||||||
|
CloudBackUpInitialDataMac,
|
||||||
|
SaveDataRepairKeyPackage,
|
||||||
|
SaveDataRepairInitialDataMacBeforeRepair,
|
||||||
|
SaveDataRepairInitialDataMacAfterRepair
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
src/LibHac/FsSrv/Sf/IMultiCommitManager.cs
Normal file
10
src/LibHac/FsSrv/Sf/IMultiCommitManager.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace LibHac.FsSrv.Sf
|
||||||
|
{
|
||||||
|
public interface IMultiCommitManager : IDisposable
|
||||||
|
{
|
||||||
|
Result Add(ReferenceCountedDisposable<IFileSystemSf> fileSystem);
|
||||||
|
Result Commit();
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
|
using LibHac.Sf;
|
||||||
|
|
||||||
namespace LibHac.FsSrv
|
namespace LibHac.FsSrv.Sf
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Iterates through the <see cref="SaveDataInfo"/> of the save data
|
/// Iterates through the <see cref="SaveDataInfo"/> of the save data
|
||||||
@ -19,6 +20,6 @@ namespace LibHac.FsSrv
|
|||||||
/// A value of 0 indicates that there are no more entries to iterate, or the buffer is too small.</param>
|
/// A value of 0 indicates that there are no more entries to iterate, or the buffer is too small.</param>
|
||||||
/// <param name="saveDataInfoBuffer">The buffer in which to write the <see cref="SaveDataInfo"/>.</param>
|
/// <param name="saveDataInfoBuffer">The buffer in which to write the <see cref="SaveDataInfo"/>.</param>
|
||||||
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||||
Result Read(out long readCount, Span<byte> saveDataInfoBuffer);
|
Result Read(out long readCount, OutBuffer saveDataInfoBuffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
11
src/LibHac/FsSrv/Sf/ISaveDataMover.cs
Normal file
11
src/LibHac/FsSrv/Sf/ISaveDataMover.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace LibHac.FsSrv.Sf
|
||||||
|
{
|
||||||
|
public interface ISaveDataMover : IDisposable
|
||||||
|
{
|
||||||
|
Result Register(ulong saveDataId);
|
||||||
|
Result Process(out long remainingSize, long sizeToProcess);
|
||||||
|
Result Cancel();
|
||||||
|
}
|
||||||
|
}
|
@ -47,7 +47,7 @@ namespace LibHac.FsSrv
|
|||||||
|
|
||||||
public static Result VerifyHostPath(U8Span path)
|
public static Result VerifyHostPath(U8Span path)
|
||||||
{
|
{
|
||||||
if(path.IsEmpty())
|
if (path.IsEmpty())
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
|
|
||||||
if (path[0] != StringTraits.DirectorySeparator)
|
if (path[0] != StringTraits.DirectorySeparator)
|
||||||
@ -55,7 +55,7 @@ namespace LibHac.FsSrv
|
|||||||
|
|
||||||
U8Span path2 = path.Slice(1);
|
U8Span path2 = path.Slice(1);
|
||||||
|
|
||||||
if(path2.IsEmpty())
|
if (path2.IsEmpty())
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
|
|
||||||
int skipLength = PathUtility.GetWindowsPathSkipLength(path2);
|
int skipLength = PathUtility.GetWindowsPathSkipLength(path2);
|
||||||
|
@ -11,14 +11,16 @@ namespace LibHac.FsSystem
|
|||||||
public int BlockSize { get; }
|
public int BlockSize { get; }
|
||||||
|
|
||||||
private IFileSystem BaseFileSystem { get; }
|
private IFileSystem BaseFileSystem { get; }
|
||||||
|
private ReferenceCountedDisposable<IFileSystem> SharedBaseFileSystem { get; }
|
||||||
private byte[] KekSource { get; }
|
private byte[] KekSource { get; }
|
||||||
private byte[] ValidationKey { get; }
|
private byte[] ValidationKey { get; }
|
||||||
|
|
||||||
public AesXtsFileSystem(IFileSystem fs, byte[] kekSource, byte[] validationKey, int blockSize)
|
public AesXtsFileSystem(ReferenceCountedDisposable<IFileSystem> fs, byte[] keys, int blockSize)
|
||||||
{
|
{
|
||||||
BaseFileSystem = fs;
|
SharedBaseFileSystem = fs.AddReference();
|
||||||
KekSource = kekSource;
|
BaseFileSystem = SharedBaseFileSystem.Target;
|
||||||
ValidationKey = validationKey;
|
KekSource = keys.AsSpan(0, 0x10).ToArray();
|
||||||
|
ValidationKey = keys.AsSpan(0x10, 0x10).ToArray();
|
||||||
BlockSize = blockSize;
|
BlockSize = blockSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,6 +32,16 @@ namespace LibHac.FsSystem
|
|||||||
BlockSize = blockSize;
|
BlockSize = blockSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
SharedBaseFileSystem?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
protected override Result DoCreateDirectory(U8Span path)
|
protected override Result DoCreateDirectory(U8Span path)
|
||||||
{
|
{
|
||||||
return BaseFileSystem.CreateDirectory(path);
|
return BaseFileSystem.CreateDirectory(path);
|
||||||
|
90
src/LibHac/FsSystem/ApplicationTemporaryFileSystem.cs
Normal file
90
src/LibHac/FsSystem/ApplicationTemporaryFileSystem.cs
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
using System;
|
||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.Fs.Fsa;
|
||||||
|
|
||||||
|
namespace LibHac.FsSystem
|
||||||
|
{
|
||||||
|
public class ApplicationTemporaryFileSystem : IFileSystem, ISaveDataExtraDataAccessor
|
||||||
|
{
|
||||||
|
protected override Result DoCreateFile(U8Span path, long size, CreateFileOptions option)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoDeleteFile(U8Span path)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoCreateDirectory(U8Span path)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoDeleteDirectory(U8Span path)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoDeleteDirectoryRecursively(U8Span path)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoCleanDirectoryRecursively(U8Span path)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoRenameFile(U8Span oldPath, U8Span newPath)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoRenameDirectory(U8Span oldPath, U8Span newPath)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoGetEntryType(out DirectoryEntryType entryType, U8Span path)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoOpenFile(out IFile file, U8Span path, OpenMode mode)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoOpenDirectory(out IDirectory directory, U8Span path, OpenDirectoryMode mode)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoCommit()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result WriteExtraData(in SaveDataExtraData extraData)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result CommitExtraData(bool updateTimeStamp)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result ReadExtraData(out SaveDataExtraData extraData)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterCacheObserver(ISaveDataExtraDataAccessorCacheObserver observer, SaveDataSpaceId spaceId, ulong saveDataId)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
83
src/LibHac/FsSystem/ForwardingFileSystem.cs
Normal file
83
src/LibHac/FsSystem/ForwardingFileSystem.cs
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
using System;
|
||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.Fs.Fsa;
|
||||||
|
|
||||||
|
namespace LibHac.FsSystem
|
||||||
|
{
|
||||||
|
public class ForwardingFileSystem : IFileSystem
|
||||||
|
{
|
||||||
|
private ReferenceCountedDisposable<IFileSystem> BaseFileSystem { get; set; }
|
||||||
|
|
||||||
|
public ForwardingFileSystem(ReferenceCountedDisposable<IFileSystem> baseFileSystem)
|
||||||
|
{
|
||||||
|
BaseFileSystem = baseFileSystem.AddReference();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ForwardingFileSystem(ref ReferenceCountedDisposable<IFileSystem> baseFileSystem)
|
||||||
|
{
|
||||||
|
BaseFileSystem = Shared.Move(ref baseFileSystem);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
BaseFileSystem?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoCreateFile(U8Span path, long size, CreateFileOptions option) =>
|
||||||
|
BaseFileSystem.Target.CreateFile(path, size, option);
|
||||||
|
|
||||||
|
protected override Result DoDeleteFile(U8Span path) => BaseFileSystem.Target.DeleteFile(path);
|
||||||
|
|
||||||
|
protected override Result DoCreateDirectory(U8Span path) => BaseFileSystem.Target.CreateDirectory(path);
|
||||||
|
|
||||||
|
protected override Result DoDeleteDirectory(U8Span path) => BaseFileSystem.Target.DeleteDirectory(path);
|
||||||
|
|
||||||
|
protected override Result DoDeleteDirectoryRecursively(U8Span path) =>
|
||||||
|
BaseFileSystem.Target.DeleteDirectoryRecursively(path);
|
||||||
|
|
||||||
|
protected override Result DoCleanDirectoryRecursively(U8Span path) =>
|
||||||
|
BaseFileSystem.Target.CleanDirectoryRecursively(path);
|
||||||
|
|
||||||
|
protected override Result DoRenameFile(U8Span oldPath, U8Span newPath) =>
|
||||||
|
BaseFileSystem.Target.RenameFile(oldPath, newPath);
|
||||||
|
|
||||||
|
protected override Result DoRenameDirectory(U8Span oldPath, U8Span newPath) =>
|
||||||
|
BaseFileSystem.Target.RenameDirectory(oldPath, newPath);
|
||||||
|
|
||||||
|
protected override Result DoGetEntryType(out DirectoryEntryType entryType, U8Span path) =>
|
||||||
|
BaseFileSystem.Target.GetEntryType(out entryType, path);
|
||||||
|
|
||||||
|
protected override Result DoGetFreeSpaceSize(out long freeSpace, U8Span path) =>
|
||||||
|
BaseFileSystem.Target.GetFreeSpaceSize(out freeSpace, path);
|
||||||
|
|
||||||
|
protected override Result DoGetTotalSpaceSize(out long totalSpace, U8Span path) =>
|
||||||
|
BaseFileSystem.Target.GetTotalSpaceSize(out totalSpace, path);
|
||||||
|
|
||||||
|
protected override Result DoOpenFile(out IFile file, U8Span path, OpenMode mode) =>
|
||||||
|
BaseFileSystem.Target.OpenFile(out file, path, mode);
|
||||||
|
|
||||||
|
protected override Result DoOpenDirectory(out IDirectory directory, U8Span path, OpenDirectoryMode mode) =>
|
||||||
|
BaseFileSystem.Target.OpenDirectory(out directory, path, mode);
|
||||||
|
|
||||||
|
protected override Result DoCommit() => BaseFileSystem.Target.Commit();
|
||||||
|
|
||||||
|
protected override Result DoCommitProvisionally(long counter) =>
|
||||||
|
BaseFileSystem.Target.CommitProvisionally(counter);
|
||||||
|
|
||||||
|
protected override Result DoRollback() => BaseFileSystem.Target.Rollback();
|
||||||
|
|
||||||
|
protected override Result DoFlush() => BaseFileSystem.Target.Flush();
|
||||||
|
|
||||||
|
protected override Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, U8Span path) =>
|
||||||
|
BaseFileSystem.Target.GetFileTimeStampRaw(out timeStamp, path);
|
||||||
|
|
||||||
|
protected override Result DoQueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId,
|
||||||
|
U8Span path) => BaseFileSystem.Target.QueryEntry(outBuffer, inBuffer, queryId, path);
|
||||||
|
}
|
||||||
|
}
|
13
src/LibHac/FsSystem/ISaveDataExtraDataAccessor.cs
Normal file
13
src/LibHac/FsSystem/ISaveDataExtraDataAccessor.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using LibHac.Fs;
|
||||||
|
|
||||||
|
namespace LibHac.FsSystem
|
||||||
|
{
|
||||||
|
public interface ISaveDataExtraDataAccessor : IDisposable
|
||||||
|
{
|
||||||
|
Result WriteExtraData(in SaveDataExtraData extraData);
|
||||||
|
Result CommitExtraData(bool updateTimeStamp);
|
||||||
|
Result ReadExtraData(out SaveDataExtraData extraData);
|
||||||
|
void RegisterCacheObserver(ISaveDataExtraDataAccessorCacheObserver observer, SaveDataSpaceId spaceId, ulong saveDataId);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
using LibHac.Fs;
|
||||||
|
|
||||||
|
namespace LibHac.FsSystem
|
||||||
|
{
|
||||||
|
public interface ISaveDataExtraDataAccessorCacheObserver : IDisposable
|
||||||
|
{
|
||||||
|
void Unregister(SaveDataSpaceId spaceId, ulong saveDataId);
|
||||||
|
}
|
||||||
|
}
|
14
src/LibHac/FsSystem/ISaveDataFileSystemCacheManager.cs
Normal file
14
src/LibHac/FsSystem/ISaveDataFileSystemCacheManager.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.FsSystem.Save;
|
||||||
|
|
||||||
|
namespace LibHac.FsSystem
|
||||||
|
{
|
||||||
|
public interface ISaveDataFileSystemCacheManager : IDisposable
|
||||||
|
{
|
||||||
|
bool GetCache(out ReferenceCountedDisposable<SaveDataFileSystem> fileSystem, SaveDataSpaceId spaceId, ulong saveDataId);
|
||||||
|
void Register(ReferenceCountedDisposable<ApplicationTemporaryFileSystem> fileSystem);
|
||||||
|
void Register(ReferenceCountedDisposable<SaveDataFileSystem> fileSystem);
|
||||||
|
void Unregister(SaveDataSpaceId spaceId, ulong saveDataId);
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace LibHac.FsSystem
|
|
||||||
{
|
|
||||||
public interface IUniqueLock : IDisposable
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
@ -21,6 +21,13 @@ namespace LibHac.FsSystem
|
|||||||
BaseFs = BaseFsShared.Target;
|
BaseFs = BaseFsShared.Target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ReferenceCountedDisposable<IFileSystem> CreateShared(
|
||||||
|
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem)
|
||||||
|
{
|
||||||
|
var fs = new ReadOnlyFileSystem(Shared.Move(ref baseFileSystem));
|
||||||
|
return new ReferenceCountedDisposable<IFileSystem>(fs);
|
||||||
|
}
|
||||||
|
|
||||||
protected override Result DoOpenDirectory(out IDirectory directory, U8Span path, OpenDirectoryMode mode)
|
protected override Result DoOpenDirectory(out IDirectory directory, U8Span path, OpenDirectoryMode mode)
|
||||||
{
|
{
|
||||||
return BaseFs.OpenDirectory(out directory, path, mode);
|
return BaseFs.OpenDirectory(out directory, path, mode);
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
namespace LibHac.FsSystem.Save
|
|
||||||
{
|
|
||||||
public interface ISaveDataExtraDataAccessor
|
|
||||||
{
|
|
||||||
Result Write(ExtraData data);
|
|
||||||
Result Commit();
|
|
||||||
Result Read(out ExtraData data);
|
|
||||||
}
|
|
||||||
}
|
|
@ -72,7 +72,7 @@ namespace LibHac.FsSystem.Save
|
|||||||
MapStorage = MetaRemapStorage.Slice(layout.JournalMapTableOffset, layout.JournalMapTableSize),
|
MapStorage = MetaRemapStorage.Slice(layout.JournalMapTableOffset, layout.JournalMapTableSize),
|
||||||
PhysicalBlockBitmap = MetaRemapStorage.Slice(layout.JournalPhysicalBitmapOffset, layout.JournalPhysicalBitmapSize),
|
PhysicalBlockBitmap = MetaRemapStorage.Slice(layout.JournalPhysicalBitmapOffset, layout.JournalPhysicalBitmapSize),
|
||||||
VirtualBlockBitmap = MetaRemapStorage.Slice(layout.JournalVirtualBitmapOffset, layout.JournalVirtualBitmapSize),
|
VirtualBlockBitmap = MetaRemapStorage.Slice(layout.JournalVirtualBitmapOffset, layout.JournalVirtualBitmapSize),
|
||||||
FreeBlockBitmap = MetaRemapStorage.Slice(layout.JournalFreeBitmapOffset, layout.JournalFreeBitmapSize),
|
FreeBlockBitmap = MetaRemapStorage.Slice(layout.JournalFreeBitmapOffset, layout.JournalFreeBitmapSize)
|
||||||
};
|
};
|
||||||
|
|
||||||
IStorage journalData = DataRemapStorage.Slice(layout.JournalDataOffset,
|
IStorage journalData = DataRemapStorage.Slice(layout.JournalDataOffset,
|
||||||
|
165
src/LibHac/FsSystem/StorageLayoutTypeSetFileSystem.cs
Normal file
165
src/LibHac/FsSystem/StorageLayoutTypeSetFileSystem.cs
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
using System;
|
||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.Fs.Fsa;
|
||||||
|
|
||||||
|
namespace LibHac.FsSystem
|
||||||
|
{
|
||||||
|
internal class StorageLayoutTypeSetFileSystem : IFileSystem
|
||||||
|
{
|
||||||
|
private ReferenceCountedDisposable<IFileSystem> BaseFileSystem { get; }
|
||||||
|
private StorageType StorageFlag { get; }
|
||||||
|
|
||||||
|
public StorageLayoutTypeSetFileSystem(ReferenceCountedDisposable<IFileSystem> baseFileSystem,
|
||||||
|
StorageType storageFlag)
|
||||||
|
{
|
||||||
|
BaseFileSystem = baseFileSystem.AddReference();
|
||||||
|
StorageFlag = storageFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected StorageLayoutTypeSetFileSystem(ref ReferenceCountedDisposable<IFileSystem> baseFileSystem,
|
||||||
|
StorageType storageFlag)
|
||||||
|
{
|
||||||
|
BaseFileSystem = Shared.Move(ref baseFileSystem);
|
||||||
|
StorageFlag = storageFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ReferenceCountedDisposable<IFileSystem> CreateShared(
|
||||||
|
ReferenceCountedDisposable<IFileSystem> baseFileSystem, StorageType storageFlag)
|
||||||
|
{
|
||||||
|
return new ReferenceCountedDisposable<IFileSystem>(
|
||||||
|
new StorageLayoutTypeSetFileSystem(baseFileSystem, storageFlag));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ReferenceCountedDisposable<IFileSystem> CreateShared(
|
||||||
|
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, StorageType storageFlag)
|
||||||
|
{
|
||||||
|
return new ReferenceCountedDisposable<IFileSystem>(
|
||||||
|
new StorageLayoutTypeSetFileSystem(ref baseFileSystem, storageFlag));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
BaseFileSystem?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoCreateFile(U8Span path, long size, CreateFileOptions option)
|
||||||
|
{
|
||||||
|
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageFlag);
|
||||||
|
return BaseFileSystem.Target.CreateFile(path, size, option);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoDeleteFile(U8Span path)
|
||||||
|
{
|
||||||
|
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageFlag);
|
||||||
|
return BaseFileSystem.Target.DeleteFile(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoCreateDirectory(U8Span path)
|
||||||
|
{
|
||||||
|
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageFlag);
|
||||||
|
return BaseFileSystem.Target.CreateDirectory(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoDeleteDirectory(U8Span path)
|
||||||
|
{
|
||||||
|
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageFlag);
|
||||||
|
return BaseFileSystem.Target.DeleteDirectory(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoDeleteDirectoryRecursively(U8Span path)
|
||||||
|
{
|
||||||
|
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageFlag);
|
||||||
|
return BaseFileSystem.Target.DeleteDirectoryRecursively(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoCleanDirectoryRecursively(U8Span path)
|
||||||
|
{
|
||||||
|
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageFlag);
|
||||||
|
return BaseFileSystem.Target.CleanDirectoryRecursively(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoRenameFile(U8Span oldPath, U8Span newPath)
|
||||||
|
{
|
||||||
|
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageFlag);
|
||||||
|
return BaseFileSystem.Target.RenameFile(oldPath, newPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoRenameDirectory(U8Span oldPath, U8Span newPath)
|
||||||
|
{
|
||||||
|
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageFlag);
|
||||||
|
return BaseFileSystem.Target.RenameDirectory(oldPath, newPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoGetEntryType(out DirectoryEntryType entryType, U8Span path)
|
||||||
|
{
|
||||||
|
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageFlag);
|
||||||
|
return BaseFileSystem.Target.GetEntryType(out entryType, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoGetFreeSpaceSize(out long freeSpace, U8Span path)
|
||||||
|
{
|
||||||
|
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageFlag);
|
||||||
|
return BaseFileSystem.Target.GetFreeSpaceSize(out freeSpace, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoGetTotalSpaceSize(out long totalSpace, U8Span path)
|
||||||
|
{
|
||||||
|
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageFlag);
|
||||||
|
return BaseFileSystem.Target.GetTotalSpaceSize(out totalSpace, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoOpenFile(out IFile file, U8Span path, OpenMode mode)
|
||||||
|
{
|
||||||
|
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageFlag);
|
||||||
|
return BaseFileSystem.Target.OpenFile(out file, path, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoOpenDirectory(out IDirectory directory, U8Span path, OpenDirectoryMode mode)
|
||||||
|
{
|
||||||
|
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageFlag);
|
||||||
|
return BaseFileSystem.Target.OpenDirectory(out directory, path, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoCommit()
|
||||||
|
{
|
||||||
|
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageFlag);
|
||||||
|
return BaseFileSystem.Target.Commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoCommitProvisionally(long counter)
|
||||||
|
{
|
||||||
|
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageFlag);
|
||||||
|
return BaseFileSystem.Target.CommitProvisionally(counter);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoRollback()
|
||||||
|
{
|
||||||
|
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageFlag);
|
||||||
|
return BaseFileSystem.Target.Rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoFlush()
|
||||||
|
{
|
||||||
|
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageFlag);
|
||||||
|
return BaseFileSystem.Target.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, U8Span path)
|
||||||
|
{
|
||||||
|
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageFlag);
|
||||||
|
return BaseFileSystem.Target.GetFileTimeStampRaw(out timeStamp, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Result DoQueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId, U8Span path)
|
||||||
|
{
|
||||||
|
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(StorageFlag);
|
||||||
|
return BaseFileSystem.Target.QueryEntry(outBuffer, inBuffer, queryId, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
src/LibHac/FsSystem/StorageLayoutTypeSetter.cs
Normal file
30
src/LibHac/FsSystem/StorageLayoutTypeSetter.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace LibHac.FsSystem
|
||||||
|
{
|
||||||
|
internal struct ScopedStorageLayoutTypeSetter : IDisposable
|
||||||
|
{
|
||||||
|
// ReSharper disable once UnusedParameter.Local
|
||||||
|
public ScopedStorageLayoutTypeSetter(StorageType storageFlag)
|
||||||
|
{
|
||||||
|
// Todo: Implement
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
internal enum StorageType
|
||||||
|
{
|
||||||
|
Bis = 1 << 0,
|
||||||
|
SdCard = 1 << 1,
|
||||||
|
GameCard = 1 << 2,
|
||||||
|
Usb = 1 << 3,
|
||||||
|
|
||||||
|
NonGameCard = Bis | SdCard | Usb,
|
||||||
|
All = Bis | SdCard | GameCard | Usb
|
||||||
|
}
|
||||||
|
}
|
97
src/LibHac/FsSystem/UniqueLockSemaphore.cs
Normal file
97
src/LibHac/FsSystem/UniqueLockSemaphore.cs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using LibHac.Common;
|
||||||
|
using LibHac.Diag;
|
||||||
|
|
||||||
|
namespace LibHac.FsSystem
|
||||||
|
{
|
||||||
|
public interface IUniqueLock : IDisposable
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a lock that may be passed between functions or objects.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>This struct must never be copied. It must always be passed by
|
||||||
|
/// reference or moved via the move constructor.</remarks>
|
||||||
|
public struct UniqueLockSemaphore : IDisposable
|
||||||
|
{
|
||||||
|
private SemaphoreAdaptor _semaphore;
|
||||||
|
private bool _isLocked;
|
||||||
|
|
||||||
|
public UniqueLockSemaphore(SemaphoreAdaptor semaphore)
|
||||||
|
{
|
||||||
|
_semaphore = semaphore;
|
||||||
|
_isLocked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniqueLockSemaphore(ref UniqueLockSemaphore other)
|
||||||
|
{
|
||||||
|
_semaphore = other._semaphore;
|
||||||
|
_isLocked = other._isLocked;
|
||||||
|
|
||||||
|
other._isLocked = false;
|
||||||
|
other._semaphore = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsLocked => _isLocked;
|
||||||
|
|
||||||
|
public bool TryLock()
|
||||||
|
{
|
||||||
|
if (_isLocked)
|
||||||
|
{
|
||||||
|
throw new SynchronizationLockException("Attempted to lock a UniqueLock that was already locked.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_isLocked = _semaphore.TryLock();
|
||||||
|
return _isLocked;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Unlock()
|
||||||
|
{
|
||||||
|
if (!_isLocked)
|
||||||
|
{
|
||||||
|
throw new SynchronizationLockException("Attempted to unlock a UniqueLock that was not locked.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_semaphore.Unlock();
|
||||||
|
_isLocked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_isLocked)
|
||||||
|
{
|
||||||
|
_semaphore.Unlock();
|
||||||
|
|
||||||
|
_isLocked = false;
|
||||||
|
_semaphore = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UniqueLockWithPin<T> : IUniqueLock where T : class, IDisposable
|
||||||
|
{
|
||||||
|
private UniqueLockSemaphore _semaphore;
|
||||||
|
private ReferenceCountedDisposable<T> _pinnedObject;
|
||||||
|
|
||||||
|
public UniqueLockWithPin(ref UniqueLockSemaphore semaphore, ref ReferenceCountedDisposable<T> pinnedObject)
|
||||||
|
{
|
||||||
|
Shared.Move(out _semaphore, ref semaphore);
|
||||||
|
Shared.Move(out _pinnedObject, ref pinnedObject);
|
||||||
|
|
||||||
|
Assert.AssertTrue(_semaphore.IsLocked);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_pinnedObject != null)
|
||||||
|
{
|
||||||
|
_semaphore.Dispose();
|
||||||
|
_pinnedObject.Dispose();
|
||||||
|
|
||||||
|
_pinnedObject = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -226,6 +226,48 @@ namespace LibHac.FsSystem
|
|||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Result TryAcquireCountSemaphore(out UniqueLockSemaphore uniqueLock, SemaphoreAdaptor semaphore)
|
||||||
|
{
|
||||||
|
UniqueLockSemaphore tempUniqueLock = default;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
tempUniqueLock = new UniqueLockSemaphore(semaphore);
|
||||||
|
|
||||||
|
if (!tempUniqueLock.TryLock())
|
||||||
|
{
|
||||||
|
uniqueLock = default;
|
||||||
|
return ResultFs.OpenCountLimit.Log();
|
||||||
|
}
|
||||||
|
|
||||||
|
uniqueLock = new UniqueLockSemaphore(ref tempUniqueLock);
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
tempUniqueLock.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result MakeUniqueLockWithPin<T>(out IUniqueLock uniqueLock, SemaphoreAdaptor semaphore,
|
||||||
|
ref ReferenceCountedDisposable<T> objectToPin) where T : class, IDisposable
|
||||||
|
{
|
||||||
|
uniqueLock = default;
|
||||||
|
|
||||||
|
UniqueLockSemaphore tempUniqueLock = default;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Result rc = TryAcquireCountSemaphore(out tempUniqueLock, semaphore);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
uniqueLock = new UniqueLockWithPin<T>(ref tempUniqueLock, ref objectToPin);
|
||||||
|
return Result.Success;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
tempUniqueLock.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static Result RetryFinitelyForTargetLocked(Func<Result> function)
|
public static Result RetryFinitelyForTargetLocked(Func<Result> function)
|
||||||
{
|
{
|
||||||
const int maxRetryCount = 10;
|
const int maxRetryCount = 10;
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
namespace LibHac.Ncm
|
using System;
|
||||||
|
|
||||||
|
namespace LibHac.Ncm
|
||||||
{
|
{
|
||||||
public readonly struct ApplicationId
|
public readonly struct ApplicationId : IEquatable<ApplicationId>
|
||||||
{
|
{
|
||||||
public readonly ulong Value;
|
public readonly ulong Value;
|
||||||
|
|
||||||
@ -20,6 +22,13 @@
|
|||||||
{
|
{
|
||||||
return Start <= programId && programId <= End;
|
return Start <= programId && programId <= End;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Equals(ApplicationId other) => Value == other.Value;
|
||||||
|
public override bool Equals(object obj) => obj is ApplicationId id && Equals(id);
|
||||||
|
public override int GetHashCode() => Value.GetHashCode();
|
||||||
|
|
||||||
|
public static bool operator ==(ApplicationId left, ApplicationId right) => left.Equals(right);
|
||||||
|
public static bool operator !=(ApplicationId left, ApplicationId right) => !left.Equals(right);
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly struct PatchId
|
public readonly struct PatchId
|
||||||
|
@ -441,6 +441,24 @@ namespace LibHac
|
|||||||
|
|
||||||
return TryAddReferenceImpl(target, referenceCount);
|
return TryAddReferenceImpl(target, referenceCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Increments the reference count for the disposable object, and returns a new disposable reference to
|
||||||
|
/// it.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>Unlike <see cref="ReferenceCountedDisposable{T}.TryAddReference"/>, this method is capable of
|
||||||
|
/// adding a reference to the underlying instance all the way up to the point where it is finally
|
||||||
|
/// disposed.</para>
|
||||||
|
///
|
||||||
|
/// <para>The returned object is an independent reference to the same underlying object. Disposing of
|
||||||
|
/// the returned value multiple times will only cause the reference count to be decreased once.</para>
|
||||||
|
/// </remarks>
|
||||||
|
/// <returns>A new <see cref="ReferenceCountedDisposable{T}"/> pointing to the same underlying object,
|
||||||
|
/// if it has not yet been disposed; otherwise, <see cref="ObjectDisposedException"/> is thrown if the
|
||||||
|
/// underlying object has already been disposed.</returns>
|
||||||
|
public ReferenceCountedDisposable<T> AddReference() =>
|
||||||
|
TryAddReference() ?? throw new ObjectDisposedException(nameof(WeakReference));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
44
src/LibHac/Sf/Buffers.cs
Normal file
44
src/LibHac/Sf/Buffers.cs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using LibHac.Common;
|
||||||
|
|
||||||
|
namespace LibHac.Sf
|
||||||
|
{
|
||||||
|
public readonly ref struct InBuffer
|
||||||
|
{
|
||||||
|
private readonly ReadOnlySpan<byte> _buffer;
|
||||||
|
|
||||||
|
public int Size => _buffer.Length;
|
||||||
|
public ReadOnlySpan<byte> Buffer => _buffer;
|
||||||
|
|
||||||
|
public InBuffer(ReadOnlySpan<byte> buffer)
|
||||||
|
{
|
||||||
|
_buffer = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static InBuffer FromStruct<T>(in T value) where T : unmanaged
|
||||||
|
{
|
||||||
|
return new InBuffer(SpanHelpers.AsReadOnlyByteSpan(in value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly ref struct OutBuffer
|
||||||
|
{
|
||||||
|
private readonly Span<byte> _buffer;
|
||||||
|
|
||||||
|
public int Size => _buffer.Length;
|
||||||
|
public Span<byte> Buffer => _buffer;
|
||||||
|
|
||||||
|
public OutBuffer(Span<byte> buffer)
|
||||||
|
{
|
||||||
|
_buffer = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static OutBuffer FromStruct<T>(ref T value) where T : unmanaged
|
||||||
|
{
|
||||||
|
return new OutBuffer(SpanHelpers.AsByteSpan(ref value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -20,7 +20,7 @@ namespace LibHac.Util.Impl
|
|||||||
// while outputs in the range [ 0x41 .. 0x46 ] ([ 'A' .. 'F' ])
|
// while outputs in the range [ 0x41 .. 0x46 ] ([ 'A' .. 'F' ])
|
||||||
// don't have the 0x20 bit set, so ORing them maps to
|
// don't have the 0x20 bit set, so ORing them maps to
|
||||||
// [ 0x61 .. 0x66 ] ([ 'a' .. 'f' ]), which is what we want.
|
// [ 0x61 .. 0x66 ] ([ 'a' .. 'f' ]), which is what we want.
|
||||||
Lower = 0x2020U,
|
Lower = 0x2020U
|
||||||
}
|
}
|
||||||
|
|
||||||
// We want to pack the incoming byte into a single integer [ 0000 HHHH 0000 LLLL ],
|
// We want to pack the incoming byte into a single integer [ 0000 HHHH 0000 LLLL ],
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using LibHac.Common;
|
||||||
using LibHac.Diag;
|
using LibHac.Diag;
|
||||||
|
|
||||||
namespace LibHac.Util
|
namespace LibHac.Util
|
||||||
@ -8,7 +9,7 @@ namespace LibHac.Util
|
|||||||
private bool _hasValue;
|
private bool _hasValue;
|
||||||
private T _value;
|
private T _value;
|
||||||
|
|
||||||
public bool HasValue => _hasValue;
|
public readonly bool HasValue => _hasValue;
|
||||||
public ref T Value
|
public ref T Value
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@ -18,6 +19,22 @@ namespace LibHac.Util
|
|||||||
return ref MemoryMarshal.CreateSpan(ref _value, 1)[0];
|
return ref MemoryMarshal.CreateSpan(ref _value, 1)[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public readonly ref readonly T ValueRo
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
Assert.AssertTrue(_hasValue);
|
||||||
|
return ref SpanHelpers.CreateReadOnlySpan(in _value, 1)[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional(in T value)
|
||||||
|
{
|
||||||
|
_value = value;
|
||||||
|
_hasValue = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator Optional<T>(in T value) => new Optional<T>(in value);
|
||||||
|
|
||||||
public void Set(in T value)
|
public void Set(in T value)
|
||||||
{
|
{
|
||||||
|
71
src/LibHac/Util/UniqueLock.cs
Normal file
71
src/LibHac/Util/UniqueLock.cs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace LibHac.Util
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a lock that may be passed between functions or objects.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>This struct must never be copied. It must always be passed by
|
||||||
|
/// reference or moved via the move constructor.</remarks>
|
||||||
|
public struct UniqueLock : IDisposable
|
||||||
|
{
|
||||||
|
private object _lockObject;
|
||||||
|
private bool _isLocked;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="UniqueLock"/> from the provided object and acquires the lock.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lockObject">The object to lock.</param>
|
||||||
|
public UniqueLock(object lockObject)
|
||||||
|
{
|
||||||
|
_lockObject = lockObject;
|
||||||
|
_isLocked = false;
|
||||||
|
|
||||||
|
Lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniqueLock(ref UniqueLock other)
|
||||||
|
{
|
||||||
|
_lockObject = other._lockObject;
|
||||||
|
_isLocked = other._isLocked;
|
||||||
|
|
||||||
|
other._isLocked = false;
|
||||||
|
other._lockObject = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsLocked => _isLocked;
|
||||||
|
|
||||||
|
public void Lock()
|
||||||
|
{
|
||||||
|
if (_isLocked)
|
||||||
|
{
|
||||||
|
throw new SynchronizationLockException("Attempted to lock a UniqueLock that was already locked.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Monitor.Enter(_lockObject, ref _isLocked);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Unlock()
|
||||||
|
{
|
||||||
|
if (!_isLocked)
|
||||||
|
{
|
||||||
|
throw new SynchronizationLockException("Attempted to unlock a UniqueLock that was not locked.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Monitor.Exit(_lockObject);
|
||||||
|
_isLocked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_isLocked)
|
||||||
|
{
|
||||||
|
Monitor.Exit(_lockObject);
|
||||||
|
|
||||||
|
_isLocked = false;
|
||||||
|
_lockObject = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,7 +14,13 @@ namespace hactoolnet
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (Run(args)) return 0;
|
if (Run(args))
|
||||||
|
{
|
||||||
|
// Console.ReadKey();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (MissingKeyException ex)
|
catch (MissingKeyException ex)
|
||||||
{
|
{
|
||||||
|
@ -60,7 +60,7 @@ namespace LibHac.Tests.Fs.FileSystemClientTests
|
|||||||
|
|
||||||
Assert.Equal(1, entriesRead);
|
Assert.Equal(1, entriesRead);
|
||||||
Assert.Equal(applicationId, info[0].ProgramId);
|
Assert.Equal(applicationId, info[0].ProgramId);
|
||||||
Assert.Equal(UserId.Zero, info[0].UserId);
|
Assert.Equal(UserId.InvalidId, info[0].UserId);
|
||||||
Assert.Equal(SaveDataType.Device, info[0].Type);
|
Assert.Equal(SaveDataType.Device, info[0].Type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ namespace LibHac.Tests.Fs.FileSystemClientTests
|
|||||||
|
|
||||||
Assert.Equal(1, entriesRead);
|
Assert.Equal(1, entriesRead);
|
||||||
Assert.Equal(applicationId, info[0].ProgramId);
|
Assert.Equal(applicationId, info[0].ProgramId);
|
||||||
Assert.Equal(UserId.Zero, info[0].UserId);
|
Assert.Equal(UserId.InvalidId, info[0].UserId);
|
||||||
Assert.Equal(SaveDataType.Bcat, info[0].Type);
|
Assert.Equal(SaveDataType.Bcat, info[0].Type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +112,7 @@ namespace LibHac.Tests.Fs.FileSystemClientTests
|
|||||||
|
|
||||||
Assert.Equal(1, entriesRead);
|
Assert.Equal(1, entriesRead);
|
||||||
Assert.Equal(applicationId, info[0].ProgramId);
|
Assert.Equal(applicationId, info[0].ProgramId);
|
||||||
Assert.Equal(UserId.Zero, info[0].UserId);
|
Assert.Equal(UserId.InvalidId, info[0].UserId);
|
||||||
Assert.Equal(SaveDataType.Temporary, info[0].Type);
|
Assert.Equal(SaveDataType.Temporary, info[0].Type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,8 +80,8 @@ namespace LibHac.Tests.Fs.FileSystemClientTests.ShimTests
|
|||||||
Assert.Equal(applicationId, info[0].ProgramId);
|
Assert.Equal(applicationId, info[0].ProgramId);
|
||||||
Assert.Equal(applicationId, info[1].ProgramId);
|
Assert.Equal(applicationId, info[1].ProgramId);
|
||||||
|
|
||||||
var expectedIndexes = new short[] { 0, 1 };
|
var expectedIndexes = new ushort[] { 0, 1 };
|
||||||
short[] actualIndexes = info.Take(2).Select(x => x.Index).OrderBy(x => x).ToArray();
|
ushort[] actualIndexes = info.Take(2).Select(x => x.Index).OrderBy(x => x).ToArray();
|
||||||
|
|
||||||
Assert.Equal(expectedIndexes, actualIndexes);
|
Assert.Equal(expectedIndexes, actualIndexes);
|
||||||
}
|
}
|
||||||
@ -294,7 +294,7 @@ namespace LibHac.Tests.Fs.FileSystemClientTests.ShimTests
|
|||||||
for (int i = 1; i <= count; i++)
|
for (int i = 1; i <= count; i++)
|
||||||
{
|
{
|
||||||
var applicationId = new Ncm.ApplicationId((uint)i);
|
var applicationId = new Ncm.ApplicationId((uint)i);
|
||||||
Result rc = fs.CreateSaveData(applicationId, UserId.Zero, 0, 0x4000, 0x4000, SaveDataFlags.None);
|
Result rc = fs.CreateSaveData(applicationId, UserId.InvalidId, 0, 0x4000, 0x4000, SaveDataFlags.None);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -305,7 +305,7 @@ namespace LibHac.Tests.Fs.FileSystemClientTests.ShimTests
|
|||||||
for (int i = 1; i <= count; i++)
|
for (int i = 1; i <= count; i++)
|
||||||
{
|
{
|
||||||
var applicationId = new Ncm.ApplicationId((uint)rng.Next());
|
var applicationId = new Ncm.ApplicationId((uint)rng.Next());
|
||||||
Result rc = fs.CreateSaveData(applicationId, UserId.Zero, 0, 0x4000, 0x4000, SaveDataFlags.None);
|
Result rc = fs.CreateSaveData(applicationId, UserId.InvalidId, 0, 0x4000, 0x4000, SaveDataFlags.None);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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