Merge pull request #167 from Thealexbarney/fsp-split

- Split `FileSystemProxyImpl` into ~8 different classes.
- Add `sf` IPC interfaces to `fssrv` and use them where appropriate
- Make `IFileSystemProxy` method signatures more accurate
- Implement LR client
- Flesh out and fully implement many `fssrv` methods
- In general, make new and previously-existing code in `fssrv` more accurate to FS 10.0.0
This commit is contained in:
Alex Barney 2020-11-11 18:20:07 -06:00 committed by GitHub
commit 46be7259d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
196 changed files with 11924 additions and 3327 deletions

View File

@ -1 +1 @@
3.1.401
3.1.402

View File

@ -1,6 +1,7 @@
Name,Index
Svc,1
Fs,2
Lr,8
Loader,9
Sf,10
Kvdb,20

1 Name Index
2 Svc 1
3 Fs 2
4 Lr 8
5 Loader 9
6 Sf 10
7 Kvdb 20

View File

@ -1,6 +1,7 @@
Name,Namespace,Path
Svc,LibHac.Svc,LibHac/Svc/ResultSvc.cs
Fs,LibHac.Fs,LibHac/Fs/ResultFs.cs
Lr,LibHac.Lr,LibHac/Lr/ResultLr.cs
Loader,LibHac.Loader,LibHac/Loader/ResultLoader.cs
Sf,LibHac.Sf,LibHac/Sf/ResultSf.cs
Kvdb,LibHac.Kvdb,LibHac/Kvdb/ResultKvdb.cs

1 Name Namespace Path
2 Svc LibHac.Svc LibHac/Svc/ResultSvc.cs
3 Fs LibHac.Fs LibHac/Fs/ResultFs.cs
4 Lr LibHac.Lr LibHac/Lr/ResultLr.cs
5 Loader LibHac.Loader LibHac/Loader/ResultLoader.cs
6 Sf LibHac.Sf LibHac/Sf/ResultSf.cs
7 Kvdb LibHac.Kvdb LibHac/Kvdb/ResultKvdb.cs

View File

@ -89,12 +89,29 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
2,3005,,OutOfRange,
2,3200,3499,AllocationMemoryFailed,
2,3256,,AllocationFailureInNcaFileSystemServiceImplA,In ParseNsp allocating FileStorageBasedFileSystem
2,3257,,AllocationFailureInNcaFileSystemServiceImplB,In ParseNca allocating FileStorageBasedFileSystem
2,3258,,AllocationFailureInProgramRegistryManagerA,In RegisterProgram allocating ProgramInfoNode
2,3264,,AllocationFailureFatFileSystemA,In Initialize allocating ProgramInfoNode
2,3280,,AllocationFailureInPartitionFileSystemCreatorA,In Create allocating PartitionFileSystemCore
2,3312,,AllocationFailureInAesXtsFileA,In Initialize allocating FileStorage
2,3313,,AllocationFailureInAesXtsFileB,In Initialize allocating AesXtsStorage
2,3314,,AllocationFailureInAesXtsFileC,In Initialize allocating AlignmentMatchingStoragePooledBuffer
2,3315,,AllocationFailureInAesXtsFileD,In Initialize allocating StorageFile
2,3383,,AllocationFailureInAesXtsFileE,In Initialize allocating SubStorage
2,3347,,AllocationFailureInPartitionFileSystemA,In Initialize allocating PartitionFileSystemMetaCore
2,3348,,AllocationFailureInPartitionFileSystemB,In DoOpenFile allocating PartitionFile
2,3349,,AllocationFailureInPartitionFileSystemC,In DoOpenDirectory allocating PartitionDirectory
2,3350,,AllocationFailureInPartitionFileSystemMetaA,In Initialize allocating metadata buffer
2,3351,,AllocationFailureInPartitionFileSystemMetaB,In Sha256 Initialize allocating metadata buffer
2,3355,,AllocationFailureInSubdirectoryFileSystemA,In Initialize allocating RootPathBuffer
2,3383,,AllocationFailureInAesXtsFileE,In Initialize
2,3394,,AllocationFailureInEncryptedFileSystemCreatorA,In Create allocating AesXtsFileSystem
2,3407,,AllocationFailureInFileSystemInterfaceAdapter, In OpenFile or OpenDirectory
2,3420,,AllocationFailureInNew,
2,3421,,AllocationFailureInCreateShared,
2,3422,,AllocationFailureInMakeUnique,
2,3423,,AllocationFailureInAllocateShared,
2,3424,,AllocationFailurePooledBufferNotEnoughSize,
2,3500,3999,MmcAccessFailed,
@ -148,8 +165,8 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
2,4464,,AllocationTableIteratedRangeEntry,
2,4501,4599,NcaCorrupted,
2,4512,,InvalidNcaFsType,
2,4527,,InvalidNcaProgramId,
2,4512,,InvalidNcaFileSystemType,
2,4527,,InvalidNcaId,
2,4601,4639,IntegrityVerificationStorageCorrupted,
2,4602,,InvalidIvfcMagic,
@ -205,6 +222,7 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
2,4812,,IncompleteBlockInZeroBitmapHashStorageFile,
2,5000,5999,Unexpected,
2,5121,,UnexpectedFatFileSystemSectorCount,
2,5307,,UnexpectedErrorInHostFileFlush,
2,5308,,UnexpectedErrorInHostFileGetSize,
2,5309,,UnknownHostFileSystemError,
@ -221,7 +239,7 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
2,6030,6059,InvalidPathForOperation,
2,6031,,DirectoryNotDeletable,
2,6032,,DestinationIsSubPathOfSource,
2,6032,,DirectoryNotRenamable,
2,6033,,PathNotFoundInSaveDataFileTable,
2,6034,,DifferentDestFileSystem,
@ -249,6 +267,7 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
2,6203,,InvalidOpenModeForWrite,
2,6300,6399,UnsupportedOperation,
2,6301,,UnsupportedCommitTarget,
2,6302,,UnsupportedOperationInSubStorageSetSize,Attempted to resize a non-resizable SubStorage.
2,6303,,UnsupportedOperationInResizableSubStorageSetSize,Attempted to resize a SubStorage that wasn't located at the end of the base storage.
2,6304,,UnsupportedOperationInMemoryStorageSetSize,
@ -260,6 +279,7 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
2,6350,,UnsupportedOperationInRoGameCardStorageWrite,
2,6351,,UnsupportedOperationInRoGameCardStorageSetSize,
2,6359,,UnsupportedOperationInConcatFsQueryEntry,
2,6362,,UnsupportedOperationInFileServiceObjectAdapterA,Called OperateRange with an invalid operation ID.
2,6364,,UnsupportedOperationModifyRomFsFileSystem,
2,6365,,UnsupportedOperationInRomFsFileSystem,Called RomFsFileSystem::CommitProvisionally.
2,6366,,UnsupportedOperationRomFsFileSystemGetSpace,
@ -278,10 +298,15 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
2,6452,,ExternalKeyAlreadyRegistered,
2,6454,,WriteStateUnflushed,
2,6456,,DirectoryNotClosed,
2,6457,,WriteModeFileNotClosed,
2,6458,,AllocatorAlreadyRegistered,
2,6459,,DefaultAllocatorUsed,
2,6461,,AllocatorAlignmentViolation,
2,6463,,MultiCommitFileSystemAlreadyAdded,The provided file system has already been added to the multi-commit manager.
2,6465,,UserNotExist,
2,6466,,DefaultGlobalFileDataCacheEnabled,
2,6467,,SaveDataRootPathUnavailable,
2,6600,6699,EntryNotFound,
2,6605,,TargetProgramNotFound,Specified program is not found in the program registry.
@ -302,6 +327,17 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
2,6905,,NotMounted,
2,6906,,SaveDataIsExtending,
8,2,,ProgramNotFound,
8,3,,DataNotFound,
8,4,,UnknownStorageId,
8,5,,LocationResolverNotFound,
8,6,,HtmlDocumentNotFound,
8,7,,AddOnContentNotFound,
8,8,,ControlNotFound,
8,9,,LegalInformationNotFound,
8,10,,DebugProgramNotFound,
8,90,,TooManyRegisteredPaths,
9,1,,TooLongArgument,
9,2,,TooManyArguments,
9,3,,TooLargeMeta,

Can't render this file because it has a wrong number of fields in line 220.

View File

@ -502,7 +502,7 @@ namespace LibHac.Boot
Package1Section.Bootloader => 0,
Package1Section.SecureMonitor => 1,
Package1Section.WarmBoot => 2,
_ => -1,
_ => -1
};
}
@ -513,7 +513,7 @@ namespace LibHac.Boot
Package1Section.Bootloader => 1,
Package1Section.SecureMonitor => 2,
Package1Section.WarmBoot => 0,
_ => -1,
_ => -1
};
}
@ -522,7 +522,7 @@ namespace LibHac.Boot
Package1Section.Bootloader => 1,
Package1Section.SecureMonitor => 0,
Package1Section.WarmBoot => 2,
_ => -1,
_ => -1
};
}

View File

@ -31,7 +31,7 @@ namespace LibHac.Common.Keys
CommonSeedDiff = Common | Seed | DifferentDev,
CommonDrvd = Common | Derived,
DeviceRoot = Device | Root,
DeviceDrvd = Device | Derived,
DeviceDrvd = Device | Derived
}
public readonly string Name;

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

View File

@ -0,0 +1,19 @@
using System.Runtime.InteropServices;
using LibHac.Common;
namespace LibHac.Fat
{
[StructLayout(LayoutKind.Explicit, Size = 0x20)]
public struct FatError
{
private const int FunctionNameLength = 0x10;
[FieldOffset(0x00)] public int Error;
[FieldOffset(0x04)] public int ExtraError;
[FieldOffset(0x08)] public int DriveId;
[FieldOffset(0x0C)] private byte _functionName;
public U8SpanMutable ErrorName =>
new U8SpanMutable(SpanHelpers.CreateSpan(ref _functionName, FunctionNameLength));
}
}

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using LibHac.Common;
using LibHac.Fs.Fsa;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
namespace LibHac.Fs.Accessors
{
@ -20,10 +21,13 @@ namespace LibHac.Fs.Accessors
private readonly object _locker = new object();
internal bool IsAccessLogEnabled { get; set; }
public IMultiCommitTarget MultiCommitTarget { get; }
public FileSystemAccessor(string name, IFileSystem baseFileSystem, FileSystemClient fsClient, ICommonMountNameGenerator nameGenerator)
public FileSystemAccessor(U8Span name, IMultiCommitTarget multiCommitTarget, IFileSystem baseFileSystem,
FileSystemClient fsClient, ICommonMountNameGenerator nameGenerator)
{
Name = name;
Name = name.ToString();
MultiCommitTarget = multiCommitTarget;
FileSystem = baseFileSystem;
FsClient = fsClient;
MountNameGenerator = nameGenerator;
@ -147,6 +151,11 @@ namespace LibHac.Fs.Accessors
return MountNameGenerator.GenerateCommonMountName(nameBuffer);
}
public ReferenceCountedDisposable<IFileSystemSf> GetMultiCommitTarget()
{
return MultiCommitTarget?.GetMultiCommitTarget();
}
internal void NotifyCloseFile(FileAccessor file)
{
lock (_locker)

View File

@ -232,7 +232,7 @@ namespace LibHac.Fs
}
private static Result EnsureApplicationCacheStorageImpl(this FileSystemClient fs, out long requiredSize,
out CacheStorageTargetMedia target, Ncm.ApplicationId applicationId, ulong saveDataOwnerId, short index,
out CacheStorageTargetMedia target, Ncm.ApplicationId applicationId, ulong saveDataOwnerId, ushort index,
long dataSize, long journalSize, bool allowExisting)
{
requiredSize = default;
@ -299,7 +299,7 @@ namespace LibHac.Fs
}
public static Result EnsureApplicationCacheStorage(this FileSystemClient fs, out long requiredSize,
out CacheStorageTargetMedia target, Ncm.ApplicationId applicationId, ulong saveDataOwnerId, short index,
out CacheStorageTargetMedia target, Ncm.ApplicationId applicationId, ulong saveDataOwnerId, ushort index,
long dataSize, long journalSize, bool allowExisting)
{
return EnsureApplicationCacheStorageImpl(fs, out requiredSize, out target, applicationId, saveDataOwnerId,
@ -334,7 +334,7 @@ namespace LibHac.Fs
}
public static Result TryCreateCacheStorage(this FileSystemClient fs, out long requiredSize,
SaveDataSpaceId spaceId, Ncm.ApplicationId applicationId, ulong saveDataOwnerId, short index,
SaveDataSpaceId spaceId, Ncm.ApplicationId applicationId, ulong saveDataOwnerId, ushort index,
long dataSize, long journalSize, bool allowExisting)
{
requiredSize = default;

View File

@ -1,8 +1,9 @@
using LibHac.Common;
using System;
using LibHac.Common;
namespace LibHac.Fs
{
internal static class CommonMountNames
internal static class CommonPaths
{
public const char ReservedMountNamePrefixCharacter = '@';
@ -21,5 +22,11 @@ namespace LibHac.Fs
public const char GameCardFileSystemMountNameUpdateSuffix = 'U';
public const char GameCardFileSystemMountNameNormalSuffix = 'N';
public const char GameCardFileSystemMountNameSecureSuffix = 'S';
public static ReadOnlySpan<byte> SdCardNintendoRootDirectoryName => // Nintendo
new[]
{
(byte) 'N', (byte) 'i', (byte) 'n', (byte) 't', (byte) 'e', (byte) 'n', (byte) 'd', (byte) 'o'
};
}
}

View File

@ -11,7 +11,7 @@ namespace LibHac.Fs
{
private readonly Key128 Key;
public ReadOnlySpan<byte> Value => SpanHelpers.AsByteSpan(ref this);
public readonly ReadOnlySpan<byte> Value => SpanHelpers.AsReadOnlyByteSpan(in this);
public EncryptionSeed(ReadOnlySpan<byte> bytes)
{

View File

@ -6,35 +6,17 @@ namespace LibHac.Fs
public class FileStorageBasedFileSystem : FileStorage2
{
// ReSharper disable once UnusedAutoPropertyAccessor.Local
// FS keeps a shared pointer to the base filesystem
private IFileSystem BaseFileSystem { get; set; }
private ReferenceCountedDisposable<IFileSystem> BaseFileSystem { get; set; }
private IFile BaseFile { get; set; }
private FileStorageBasedFileSystem()
public FileStorageBasedFileSystem()
{
FileSize = SizeNotInitialized;
}
public static Result CreateNew(out FileStorageBasedFileSystem created, IFileSystem baseFileSystem, U8Span path,
OpenMode mode)
public Result Initialize(ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path, OpenMode mode)
{
var obj = new FileStorageBasedFileSystem();
Result rc = obj.Initialize(baseFileSystem, path, mode);
if (rc.IsSuccess())
{
created = obj;
return Result.Success;
}
obj.Dispose();
created = default;
return rc;
}
private Result Initialize(IFileSystem baseFileSystem, U8Span path, OpenMode mode)
{
Result rc = baseFileSystem.OpenFile(out IFile file, path, mode);
Result rc = baseFileSystem.Target.OpenFile(out IFile file, path, mode);
if (rc.IsFailure()) return rc;
SetFile(file);
@ -49,6 +31,7 @@ namespace LibHac.Fs
if (disposing)
{
BaseFile?.Dispose();
BaseFileSystem?.Dispose();
}
base.Dispose(disposing);

View File

@ -3,6 +3,7 @@ using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Fs.Accessors;
using LibHac.FsSrv;
using LibHac.Sf;
namespace LibHac.Fs
{
@ -187,7 +188,7 @@ namespace LibHac.Fs
string logString = AccessLogHelpers.BuildDefaultLogLine(result, startTime, endTime, handleId, message, caller);
IFileSystemProxy fsProxy = GetFileSystemProxyServiceObject();
fsProxy.OutputAccessLogToSdCard(logString.ToU8Span());
fsProxy.OutputAccessLogToSdCard(new InBuffer(logString.ToU8Span())).IgnoreResult();
}
}

View File

@ -9,6 +9,7 @@ using LibHac.FsSrv;
using LibHac.FsSrv.Sf;
using LibHac.FsSystem;
using LibHac.Util;
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
namespace LibHac.Fs
{
@ -134,7 +135,13 @@ namespace LibHac.Fs
public Result Register(U8Span mountName, IFileSystem fileSystem, ICommonMountNameGenerator nameGenerator)
{
var accessor = new FileSystemAccessor(mountName.ToString(), fileSystem, this, nameGenerator);
return Register(mountName, null, fileSystem, nameGenerator);
}
public Result Register(U8Span mountName, IMultiCommitTarget multiCommitTarget, IFileSystem fileSystem,
ICommonMountNameGenerator nameGenerator)
{
var accessor = new FileSystemAccessor(mountName, multiCommitTarget, fileSystem, this, nameGenerator);
Result rc = MountTable.Mount(accessor);
if (rc.IsFailure()) return rc;
@ -173,8 +180,8 @@ namespace LibHac.Fs
if (path.IsNull())
return ResultFs.NullptrArgument.Log();
int hostMountNameLen = StringUtils.GetLength(CommonMountNames.HostRootFileSystemMountName);
if (StringUtils.Compare(path, CommonMountNames.HostRootFileSystemMountName, hostMountNameLen) == 0)
int hostMountNameLen = StringUtils.GetLength(CommonPaths.HostRootFileSystemMountName);
if (StringUtils.Compare(path, CommonPaths.HostRootFileSystemMountName, hostMountNameLen) == 0)
{
return ResultFs.NotMounted.Log();
}
@ -195,7 +202,7 @@ namespace LibHac.Fs
if (PathUtility.IsWindowsDrive(path) || PathUtility.IsUnc(path))
{
StringUtils.Copy(mountName.Name, CommonMountNames.HostRootFileSystemMountName);
StringUtils.Copy(mountName.Name, CommonPaths.HostRootFileSystemMountName);
mountName.Name[PathTools.MountNameLengthMax] = StringTraits.NullTerminator;
subPath = path;

View File

@ -0,0 +1,15 @@
using System.Runtime.InteropServices;
using LibHac.Fat;
namespace LibHac.Fs
{
[StructLayout(LayoutKind.Explicit, Size = 0x80)]
public struct FileSystemProxyErrorInfo
{
[FieldOffset(0x00)] public int RomFsRemountForDataCorruptionCount;
[FieldOffset(0x04)] public int RomFsUnrecoverableDataCorruptionByRemountCount;
[FieldOffset(0x08)] public FatError FatError;
[FieldOffset(0x28)] public int RomFsRecoveredByInvalidateCacheCount;
[FieldOffset(0x2C)] public int SaveDataIndexCount;
}
}

View File

@ -1,9 +1,13 @@
namespace LibHac.Fs
using System.Runtime.InteropServices;
namespace LibHac.Fs
{
[StructLayout(LayoutKind.Sequential, Size = 0x20)]
public struct FileTimeStampRaw
{
public long Created;
public long Accessed;
public long Modified;
public bool IsLocalTime;
}
}

View File

@ -96,10 +96,11 @@ namespace LibHac.Fs
public enum SaveDataState : byte
{
Normal = 0,
Creating = 1,
Processing = 1,
State2 = 2,
MarkedForDeletion = 3,
Extending = 4
Extending = 4,
ImportSuspended = 5
}
public enum ImageDirectoryId
@ -165,7 +166,8 @@ namespace LibHac.Fs
KeepAfterResettingSystemSaveData = 1 << 0,
KeepAfterRefurbishment = 1 << 1,
KeepAfterResettingSystemSaveDataWithoutUserSaveData = 1 << 2,
NeedsSecureDelete = 1 << 3
NeedsSecureDelete = 1 << 3,
Restore = 1 << 4
}
public enum SdmmcPort
@ -188,4 +190,11 @@ namespace LibHac.Fs
None = 0,
PseudoCaseSensitive = 1
}
public enum SimulatingDeviceDetectionMode
{
None = 0,
Inserted = 1,
NotInserted = 2
}
}

View File

@ -1,4 +1,5 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.FsSystem;
@ -36,6 +37,31 @@ namespace LibHac.Fs.Fsa
return DoCreateFile(path, size, option);
}
/// <summary>
/// Creates or overwrites a file at the specified path.
/// </summary>
/// <param name="path">The full path of the file to create.</param>
/// <param name="size">The initial size of the created file.
/// Should usually be <see cref="CreateFileOptions.None"/></param>
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
/// <remarks>
/// The following <see cref="Result"/> codes may be returned under certain conditions:
///
/// The parent directory of the specified path does not exist: <see cref="ResultFs.PathNotFound"/>
/// Specified path already exists as either a file or directory: <see cref="ResultFs.PathAlreadyExists"/>
/// Insufficient free space to create the file: <see cref="ResultFs.InsufficientFreeSpace"/>
/// </remarks>
public Result CreateFile(U8Span path, long size)
{
if (path.IsNull())
return ResultFs.NullptrArgument.Log();
if (size < 0)
return ResultFs.OutOfRange.Log();
return DoCreateFile(path, size, CreateFileOptions.None);
}
/// <summary>
/// Deletes the specified file.
/// </summary>
@ -167,7 +193,7 @@ namespace LibHac.Fs.Fsa
/// <paramref name="oldPath"/> does not exist or is a file: <see cref="ResultFs.PathNotFound"/>
/// <paramref name="newPath"/>'s parent directory does not exist: <see cref="ResultFs.PathNotFound"/>
/// <paramref name="newPath"/> already exists as either a file or directory: <see cref="ResultFs.PathAlreadyExists"/>
/// Either <paramref name="oldPath"/> or <paramref name="newPath"/> is a subpath of the other: <see cref="ResultFs.DestinationIsSubPathOfSource"/>
/// Either <paramref name="oldPath"/> or <paramref name="newPath"/> is a subpath of the other: <see cref="ResultFs.DirectoryNotRenamable"/>
/// </remarks>
public Result RenameDirectory(U8Span oldPath, U8Span newPath)
{
@ -391,7 +417,7 @@ namespace LibHac.Fs.Fsa
protected virtual Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, U8Span path)
{
timeStamp = default;
Unsafe.SkipInit(out timeStamp);
return ResultFs.NotImplemented.Log();
}

View File

@ -0,0 +1,9 @@
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
namespace LibHac.Fs.Fsa
{
public interface IMultiCommitTarget
{
ReferenceCountedDisposable<IFileSystemSf> GetMultiCommitTarget();
}
}

View File

@ -0,0 +1,43 @@
using System;
using System.Runtime.InteropServices;
using LibHac.Sf;
using IDirectory = LibHac.Fs.Fsa.IDirectory;
using IDirectorySf = LibHac.FsSrv.Sf.IDirectory;
namespace LibHac.Fs.Impl
{
/// <summary>
/// An adapter for using an <see cref="IDirectorySf"/> service object as an <see cref="IDirectory"/>. Used
/// when receiving a Horizon IPC directory object so it can be used as an <see cref="IDirectory"/> locally.
/// </summary>
internal class DirectoryServiceObjectAdapter : IDirectory
{
private ReferenceCountedDisposable<IDirectorySf> BaseDirectory { get; }
public DirectoryServiceObjectAdapter(ReferenceCountedDisposable<IDirectorySf> baseDirectory)
{
BaseDirectory = baseDirectory.AddReference();
}
protected override Result DoRead(out long entriesRead, Span<DirectoryEntry> entryBuffer)
{
Span<byte> buffer = MemoryMarshal.Cast<DirectoryEntry, byte>(entryBuffer);
return BaseDirectory.Target.Read(out entriesRead, new OutBuffer(buffer));
}
protected override Result DoGetEntryCount(out long entryCount)
{
return BaseDirectory.Target.GetEntryCount(out entryCount);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
BaseDirectory?.Dispose();
}
base.Dispose(disposing);
}
}
}

View File

@ -0,0 +1,75 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using IFile = LibHac.Fs.Fsa.IFile;
using IFileSf = LibHac.FsSrv.Sf.IFile;
namespace LibHac.Fs.Impl
{
/// <summary>
/// An adapter for using an <see cref="IFileSf"/> service object as an <see cref="IFile"/>. Used
/// when receiving a Horizon IPC file object so it can be used as an <see cref="IFile"/> locally.
/// </summary>
internal class FileServiceObjectAdapter : IFile
{
private ReferenceCountedDisposable<IFileSf> BaseFile { get; }
public FileServiceObjectAdapter(ReferenceCountedDisposable<IFileSf> baseFile)
{
BaseFile = baseFile.AddReference();
}
protected override Result DoRead(out long bytesRead, long offset, Span<byte> destination, in ReadOption option)
{
return BaseFile.Target.Read(out bytesRead, offset, destination, option);
}
protected override Result DoWrite(long offset, ReadOnlySpan<byte> source, in WriteOption option)
{
return BaseFile.Target.Write(offset, source, option);
}
protected override Result DoFlush()
{
return BaseFile.Target.Flush();
}
protected override Result DoSetSize(long size)
{
return BaseFile.Target.SetSize(size);
}
protected override Result DoGetSize(out long size)
{
return BaseFile.Target.GetSize(out size);
}
protected override Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan<byte> inBuffer)
{
switch (operationId)
{
case OperationId.InvalidateCache:
return BaseFile.Target.OperateRange(out _, (int)OperationId.InvalidateCache, offset, size);
case OperationId.QueryRange:
if (outBuffer.Length != Unsafe.SizeOf<QueryRangeInfo>())
return ResultFs.InvalidSize.Log();
ref QueryRangeInfo info = ref SpanHelpers.AsStruct<QueryRangeInfo>(outBuffer);
return BaseFile.Target.OperateRange(out info, (int)OperationId.QueryRange, offset, size);
default:
return ResultFs.UnsupportedOperationInFileServiceObjectAdapterA.Log();
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
BaseFile?.Dispose();
}
base.Dispose(disposing);
}
}
}

View File

@ -0,0 +1,231 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.FsSrv.Sf;
using LibHac.Util;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
using IFileSf = LibHac.FsSrv.Sf.IFile;
using IDirectorySf = LibHac.FsSrv.Sf.IDirectory;
namespace LibHac.Fs.Impl
{
/// <summary>
/// An adapter for using an <see cref="IFileSystemSf"/> service object as an <see cref="Fsa.IFileSystem"/>. Used
/// when receiving a Horizon IPC file system object so it can be used as an <see cref="Fsa.IFileSystem"/> locally.
/// </summary>
internal class FileSystemServiceObjectAdapter : Fsa.IFileSystem, IMultiCommitTarget
{
private ReferenceCountedDisposable<IFileSystemSf> BaseFs { get; }
public FileSystemServiceObjectAdapter(ReferenceCountedDisposable<IFileSystemSf> baseFileSystem)
{
BaseFs = baseFileSystem.AddReference();
}
protected override Result DoCreateFile(U8Span path, long size, CreateFileOptions option)
{
Result rc = GetPathForServiceObject(out Path sfPath, path);
if (rc.IsFailure()) return rc;
return BaseFs.Target.CreateFile(in sfPath, size, (int)option);
}
protected override Result DoDeleteFile(U8Span path)
{
Result rc = GetPathForServiceObject(out Path sfPath, path);
if (rc.IsFailure()) return rc;
return BaseFs.Target.DeleteFile(in sfPath);
}
protected override Result DoCreateDirectory(U8Span path)
{
Result rc = GetPathForServiceObject(out Path sfPath, path);
if (rc.IsFailure()) return rc;
return BaseFs.Target.DeleteFile(in sfPath);
}
protected override Result DoDeleteDirectory(U8Span path)
{
Result rc = GetPathForServiceObject(out Path sfPath, path);
if (rc.IsFailure()) return rc;
return BaseFs.Target.DeleteDirectory(in sfPath);
}
protected override Result DoDeleteDirectoryRecursively(U8Span path)
{
Result rc = GetPathForServiceObject(out Path sfPath, path);
if (rc.IsFailure()) return rc;
return BaseFs.Target.DeleteDirectoryRecursively(in sfPath);
}
protected override Result DoCleanDirectoryRecursively(U8Span path)
{
Result rc = GetPathForServiceObject(out Path sfPath, path);
if (rc.IsFailure()) return rc;
return BaseFs.Target.CleanDirectoryRecursively(in sfPath);
}
protected override Result DoRenameFile(U8Span oldPath, U8Span newPath)
{
Result rc = GetPathForServiceObject(out Path oldSfPath, oldPath);
if (rc.IsFailure()) return rc;
rc = GetPathForServiceObject(out Path newSfPath, newPath);
if (rc.IsFailure()) return rc;
return BaseFs.Target.RenameFile(in oldSfPath, in newSfPath);
}
protected override Result DoRenameDirectory(U8Span oldPath, U8Span newPath)
{
Result rc = GetPathForServiceObject(out Path oldSfPath, oldPath);
if (rc.IsFailure()) return rc;
rc = GetPathForServiceObject(out Path newSfPath, newPath);
if (rc.IsFailure()) return rc;
return BaseFs.Target.RenameDirectory(in oldSfPath, in newSfPath);
}
protected override Result DoGetEntryType(out DirectoryEntryType entryType, U8Span path)
{
Unsafe.SkipInit(out entryType);
Result rc = GetPathForServiceObject(out Path sfPath, path);
if (rc.IsFailure()) return rc;
ref uint sfEntryType = ref Unsafe.As<DirectoryEntryType, uint>(ref entryType);
return BaseFs.Target.GetEntryType(out sfEntryType, in sfPath);
}
protected override Result DoGetFreeSpaceSize(out long freeSpace, U8Span path)
{
Unsafe.SkipInit(out freeSpace);
Result rc = GetPathForServiceObject(out Path sfPath, path);
if (rc.IsFailure()) return rc;
return BaseFs.Target.GetFreeSpaceSize(out freeSpace, in sfPath);
}
protected override Result DoGetTotalSpaceSize(out long totalSpace, U8Span path)
{
Unsafe.SkipInit(out totalSpace);
Result rc = GetPathForServiceObject(out Path sfPath, path);
if (rc.IsFailure()) return rc;
return BaseFs.Target.GetTotalSpaceSize(out totalSpace, in sfPath);
}
protected override Result DoOpenFile(out Fsa.IFile file, U8Span path, OpenMode mode)
{
file = default;
Result rc = GetPathForServiceObject(out Path sfPath, path);
if (rc.IsFailure()) return rc;
ReferenceCountedDisposable<IFileSf> sfFile = null;
try
{
rc = BaseFs.Target.OpenFile(out sfFile, in sfPath, (uint)mode);
if (rc.IsFailure()) return rc;
file = new FileServiceObjectAdapter(sfFile);
return Result.Success;
}
finally
{
sfFile?.Dispose();
}
}
protected override Result DoOpenDirectory(out Fsa.IDirectory directory, U8Span path, OpenDirectoryMode mode)
{
directory = default;
Result rc = GetPathForServiceObject(out Path sfPath, path);
if (rc.IsFailure()) return rc;
ReferenceCountedDisposable<IDirectorySf> sfDir = null;
try
{
rc = BaseFs.Target.OpenDirectory(out sfDir, in sfPath, (uint)mode);
if (rc.IsFailure()) return rc;
directory = new DirectoryServiceObjectAdapter(sfDir);
return Result.Success;
}
finally
{
sfDir?.Dispose();
}
}
protected override Result DoCommit()
{
return BaseFs.Target.Commit();
}
protected override Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, U8Span path)
{
Unsafe.SkipInit(out timeStamp);
Result rc = GetPathForServiceObject(out Path sfPath, path);
if (rc.IsFailure()) return rc;
return BaseFs.Target.GetFileTimeStampRaw(out timeStamp, in sfPath);
}
protected override Result DoQueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId,
U8Span path)
{
Result rc = GetPathForServiceObject(out Path sfPath, path);
if (rc.IsFailure()) return rc;
return BaseFs.Target.QueryEntry(outBuffer, inBuffer, (int)queryId, in sfPath);
}
public ReferenceCountedDisposable<IFileSystemSf> GetMultiCommitTarget()
{
return BaseFs.AddReference();
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
BaseFs?.Dispose();
}
base.Dispose(disposing);
}
private Result GetPathForServiceObject(out Path sfPath, U8Span path)
{
// This is the function used to create Sf.Path structs. Get an unsafe byte span for init only.
Unsafe.SkipInit(out sfPath);
Span<byte> outPath = SpanHelpers.AsByteSpan(ref sfPath);
// Copy and null terminate
StringUtils.Copy(outPath, path);
outPath[Unsafe.SizeOf<Path>() - 1] = StringTraits.NullTerminator;
// Replace directory separators
PathUtility.Replace(outPath, StringTraits.AltDirectorySeparator, StringTraits.DirectorySeparator);
// Get lengths
int windowsSkipLength = PathUtility.GetWindowsPathSkipLength(path);
var nonWindowsPath = new U8Span(sfPath.Str.Slice(windowsSkipLength));
int maxLength = PathTool.EntryNameLengthMax - windowsSkipLength;
return PathUtility.VerifyPath(nonWindowsPath, maxLength, maxLength);
}
}
}

View File

@ -0,0 +1,53 @@
using System;
using System.Threading;
namespace LibHac.Fs.Impl
{
/// <summary>
/// A wrapper for handling write access to a reader-writer lock.
/// </summary>
public class UniqueLock : IDisposable
{
private ReaderWriterLockSlim _lock;
private bool _hasLock;
public UniqueLock(ReaderWriterLockSlim readerWriterLock)
{
_lock = readerWriterLock;
readerWriterLock.EnterWriteLock();
_hasLock = true;
}
public void Dispose()
{
if (_hasLock)
{
_lock.ExitWriteLock();
}
}
}
/// <summary>
/// A wrapper for handling read access to a reader-writer lock.
/// </summary>
public class SharedLock : IDisposable
{
private ReaderWriterLockSlim _lock;
private bool _hasLock;
public SharedLock(ReaderWriterLockSlim readerWriterLock)
{
_lock = readerWriterLock;
readerWriterLock.EnterReadLock();
_hasLock = true;
}
public void Dispose()
{
if (_hasLock)
{
_lock.EnterReadLock();
}
}
}
}

View File

@ -0,0 +1,73 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using IStorageSf = LibHac.FsSrv.Sf.IStorage;
namespace LibHac.Fs.Impl
{
/// <summary>
/// An adapter for using an <see cref="IStorageSf"/> service object as an <see cref="IStorage"/>. Used
/// when receiving a Horizon IPC storage object so it can be used as an <see cref="IStorage"/> locally.
/// </summary>
internal class StorageServiceObjectAdapter : IStorage
{
private ReferenceCountedDisposable<IStorageSf> BaseStorage { get; }
public StorageServiceObjectAdapter(ReferenceCountedDisposable<IStorageSf> baseStorage)
{
BaseStorage = baseStorage.AddReference();
}
protected override Result DoRead(long offset, Span<byte> destination)
{
return BaseStorage.Target.Read(offset, destination);
}
protected override Result DoWrite(long offset, ReadOnlySpan<byte> source)
{
return BaseStorage.Target.Write(offset, source);
}
protected override Result DoFlush()
{
return BaseStorage.Target.Flush();
}
protected override Result DoSetSize(long size)
{
return BaseStorage.Target.SetSize(size);
}
protected override Result DoGetSize(out long size)
{
return BaseStorage.Target.GetSize(out size);
}
protected override Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan<byte> inBuffer)
{
switch (operationId)
{
case OperationId.InvalidateCache:
return BaseStorage.Target.OperateRange(out _, (int)OperationId.InvalidateCache, offset, size);
case OperationId.QueryRange:
if (outBuffer.Length != Unsafe.SizeOf<QueryRangeInfo>())
return ResultFs.InvalidSize.Log();
ref QueryRangeInfo info = ref SpanHelpers.AsStruct<QueryRangeInfo>(outBuffer);
return BaseStorage.Target.OperateRange(out info, (int)OperationId.QueryRange, offset, size);
default:
return ResultFs.UnsupportedOperationInFileServiceObjectAdapterA.Log();
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
BaseStorage?.Dispose();
}
base.Dispose(disposing);
}
}
}

View File

@ -1,5 +1,5 @@
using LibHac.Common;
using static LibHac.Fs.CommonMountNames;
using static LibHac.Fs.CommonPaths;
namespace LibHac.Fs
{

View File

@ -19,6 +19,7 @@ namespace LibHac.Fs
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsWindowsDriveCharacter(byte c)
{
// Mask lowercase letters to uppercase and check if it's in range
return (0b1101_1111 & c) - 'A' <= 'Z' - 'A';
//return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z';
}

View File

@ -1,11 +1,31 @@
using System.Runtime.InteropServices;
using System;
using System.Runtime.InteropServices;
namespace LibHac.Fs
{
[StructLayout(LayoutKind.Sequential, Size = 0x40)]
public struct QueryRangeInfo
{
public uint AesCtrKeyType;
public uint SpeedEmulationType;
public int AesCtrKeyType;
public int SpeedEmulationType;
public void Clear()
{
this = default;
}
public void Merge(in QueryRangeInfo other)
{
AesCtrKeyType |= other.AesCtrKeyType;
SpeedEmulationType |= other.SpeedEmulationType;
}
[Flags]
public enum AesCtrKeyTypeFlag
{
InternalKeyForSoftwareAes = 1 << 0,
InternalKeyForHardwareAes = 1 << 1,
ExternalKeyForHardwareAes = 1 << 2
}
}
}

View File

@ -96,8 +96,16 @@ namespace LibHac.Fs
/// <summary>Error code: 2002-3200; Range: 3200-3499; Inner value: 0x190002</summary>
public static Result.Base AllocationMemoryFailed { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 3200, 3499); }
/// <summary>In ParseNsp allocating FileStorageBasedFileSystem<br/>Error code: 2002-3256; Inner value: 0x197002</summary>
public static Result.Base AllocationFailureInNcaFileSystemServiceImplA => new Result.Base(ModuleFs, 3256);
/// <summary>In ParseNca allocating FileStorageBasedFileSystem<br/>Error code: 2002-3257; Inner value: 0x197202</summary>
public static Result.Base AllocationFailureInNcaFileSystemServiceImplB => new Result.Base(ModuleFs, 3257);
/// <summary>In RegisterProgram allocating ProgramInfoNode<br/>Error code: 2002-3258; Inner value: 0x197402</summary>
public static Result.Base AllocationFailureInProgramRegistryManagerA => new Result.Base(ModuleFs, 3258);
/// <summary>In Initialize allocating ProgramInfoNode<br/>Error code: 2002-3264; Inner value: 0x198002</summary>
public static Result.Base AllocationFailureFatFileSystemA => new Result.Base(ModuleFs, 3264);
/// <summary>In Create allocating PartitionFileSystemCore<br/>Error code: 2002-3280; Inner value: 0x19a002</summary>
public static Result.Base AllocationFailureInPartitionFileSystemCreatorA => new Result.Base(ModuleFs, 3280);
/// <summary>In Initialize allocating FileStorage<br/>Error code: 2002-3312; Inner value: 0x19e002</summary>
public static Result.Base AllocationFailureInAesXtsFileA => new Result.Base(ModuleFs, 3312);
/// <summary>In Initialize allocating AesXtsStorage<br/>Error code: 2002-3313; Inner value: 0x19e202</summary>
@ -106,8 +114,34 @@ namespace LibHac.Fs
public static Result.Base AllocationFailureInAesXtsFileC => new Result.Base(ModuleFs, 3314);
/// <summary>In Initialize allocating StorageFile<br/>Error code: 2002-3315; Inner value: 0x19e602</summary>
public static Result.Base AllocationFailureInAesXtsFileD => new Result.Base(ModuleFs, 3315);
/// <summary>In Initialize allocating SubStorage<br/>Error code: 2002-3383; Inner value: 0x1a6e02</summary>
/// <summary>In Initialize allocating PartitionFileSystemMetaCore<br/>Error code: 2002-3347; Inner value: 0x1a2602</summary>
public static Result.Base AllocationFailureInPartitionFileSystemA => new Result.Base(ModuleFs, 3347);
/// <summary>In DoOpenFile allocating PartitionFile<br/>Error code: 2002-3348; Inner value: 0x1a2802</summary>
public static Result.Base AllocationFailureInPartitionFileSystemB => new Result.Base(ModuleFs, 3348);
/// <summary>In DoOpenDirectory allocating PartitionDirectory<br/>Error code: 2002-3349; Inner value: 0x1a2a02</summary>
public static Result.Base AllocationFailureInPartitionFileSystemC => new Result.Base(ModuleFs, 3349);
/// <summary>In Initialize allocating metadata buffer<br/>Error code: 2002-3350; Inner value: 0x1a2c02</summary>
public static Result.Base AllocationFailureInPartitionFileSystemMetaA => new Result.Base(ModuleFs, 3350);
/// <summary>In Sha256 Initialize allocating metadata buffer<br/>Error code: 2002-3351; Inner value: 0x1a2e02</summary>
public static Result.Base AllocationFailureInPartitionFileSystemMetaB => new Result.Base(ModuleFs, 3351);
/// <summary>In Initialize allocating RootPathBuffer<br/>Error code: 2002-3355; Inner value: 0x1a3602</summary>
public static Result.Base AllocationFailureInSubdirectoryFileSystemA => new Result.Base(ModuleFs, 3355);
/// <summary>In Initialize<br/>Error code: 2002-3383; Inner value: 0x1a6e02</summary>
public static Result.Base AllocationFailureInAesXtsFileE => new Result.Base(ModuleFs, 3383);
/// <summary>In Create allocating AesXtsFileSystem<br/>Error code: 2002-3394; Inner value: 0x1a8402</summary>
public static Result.Base AllocationFailureInEncryptedFileSystemCreatorA => new Result.Base(ModuleFs, 3394);
/// <summary> In OpenFile or OpenDirectory<br/>Error code: 2002-3407; Inner value: 0x1a9e02</summary>
public static Result.Base AllocationFailureInFileSystemInterfaceAdapter => new Result.Base(ModuleFs, 3407);
/// <summary>Error code: 2002-3420; Inner value: 0x1ab802</summary>
public static Result.Base AllocationFailureInNew => new Result.Base(ModuleFs, 3420);
/// <summary>Error code: 2002-3421; Inner value: 0x1aba02</summary>
public static Result.Base AllocationFailureInCreateShared => new Result.Base(ModuleFs, 3421);
/// <summary>Error code: 2002-3422; Inner value: 0x1abc02</summary>
public static Result.Base AllocationFailureInMakeUnique => new Result.Base(ModuleFs, 3422);
/// <summary>Error code: 2002-3423; Inner value: 0x1abe02</summary>
public static Result.Base AllocationFailureInAllocateShared => new Result.Base(ModuleFs, 3423);
/// <summary>Error code: 2002-3424; Inner value: 0x1ac002</summary>
public static Result.Base AllocationFailurePooledBufferNotEnoughSize => new Result.Base(ModuleFs, 3424);
/// <summary>Error code: 2002-3500; Range: 3500-3999; Inner value: 0x1b5802</summary>
public static Result.Base MmcAccessFailed { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 3500, 3999); }
@ -205,9 +239,9 @@ namespace LibHac.Fs
/// <summary>Error code: 2002-4501; Range: 4501-4599; Inner value: 0x232a02</summary>
public static Result.Base NcaCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 4501, 4599); }
/// <summary>Error code: 2002-4512; Inner value: 0x234002</summary>
public static Result.Base InvalidNcaFsType => new Result.Base(ModuleFs, 4512);
public static Result.Base InvalidNcaFileSystemType => new Result.Base(ModuleFs, 4512);
/// <summary>Error code: 2002-4527; Inner value: 0x235e02</summary>
public static Result.Base InvalidNcaProgramId => new Result.Base(ModuleFs, 4527);
public static Result.Base InvalidNcaId => new Result.Base(ModuleFs, 4527);
/// <summary>Error code: 2002-4601; Range: 4601-4639; Inner value: 0x23f202</summary>
public static Result.Base IntegrityVerificationStorageCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 4601, 4639); }
@ -305,6 +339,8 @@ namespace LibHac.Fs
/// <summary>Error code: 2002-5000; Range: 5000-5999; Inner value: 0x271002</summary>
public static Result.Base Unexpected { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 5000, 5999); }
/// <summary>Error code: 2002-5121; Inner value: 0x280202</summary>
public static Result.Base UnexpectedFatFileSystemSectorCount => new Result.Base(ModuleFs, 5121);
/// <summary>Error code: 2002-5307; Inner value: 0x297602</summary>
public static Result.Base UnexpectedErrorInHostFileFlush => new Result.Base(ModuleFs, 5307);
/// <summary>Error code: 2002-5308; Inner value: 0x297802</summary>
@ -336,7 +372,7 @@ namespace LibHac.Fs
/// <summary>Error code: 2002-6031; Inner value: 0x2f1e02</summary>
public static Result.Base DirectoryNotDeletable => new Result.Base(ModuleFs, 6031);
/// <summary>Error code: 2002-6032; Inner value: 0x2f2002</summary>
public static Result.Base DestinationIsSubPathOfSource => new Result.Base(ModuleFs, 6032);
public static Result.Base DirectoryNotRenamable => new Result.Base(ModuleFs, 6032);
/// <summary>Error code: 2002-6033; Inner value: 0x2f2202</summary>
public static Result.Base PathNotFoundInSaveDataFileTable => new Result.Base(ModuleFs, 6033);
/// <summary>Error code: 2002-6034; Inner value: 0x2f2402</summary>
@ -387,6 +423,8 @@ namespace LibHac.Fs
/// <summary>Error code: 2002-6300; Range: 6300-6399; Inner value: 0x313802</summary>
public static Result.Base UnsupportedOperation { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6300, 6399); }
/// <summary>Error code: 2002-6301; Inner value: 0x313a02</summary>
public static Result.Base UnsupportedCommitTarget => new Result.Base(ModuleFs, 6301);
/// <summary>Attempted to resize a non-resizable SubStorage.<br/>Error code: 2002-6302; Inner value: 0x313c02</summary>
public static Result.Base UnsupportedOperationInSubStorageSetSize => new Result.Base(ModuleFs, 6302);
/// <summary>Attempted to resize a SubStorage that wasn't located at the end of the base storage.<br/>Error code: 2002-6303; Inner value: 0x313e02</summary>
@ -409,6 +447,8 @@ namespace LibHac.Fs
public static Result.Base UnsupportedOperationInRoGameCardStorageSetSize => new Result.Base(ModuleFs, 6351);
/// <summary>Error code: 2002-6359; Inner value: 0x31ae02</summary>
public static Result.Base UnsupportedOperationInConcatFsQueryEntry => new Result.Base(ModuleFs, 6359);
/// <summary>Called OperateRange with an invalid operation ID.<br/>Error code: 2002-6362; Inner value: 0x31b402</summary>
public static Result.Base UnsupportedOperationInFileServiceObjectAdapterA => new Result.Base(ModuleFs, 6362);
/// <summary>Error code: 2002-6364; Inner value: 0x31b802</summary>
public static Result.Base UnsupportedOperationModifyRomFsFileSystem => new Result.Base(ModuleFs, 6364);
/// <summary>Called RomFsFileSystem::CommitProvisionally.<br/>Error code: 2002-6365; Inner value: 0x31ba02</summary>
@ -443,14 +483,24 @@ namespace LibHac.Fs
public static Result.Base ExternalKeyAlreadyRegistered => new Result.Base(ModuleFs, 6452);
/// <summary>Error code: 2002-6454; Inner value: 0x326c02</summary>
public static Result.Base WriteStateUnflushed => new Result.Base(ModuleFs, 6454);
/// <summary>Error code: 2002-6456; Inner value: 0x327002</summary>
public static Result.Base DirectoryNotClosed => new Result.Base(ModuleFs, 6456);
/// <summary>Error code: 2002-6457; Inner value: 0x327202</summary>
public static Result.Base WriteModeFileNotClosed => new Result.Base(ModuleFs, 6457);
/// <summary>Error code: 2002-6458; Inner value: 0x327402</summary>
public static Result.Base AllocatorAlreadyRegistered => new Result.Base(ModuleFs, 6458);
/// <summary>Error code: 2002-6459; Inner value: 0x327602</summary>
public static Result.Base DefaultAllocatorUsed => new Result.Base(ModuleFs, 6459);
/// <summary>Error code: 2002-6461; Inner value: 0x327a02</summary>
public static Result.Base AllocatorAlignmentViolation => new Result.Base(ModuleFs, 6461);
/// <summary>The provided file system has already been added to the multi-commit manager.<br/>Error code: 2002-6463; Inner value: 0x327e02</summary>
public static Result.Base MultiCommitFileSystemAlreadyAdded => new Result.Base(ModuleFs, 6463);
/// <summary>Error code: 2002-6465; Inner value: 0x328202</summary>
public static Result.Base UserNotExist => new Result.Base(ModuleFs, 6465);
/// <summary>Error code: 2002-6466; Inner value: 0x328402</summary>
public static Result.Base DefaultGlobalFileDataCacheEnabled => new Result.Base(ModuleFs, 6466);
/// <summary>Error code: 2002-6467; Inner value: 0x328602</summary>
public static Result.Base SaveDataRootPathUnavailable => new Result.Base(ModuleFs, 6467);
/// <summary>Error code: 2002-6600; Range: 6600-6699; Inner value: 0x339002</summary>
public static Result.Base EntryNotFound { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6600, 6699); }

View File

@ -1,7 +1,9 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Ncm;
using LibHac.Util;
namespace LibHac.Fs
{
@ -13,17 +15,17 @@ namespace LibHac.Fs
[FieldOffset(0x18)] public ulong StaticSaveDataId;
[FieldOffset(0x20)] public SaveDataType Type;
[FieldOffset(0x21)] public SaveDataRank Rank;
[FieldOffset(0x22)] public short Index;
[FieldOffset(0x22)] public ushort Index;
public SaveDataAttribute(ProgramId programId, SaveDataType type, UserId userId, ulong saveDataId) : this(
programId, type, userId, saveDataId, 0, SaveDataRank.Primary)
{ }
public SaveDataAttribute(ProgramId programId, SaveDataType type, UserId userId, ulong saveDataId,
short index) : this(programId, type, userId, saveDataId, index, SaveDataRank.Primary)
ushort index) : this(programId, type, userId, saveDataId, index, SaveDataRank.Primary)
{ }
public SaveDataAttribute(ProgramId programId, SaveDataType type, UserId userId, ulong saveDataId, short index,
public SaveDataAttribute(ProgramId programId, SaveDataType type, UserId userId, ulong saveDataId, ushort index,
SaveDataRank rank)
{
ProgramId = programId;
@ -34,6 +36,38 @@ namespace LibHac.Fs
Rank = rank;
}
public static Result Make(out SaveDataAttribute attribute, ProgramId programId, SaveDataType type,
UserId userId, ulong staticSaveDataId)
{
return Make(out attribute, programId, type, userId, staticSaveDataId, 0, SaveDataRank.Primary);
}
public static Result Make(out SaveDataAttribute attribute, ProgramId programId, SaveDataType type,
UserId userId, ulong staticSaveDataId, ushort index)
{
return Make(out attribute, programId, type, userId, staticSaveDataId, index, SaveDataRank.Primary);
}
public static Result Make(out SaveDataAttribute attribute, ProgramId programId, SaveDataType type,
UserId userId, ulong staticSaveDataId, ushort index, SaveDataRank rank)
{
Unsafe.SkipInit(out attribute);
SaveDataAttribute tempAttribute = default;
tempAttribute.ProgramId = programId;
tempAttribute.Type = type;
tempAttribute.UserId = userId;
tempAttribute.StaticSaveDataId = staticSaveDataId;
tempAttribute.Index = index;
tempAttribute.Rank = rank;
if (!SaveDataTypesValidity.IsValid(in tempAttribute))
return ResultFs.InvalidArgument.Log();
attribute = tempAttribute;
return Result.Success;
}
public override readonly bool Equals(object obj)
{
return obj is SaveDataAttribute attribute && Equals(attribute);
@ -85,40 +119,96 @@ namespace LibHac.Fs
[FieldOffset(0x04)] public bool FilterByIndex;
[FieldOffset(0x05)] public SaveDataRank Rank;
[FieldOffset(0x08)] public ProgramId ProgramId;
[FieldOffset(0x10)] public UserId UserId;
[FieldOffset(0x20)] public ulong SaveDataId;
[FieldOffset(0x28)] public SaveDataType SaveDataType;
[FieldOffset(0x2A)] public short Index;
[FieldOffset(0x08)] public SaveDataAttribute Attribute;
public void SetProgramId(ProgramId value)
{
FilterByProgramId = true;
ProgramId = value;
Attribute.ProgramId = value;
}
public void SetSaveDataType(SaveDataType value)
{
FilterBySaveDataType = true;
SaveDataType = value;
Attribute.Type = value;
}
public void SetUserId(UserId value)
{
FilterByUserId = true;
UserId = value;
Attribute.UserId = value;
}
public void SetSaveDataId(ulong value)
{
FilterBySaveDataId = true;
SaveDataId = value;
Attribute.StaticSaveDataId = value;
}
public void SetIndex(short value)
public void SetIndex(ushort value)
{
FilterByIndex = true;
Index = value;
Attribute.Index = value;
}
public static Result Make(out SaveDataFilter filter, Optional<ulong> programId, Optional<SaveDataType> saveType,
Optional<UserId> userId, Optional<ulong> saveDataId, Optional<ushort> index)
{
return Make(out filter, programId, saveType, userId, saveDataId, index, SaveDataRank.Primary);
}
public static Result Make(out SaveDataFilter filter, Optional<ulong> programId, Optional<SaveDataType> saveType,
Optional<UserId> userId, Optional<ulong> saveDataId, Optional<ushort> index, SaveDataRank rank)
{
Unsafe.SkipInit(out filter);
SaveDataFilter tempFilter = Make(programId, saveType, userId, saveDataId, index, rank);
if (!SaveDataTypesValidity.IsValid(in tempFilter))
return ResultFs.InvalidArgument.Log();
filter = tempFilter;
return Result.Success;
}
public static SaveDataFilter Make(Optional<ulong> programId, Optional<SaveDataType> saveType,
Optional<UserId> userId, Optional<ulong> saveDataId, Optional<ushort> index, SaveDataRank rank)
{
var filter = new SaveDataFilter();
if (programId.HasValue)
{
filter.FilterByProgramId = true;
filter.Attribute.ProgramId = new ProgramId(programId.Value);
}
if (saveType.HasValue)
{
filter.FilterBySaveDataType = true;
filter.Attribute.Type = saveType.Value;
}
if (userId.HasValue)
{
filter.FilterByUserId = true;
filter.Attribute.UserId = userId.Value;
}
if (saveDataId.HasValue)
{
filter.FilterBySaveDataId = true;
filter.Attribute.StaticSaveDataId = saveDataId.Value;
}
if (index.HasValue)
{
filter.FilterByIndex = true;
filter.Attribute.Index = index.Value;
}
filter.Rank = rank;
return filter;
}
}
@ -130,6 +220,7 @@ namespace LibHac.Fs
[FieldOffset(0x00)] private byte _hashStart;
public Span<byte> Hash => SpanHelpers.CreateSpan(ref _hashStart, HashLength);
public ReadOnlySpan<byte> HashRo => SpanHelpers.CreateReadOnlySpan(in _hashStart, HashLength);
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
@ -140,7 +231,7 @@ namespace LibHac.Fs
}
[StructLayout(LayoutKind.Explicit, Size = 0x10)]
public struct SaveMetaCreateInfo
public struct SaveDataMetaInfo
{
[FieldOffset(0)] public int Size;
[FieldOffset(4)] public SaveDataMetaType Type;
@ -151,7 +242,7 @@ namespace LibHac.Fs
{
[FieldOffset(0x00)] public long Size;
[FieldOffset(0x08)] public long JournalSize;
[FieldOffset(0x10)] public ulong BlockSize;
[FieldOffset(0x10)] public long BlockSize;
[FieldOffset(0x18)] public ulong OwnerId;
[FieldOffset(0x20)] public SaveDataFlags Flags;
[FieldOffset(0x24)] public SaveDataSpaceId SpaceId;
@ -168,8 +259,66 @@ namespace LibHac.Fs
[FieldOffset(0x20)] public ulong StaticSaveDataId;
[FieldOffset(0x28)] public ProgramId ProgramId;
[FieldOffset(0x30)] public long Size;
[FieldOffset(0x38)] public short Index;
[FieldOffset(0x38)] public ushort Index;
[FieldOffset(0x3A)] public SaveDataRank Rank;
[FieldOffset(0x3B)] public SaveDataState State;
}
[StructLayout(LayoutKind.Explicit, Size = 0x200)]
public struct SaveDataExtraData
{
[FieldOffset(0x00)] public SaveDataAttribute Attribute;
[FieldOffset(0x40)] public ulong OwnerId;
[FieldOffset(0x48)] public ulong TimeStamp;
[FieldOffset(0x50)] public SaveDataFlags Flags;
[FieldOffset(0x58)] public long DataSize;
[FieldOffset(0x60)] public long JournalSize;
[FieldOffset(0x68)] public long CommitId;
}
internal static class SaveDataTypesValidity
{
public static bool IsValid(in SaveDataAttribute attribute)
{
return IsValid(in attribute.Type) && IsValid(in attribute.Rank);
}
public static bool IsValid(in SaveDataCreationInfo creationInfo)
{
return creationInfo.Size >= 0 && creationInfo.JournalSize >= 0 && creationInfo.BlockSize >= 0 &&
IsValid(in creationInfo.SpaceId);
}
public static bool IsValid(in SaveDataMetaInfo metaInfo)
{
return IsValid(in metaInfo.Type);
}
public static bool IsValid(in SaveDataFilter filter)
{
return IsValid(in filter.Attribute);
}
public static bool IsValid(in SaveDataType type)
{
// SaveDataType.SystemBcat is excluded in this check
return (uint)type <= (uint)SaveDataType.Cache;
}
public static bool IsValid(in SaveDataRank rank)
{
return (uint)rank <= (uint)SaveDataRank.Secondary;
}
public static bool IsValid(in SaveDataSpaceId spaceId)
{
return (uint)spaceId <= (uint)SaveDataSpaceId.SdCache || spaceId == SaveDataSpaceId.ProperSystem ||
spaceId == SaveDataSpaceId.SafeMode;
}
public static bool IsValid(in SaveDataMetaType metaType)
{
return (uint)metaType <= (uint)SaveDataMetaType.ExtensionContext;
}
}
}

View File

@ -4,7 +4,7 @@ using LibHac.FsSrv;
namespace LibHac.Fs
{
/// <summary>
/// The default access logger that will output to the SD card via <see cref="FileSystemProxy"/>.
/// The default access logger that will output to the SD card via <see cref="FileSystemProxyImpl"/>.
/// </summary>
public class SdCardAccessLog : IAccessLog
{

View File

@ -1,8 +1,9 @@
using System;
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.Fs.Impl;
using LibHac.FsSrv;
using LibHac.FsSystem;
using LibHac.FsSrv.Sf;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
namespace LibHac.Fs.Shim
{
@ -40,14 +41,20 @@ namespace LibHac.Fs.Shim
Result rc = MountHelpers.CheckMountName(mountName);
if (rc.IsFailure()) return rc;
FsPath.FromSpan(out FsPath fsPath, path);
FspPath.FromSpan(out FspPath sfPath, path);
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
rc = fsProxy.OpenFileSystemWithId(out IFileSystem fileSystem, ref fsPath, default, FileSystemProxyType.Package);
rc = fsProxy.OpenFileSystemWithId(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, in sfPath,
default, FileSystemProxyType.Package);
if (rc.IsFailure()) return rc;
return fs.Register(mountName, fileSystem);
using (fileSystem)
{
var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem);
return fs.Register(mountName, fileSystemAdapter);
}
}
}
}

View File

@ -1,7 +1,8 @@
using System;
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.Fs.Impl;
using LibHac.FsSrv;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
namespace LibHac.Fs.Shim
{
@ -43,12 +44,23 @@ namespace LibHac.Fs.Shim
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
var attribute = new SaveDataAttribute(applicationId, SaveDataType.Bcat, UserId.Zero, 0);
var attribute = new SaveDataAttribute(applicationId, SaveDataType.Bcat, UserId.InvalidId, 0);
rc = fsProxy.OpenSaveDataFileSystem(out IFileSystem fileSystem, SaveDataSpaceId.User, ref attribute);
if (rc.IsFailure()) return rc;
ReferenceCountedDisposable<IFileSystemSf> saveFs = null;
return fs.Register(mountName, fileSystem);
try
{
rc = fsProxy.OpenSaveDataFileSystem(out saveFs, SaveDataSpaceId.User, in attribute);
if (rc.IsFailure()) return rc;
var fileSystemAdapter = new FileSystemServiceObjectAdapter(saveFs);
return fs.Register(mountName, fileSystemAdapter);
}
finally
{
saveFs?.Dispose();
}
}
}
}

View File

@ -1,11 +1,15 @@
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.Fs.Impl;
using LibHac.FsSrv;
using LibHac.FsSrv.Sf;
using LibHac.FsSystem;
using LibHac.Util;
using static LibHac.Fs.CommonMountNames;
using static LibHac.Fs.CommonPaths;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
using IStorageSf = LibHac.FsSrv.Sf.IStorage;
namespace LibHac.Fs.Shim
{
@ -80,25 +84,28 @@ namespace LibHac.Fs.Shim
}
// ReSharper disable once UnusedParameter.Local
private static Result MountBisImpl(FileSystemClient fs, U8Span mountName, BisPartitionId partitionId, U8Span rootPath)
private static Result MountBisImpl(FileSystemClient fs, U8Span mountName, BisPartitionId partitionId,
U8Span rootPath)
{
Result rc = MountHelpers.CheckMountNameAcceptingReservedMountName(mountName);
if (rc.IsFailure()) return rc;
FsPath sfPath;
unsafe { _ = &sfPath; } // workaround for CS0165
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
// Nintendo doesn't use the provided rootPath
sfPath.Str[0] = 0;
FspPath.CreateEmpty(out FspPath sfPath);
rc = fsProxy.OpenBisFileSystem(out IFileSystem fileSystem, ref sfPath, partitionId);
rc = fsProxy.OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, in sfPath,
partitionId);
if (rc.IsFailure()) return rc;
var nameGenerator = new BisCommonMountNameGenerator(partitionId);
using (fileSystem)
{
var nameGenerator = new BisCommonMountNameGenerator(partitionId);
var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem);
return fs.Register(mountName, fileSystem, nameGenerator);
return fs.Register(mountName, fileSystemAdapter, nameGenerator);
}
}
public static U8Span GetBisMountName(BisPartitionId partitionId)
@ -134,8 +141,7 @@ namespace LibHac.Fs.Shim
// todo: Decide how to handle SetBisRootForHost since it allows mounting any directory on the user's computer
public static Result SetBisRootForHost(this FileSystemClient fs, BisPartitionId partitionId, U8Span rootPath)
{
FsPath sfPath;
unsafe { _ = &sfPath; } // workaround for CS0165
Unsafe.SkipInit(out FsPath path);
int pathLen = StringUtils.GetLength(rootPath, PathTools.MaxPathLength + 1);
if (pathLen > PathTools.MaxPathLength)
@ -147,30 +153,38 @@ namespace LibHac.Fs.Shim
? StringTraits.NullTerminator
: StringTraits.DirectorySeparator;
var sb = new U8StringBuilder(sfPath.Str);
var sb = new U8StringBuilder(path.Str);
Result rc = sb.Append(rootPath).Append(endingSeparator).ToSfPath();
if (rc.IsFailure()) return rc;
}
else
{
sfPath.Str[0] = StringTraits.NullTerminator;
path.Str[0] = StringTraits.NullTerminator;
}
FspPath.FromSpan(out FspPath sfPath, path.Str);
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
return fsProxy.SetBisRootForHost(partitionId, ref sfPath);
return fsProxy.SetBisRootForHost(partitionId, in sfPath);
}
public static Result OpenBisPartition(this FileSystemClient fs, out IStorage partitionStorage, BisPartitionId partitionId)
public static Result OpenBisPartition(this FileSystemClient fs, out IStorage partitionStorage,
BisPartitionId partitionId)
{
partitionStorage = default;
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
Result rc = fsProxy.OpenBisStorage(out IStorage storage, partitionId);
Result rc = fsProxy.OpenBisStorage(out ReferenceCountedDisposable<IStorageSf> storage, partitionId);
if (rc.IsFailure()) return rc;
partitionStorage = storage;
return Result.Success;
using (storage)
{
var storageAdapter = new StorageServiceObjectAdapter(storage);
partitionStorage = storageAdapter;
return Result.Success;
}
}
public static Result InvalidateBisCache(this FileSystemClient fs)

View File

@ -1,9 +1,10 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.Fs.Impl;
using LibHac.FsSrv.Sf;
using LibHac.Ncm;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
namespace LibHac.Fs.Shim
{
@ -49,10 +50,16 @@ namespace LibHac.Fs.Shim
IFileSystemProxyForLoader fsProxy = fs.GetFileSystemProxyForLoaderServiceObject();
rc = fsProxy.OpenCodeFileSystem(out IFileSystem codeFs, out verificationData, in fsPath, programId);
rc = fsProxy.OpenCodeFileSystem(out ReferenceCountedDisposable<IFileSystemSf> codeFs, out verificationData,
in fsPath, programId);
if (rc.IsFailure()) return rc;
return fs.Register(mountName, codeFs);
using (codeFs)
{
var fileSystemAdapter = new FileSystemServiceObjectAdapter(codeFs);
return fs.Register(mountName, fileSystemAdapter);
}
}
}
}

View File

@ -1,9 +1,10 @@
using System;
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.Fs.Impl;
using LibHac.FsSrv;
using LibHac.FsSystem;
using LibHac.FsSrv.Sf;
using LibHac.Ncm;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
namespace LibHac.Fs.Shim
{
@ -27,10 +28,16 @@ namespace LibHac.Fs.Shim
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
rc = fsProxy.OpenFileSystemWithPatch(out IFileSystem fileSystem, programId, fspType);
rc = fsProxy.OpenFileSystemWithPatch(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, programId,
fspType);
if (rc.IsFailure()) return rc;
return fs.Register(mountName, fileSystem);
using (fileSystem)
{
var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem);
return fs.Register(mountName, fileSystemAdapter);
}
}
public static Result MountContent(this FileSystemClient fs, U8Span mountName, U8Span path, ProgramId programId, ContentType type)
@ -55,14 +62,20 @@ namespace LibHac.Fs.Shim
private static Result MountContentImpl(FileSystemClient fs, U8Span mountName, U8Span path, ulong id, FileSystemProxyType type)
{
FsPath.FromSpan(out FsPath fsPath, path);
FspPath.FromSpan(out FspPath fsPath, path);
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
Result rc = fsProxy.OpenFileSystemWithId(out IFileSystem fileSystem, ref fsPath, id, type);
Result rc = fsProxy.OpenFileSystemWithId(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, in fsPath,
id, type);
if (rc.IsFailure()) return rc;
return fs.Register(mountName, fileSystem);
using (fileSystem)
{
var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem);
return fs.Register(mountName, fileSystemAdapter);
}
}
private static FileSystemProxyType ConvertToFileSystemProxyType(ContentType type) => type switch
@ -71,7 +84,7 @@ namespace LibHac.Fs.Shim
ContentType.Control => FileSystemProxyType.Control,
ContentType.Manual => FileSystemProxyType.Manual,
ContentType.Data => FileSystemProxyType.Data,
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null),
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
};
}
}

View File

@ -1,8 +1,9 @@
using System;
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.Fs.Impl;
using LibHac.FsSrv;
using LibHac.Util;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
namespace LibHac.Fs.Shim
{
@ -20,12 +21,17 @@ namespace LibHac.Fs.Shim
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
rc = fsProxy.OpenContentStorageFileSystem(out IFileSystem contentFs, storageId);
rc = fsProxy.OpenContentStorageFileSystem(out ReferenceCountedDisposable<IFileSystemSf> contentFs, storageId);
if (rc.IsFailure()) return rc;
var mountNameGenerator = new ContentStorageCommonMountNameGenerator(storageId);
using (contentFs)
{
var mountNameGenerator = new ContentStorageCommonMountNameGenerator(storageId);
return fs.Register(mountName, contentFs, mountNameGenerator);
var fileSystemAdapter = new FileSystemServiceObjectAdapter(contentFs);
return fs.Register(mountName, fileSystemAdapter, mountNameGenerator);
}
}
public static U8String GetContentStorageMountName(ContentStorageId storageId)
@ -33,11 +39,11 @@ namespace LibHac.Fs.Shim
switch (storageId)
{
case ContentStorageId.System:
return CommonMountNames.ContentStorageSystemMountName;
return CommonPaths.ContentStorageSystemMountName;
case ContentStorageId.User:
return CommonMountNames.ContentStorageUserMountName;
return CommonPaths.ContentStorageUserMountName;
case ContentStorageId.SdCard:
return CommonMountNames.ContentStorageSdCardMountName;
return CommonPaths.ContentStorageSdCardMountName;
default:
throw new ArgumentOutOfRangeException(nameof(storageId), storageId, null);
}

View File

@ -1,7 +1,8 @@
using System;
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.Fs.Impl;
using LibHac.FsSrv;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
namespace LibHac.Fs.Shim
{
@ -12,12 +13,22 @@ namespace LibHac.Fs.Shim
Result rc = MountHelpers.CheckMountName(mountName);
if (rc.IsFailure()) return rc;
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
ReferenceCountedDisposable<IFileSystemSf> customFs = null;
try
{
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
rc = fsProxy.OpenCustomStorageFileSystem(out IFileSystem customFs, storageId);
if (rc.IsFailure()) return rc;
rc = fsProxy.OpenCustomStorageFileSystem(out customFs, storageId);
if (rc.IsFailure()) return rc;
return fs.Register(mountName, customFs);
var adapter = new FileSystemServiceObjectAdapter(customFs);
return fs.Register(mountName, adapter);
}
finally
{
customFs?.Dispose();
}
}
public static string GetCustomStorageDirectoryName(CustomStorageId storageId)

View File

@ -1,7 +1,10 @@
using System;
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.Fs.Impl;
using LibHac.FsSrv;
using LibHac.FsSrv.Sf;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
using IStorageSf = LibHac.FsSrv.Sf.IStorage;
namespace LibHac.Fs.Shim
{
@ -11,33 +14,63 @@ namespace LibHac.Fs.Shim
{
handle = default;
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
ReferenceCountedDisposable<IDeviceOperator> deviceOperator = null;
try
{
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
Result rc = fsProxy.OpenDeviceOperator(out IDeviceOperator deviceOperator);
if (rc.IsFailure()) return rc;
Result rc = fsProxy.OpenDeviceOperator(out deviceOperator);
if (rc.IsFailure()) return rc;
return deviceOperator.GetGameCardHandle(out handle);
return deviceOperator.Target.GetGameCardHandle(out handle);
}
finally
{
deviceOperator?.Dispose();
}
}
public static bool IsGameCardInserted(this FileSystemClient fs)
{
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
ReferenceCountedDisposable<IDeviceOperator> deviceOperator = null;
try
{
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
Result rc = fsProxy.OpenDeviceOperator(out IDeviceOperator deviceOperator);
if (rc.IsFailure()) throw new LibHacException("Abort");
Result rc = fsProxy.OpenDeviceOperator(out deviceOperator);
if (rc.IsFailure()) throw new LibHacException("Abort");
rc = deviceOperator.IsGameCardInserted(out bool isInserted);
if (rc.IsFailure()) throw new LibHacException("Abort");
rc = deviceOperator.Target.IsGameCardInserted(out bool isInserted);
if (rc.IsFailure()) throw new LibHacException("Abort");
return isInserted;
return isInserted;
}
finally
{
deviceOperator?.Dispose();
}
}
public static Result OpenGameCardPartition(this FileSystemClient fs, out IStorage storage,
GameCardHandle handle, GameCardPartitionRaw partitionType)
{
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
storage = default;
return fsProxy.OpenGameCardStorage(out storage, handle, partitionType);
ReferenceCountedDisposable<IStorageSf> sfStorage = null;
try
{
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
Result rc = fsProxy.OpenGameCardStorage(out sfStorage, handle, partitionType);
if (rc.IsFailure()) return rc;
storage = new StorageServiceObjectAdapter(sfStorage);
return Result.Success;
}
finally
{
sfStorage?.Dispose();
}
}
public static Result MountGameCardPartition(this FileSystemClient fs, U8Span mountName, GameCardHandle handle,
@ -48,12 +81,16 @@ namespace LibHac.Fs.Shim
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
rc = fsProxy.OpenGameCardFileSystem(out IFileSystem cardFs, handle, partitionId);
rc = fsProxy.OpenGameCardFileSystem(out ReferenceCountedDisposable<IFileSystemSf> cardFs, handle, partitionId);
if (rc.IsFailure()) return rc;
var mountNameGenerator = new GameCardCommonMountNameGenerator(handle, partitionId);
using (cardFs)
{
var mountNameGenerator = new GameCardCommonMountNameGenerator(handle, partitionId);
var fileSystemAdapter = new FileSystemServiceObjectAdapter(cardFs);
return fs.Register(mountName, cardFs, mountNameGenerator);
return fs.Register(mountName, fileSystemAdapter, mountNameGenerator);
}
}
private class GameCardCommonMountNameGenerator : ICommonMountNameGenerator
@ -71,7 +108,7 @@ namespace LibHac.Fs.Shim
{
char letter = GetGameCardMountNameSuffix(PartitionId);
string mountName = $"{CommonMountNames.GameCardFileSystemMountName}{letter}{Handle.Value:x8}";
string mountName = $"{CommonPaths.GameCardFileSystemMountName}{letter}{Handle.Value:x8}";
new U8Span(mountName).Value.CopyTo(nameBuffer);
return Result.Success;

View File

@ -2,11 +2,14 @@
using System.Diagnostics;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.Fs.Impl;
using LibHac.FsSrv;
using LibHac.FsSrv.Sf;
using LibHac.FsSystem;
using LibHac.Util;
using static LibHac.Fs.CommonMountNames;
using static LibHac.Fs.CommonPaths;
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
namespace LibHac.Fs.Shim
{
@ -76,12 +79,11 @@ namespace LibHac.Fs.Shim
public static Result MountHostRoot(this FileSystemClient fs)
{
IFileSystem hostFileSystem = default;
var path = new FsPath();
path.Str[0] = 0;
FspPath.CreateEmpty(out FspPath path);
static string LogMessageGenerator() => $", name: \"{HostRootFileSystemMountName.ToString()}\"";
Result OpenHostFs() => OpenHostFileSystemImpl(fs, out hostFileSystem, ref path, MountHostOption.None);
Result OpenHostFs() => OpenHostFileSystemImpl(fs, out hostFileSystem, in path, MountHostOption.None);
Result MountHostFs() => fs.Register(HostRootFileSystemMountName, hostFileSystem,
new HostRootCommonMountNameGenerator());
@ -110,13 +112,12 @@ namespace LibHac.Fs.Shim
public static Result MountHostRoot(this FileSystemClient fs, MountHostOption option)
{
IFileSystem hostFileSystem = default;
var path = new FsPath();
path.Str[0] = 0;
FspPath.CreateEmpty(out FspPath path);
string LogMessageGenerator() =>
$", name: \"{HostRootFileSystemMountName.ToString()}, mount_host_option: {option}\"";
Result OpenHostFs() => OpenHostFileSystemImpl(fs, out hostFileSystem, ref path, option);
Result OpenHostFs() => OpenHostFileSystemImpl(fs, out hostFileSystem, in path, option);
Result MountHostFs() => fs.Register(HostRootFileSystemMountName, hostFileSystem,
new HostRootCommonMountNameGenerator());
@ -318,8 +319,7 @@ namespace LibHac.Fs.Shim
if (pathLength + 1 > PathTools.MaxPathLength)
return ResultFs.TooLongPath.Log();
FsPath fullPath;
unsafe { _ = &fullPath; } // workaround for CS0165
Unsafe.SkipInit(out FsPath fullPath);
var sb = new U8StringBuilder(fullPath.Str);
sb.Append(StringTraits.DirectorySeparator).Append(path);
@ -341,7 +341,9 @@ namespace LibHac.Fs.Shim
}
}
return OpenHostFileSystemImpl(fs, out fileSystem, ref fullPath, option);
FspPath.FromSpan(out FspPath sfPath, fullPath.Str);
return OpenHostFileSystemImpl(fs, out fileSystem, in sfPath, option);
}
/// <summary>
@ -352,26 +354,34 @@ namespace LibHac.Fs.Shim
/// <param name="path">The path on the host computer to open. e.g. /C:\Windows\System32/</param>
/// <param name="option">Options for opening the host file system.</param>
/// <returns>The <see cref="Result"/> of the operation.</returns>
private static Result OpenHostFileSystemImpl(FileSystemClient fs, out IFileSystem fileSystem, ref FsPath path, MountHostOption option)
private static Result OpenHostFileSystemImpl(FileSystemClient fs, out IFileSystem fileSystem, in FspPath path,
MountHostOption option)
{
fileSystem = default;
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
IFileSystem hostFs;
ReferenceCountedDisposable<IFileSystemSf> hostFs = null;
if (option == MountHostOption.None)
try
{
Result rc = fsProxy.OpenHostFileSystem(out hostFs, ref path);
if (rc.IsFailure()) return rc;
}
else
{
Result rc = fsProxy.OpenHostFileSystemWithOption(out hostFs, ref path, option);
if (rc.IsFailure()) return rc;
}
if (option == MountHostOption.None)
{
Result rc = fsProxy.OpenHostFileSystem(out hostFs, in path);
if (rc.IsFailure()) return rc;
}
else
{
Result rc = fsProxy.OpenHostFileSystemWithOption(out hostFs, in path, option);
if (rc.IsFailure()) return rc;
}
fileSystem = hostFs;
return Result.Success;
fileSystem = new FileSystemServiceObjectAdapter(hostFs);
return Result.Success;
}
finally
{
hostFs?.Dispose();
}
}
}
}

View File

@ -1,6 +1,7 @@
using System;
using System.Runtime.InteropServices;
using LibHac.FsSrv;
using LibHac.Sf;
namespace LibHac.Fs.Shim
{
@ -21,7 +22,7 @@ namespace LibHac.Fs.Shim
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
ReadOnlySpan<byte> mapInfoBuffer = MemoryMarshal.Cast<ProgramIndexMapInfo, byte>(mapInfo);
var mapInfoBuffer = new InBuffer(MemoryMarshal.Cast<ProgramIndexMapInfo, byte>(mapInfo));
return fsProxy.RegisterProgramIndexMapInfo(mapInfoBuffer, mapInfo.Length);
}

View File

@ -2,6 +2,7 @@
using LibHac.FsSrv;
using LibHac.FsSrv.Sf;
using LibHac.Ncm;
using LibHac.Sf;
namespace LibHac.Fs.Shim
{
@ -16,8 +17,8 @@ namespace LibHac.Fs.Shim
Result rc = registry.SetCurrentProcess(fs.Hos.ProcessId.Value);
if (rc.IsFailure()) return rc;
return registry.RegisterProgram(processId, programId, storageId, accessControlData,
accessControlDescriptor);
return registry.RegisterProgram(processId, programId, storageId, new InBuffer(accessControlData),
new InBuffer(accessControlDescriptor));
}
/// <inheritdoc cref="ProgramRegistryImpl.UnregisterProgram"/>

View File

@ -1,6 +1,6 @@
using LibHac.Common;
using LibHac.FsSrv;
using LibHac.FsSystem;
using LibHac.FsSrv.Sf;
using LibHac.Ncm;
using LibHac.Spl;
using FsRightsId = LibHac.Fs.RightsId;
@ -23,10 +23,10 @@ namespace LibHac.Fs.Shim
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
Result rc = FsPath.FromSpan(out FsPath fsPath, path);
Result rc = FspPath.FromSpan(out FspPath sfPath, path);
if (rc.IsFailure()) return rc;
return fsProxy.GetRightsIdByPath(out rightsId, ref fsPath);
return fsProxy.GetRightsIdByPath(out rightsId, in sfPath);
}
public static Result GetRightsId(this FileSystemClient fs, out FsRightsId rightsId, out byte keyGeneration, U8Span path)
@ -36,24 +36,24 @@ namespace LibHac.Fs.Shim
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
Result rc = FsPath.FromSpan(out FsPath fsPath, path);
Result rc = FspPath.FromSpan(out FspPath sfPath, path);
if (rc.IsFailure()) return rc;
return fsProxy.GetRightsIdAndKeyGenerationByPath(out rightsId, out keyGeneration, ref fsPath);
return fsProxy.GetRightsIdAndKeyGenerationByPath(out rightsId, out keyGeneration, in sfPath);
}
public static Result RegisterExternalKey(this FileSystemClient fs, ref FsRightsId rightsId, ref AccessKey key)
public static Result RegisterExternalKey(this FileSystemClient fs, in FsRightsId rightsId, in AccessKey key)
{
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
return fsProxy.RegisterExternalKey(ref rightsId, ref key);
return fsProxy.RegisterExternalKey(in rightsId, in key);
}
public static Result UnregisterExternalKey(this FileSystemClient fs, ref FsRightsId rightsId)
{
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
return fsProxy.UnregisterExternalKey(ref rightsId);
return fsProxy.UnregisterExternalKey(in rightsId);
}
public static Result UnregisterAllExternalKey(this FileSystemClient fs)

View File

@ -1,8 +1,9 @@
using System;
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.Fs.Impl;
using LibHac.FsSrv;
using LibHac.Ncm;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
namespace LibHac.Fs.Shim
{
@ -115,14 +116,14 @@ namespace LibHac.Fs.Shim
if (fs.IsEnabledAccessLog(AccessLogTarget.Application))
{
TimeSpan startTime = fs.Time.GetCurrent();
rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, default, default, SaveDataType.Cache, false, (short)index);
rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, default, default, SaveDataType.Cache, false, (ushort)index);
TimeSpan endTime = fs.Time.GetCurrent();
fs.OutputAccessLog(rc, startTime, endTime, $", name: \"{mountName.ToString()}\", index: {index}");
}
else
{
rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, default, default, SaveDataType.Cache, false, (short)index);
rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, default, default, SaveDataType.Cache, false, (ushort)index);
}
if (rc.IsSuccess() && fs.IsEnabledAccessLog(AccessLogTarget.Application))
@ -165,14 +166,14 @@ namespace LibHac.Fs.Shim
if (fs.IsEnabledAccessLog(AccessLogTarget.System))
{
TimeSpan startTime = fs.Time.GetCurrent();
rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, applicationId, default, SaveDataType.Cache, false, (short)index);
rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, applicationId, default, SaveDataType.Cache, false, (ushort)index);
TimeSpan endTime = fs.Time.GetCurrent();
fs.OutputAccessLog(rc, startTime, endTime, $", name: \"{mountName.ToString()}\", applicationid: 0x{applicationId}, index: {index}");
}
else
{
rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, applicationId, default, SaveDataType.Cache, false, (short)index);
rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, applicationId, default, SaveDataType.Cache, false, (ushort)index);
}
if (rc.IsSuccess() && fs.IsEnabledAccessLog(AccessLogTarget.System))
@ -184,7 +185,7 @@ namespace LibHac.Fs.Shim
}
private static Result MountSaveDataImpl(this FileSystemClient fs, U8Span mountName, SaveDataSpaceId spaceId,
ProgramId programId, UserId userId, SaveDataType type, bool openReadOnly, short index)
ProgramId programId, UserId userId, SaveDataType type, bool openReadOnly, ushort index)
{
Result rc = MountHelpers.CheckMountName(mountName);
if (rc.IsFailure()) return rc;
@ -193,20 +194,29 @@ namespace LibHac.Fs.Shim
var attribute = new SaveDataAttribute(programId, type, userId, 0, index);
IFileSystem saveFs;
ReferenceCountedDisposable<IFileSystemSf> saveFs = null;
if (openReadOnly)
try
{
rc = fsProxy.OpenReadOnlySaveDataFileSystem(out saveFs, spaceId, ref attribute);
if (openReadOnly)
{
rc = fsProxy.OpenReadOnlySaveDataFileSystem(out saveFs, spaceId, in attribute);
}
else
{
rc = fsProxy.OpenSaveDataFileSystem(out saveFs, spaceId, in attribute);
}
if (rc.IsFailure()) return rc;
var fileSystemAdapter = new FileSystemServiceObjectAdapter(saveFs);
return fs.Register(mountName, fileSystemAdapter, fileSystemAdapter, null);
}
else
finally
{
rc = fsProxy.OpenSaveDataFileSystem(out saveFs, spaceId, ref attribute);
saveFs?.Dispose();
}
if (rc.IsFailure()) return rc;
return fs.Register(mountName, saveFs);
}
}
}

View File

@ -1,9 +1,10 @@
using System;
using System.Runtime.InteropServices;
using LibHac.Common;
using LibHac.Diag;
using LibHac.FsSrv;
using LibHac.FsSrv.Sf;
using LibHac.Ncm;
using LibHac.Sf;
namespace LibHac.Fs.Shim
{
@ -29,13 +30,13 @@ namespace LibHac.Fs.Shim
SpaceId = SaveDataSpaceId.User
};
var metaInfo = new SaveMetaCreateInfo
var metaInfo = new SaveDataMetaInfo
{
Type = SaveDataMetaType.Thumbnail,
Size = 0x40060
};
return fsProxy.CreateSaveDataFileSystem(ref attribute, ref createInfo, ref metaInfo);
return fsProxy.CreateSaveDataFileSystem(in attribute, in createInfo, in metaInfo);
},
() =>
$", applicationid: 0x{applicationId.Value:X}, userid: 0x{userId}, save_data_owner_id: 0x{ownerId:X}, save_data_size: {size}, save_data_journal_size: {journalSize}, save_data_flags: 0x{(int)flags:X8}");
@ -61,14 +62,14 @@ namespace LibHac.Fs.Shim
SpaceId = SaveDataSpaceId.User
};
var metaInfo = new SaveMetaCreateInfo
var metaInfo = new SaveDataMetaInfo
{
Type = SaveDataMetaType.Thumbnail,
Size = 0x40060
};
return fsProxy.CreateSaveDataFileSystemWithHashSalt(ref attribute, ref createInfo, ref metaInfo,
ref hashSalt);
return fsProxy.CreateSaveDataFileSystemWithHashSalt(in attribute, in createInfo, in metaInfo,
in hashSalt);
},
() =>
$", applicationid: 0x{applicationId.Value:X}, userid: 0x{userId}, save_data_owner_id: 0x{ownerId:X}, save_data_size: {size}, save_data_journal_size: {journalSize}, save_data_flags: 0x{(int)flags:X8}");
@ -81,7 +82,7 @@ namespace LibHac.Fs.Shim
{
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
var attribute = new SaveDataAttribute(applicationId, SaveDataType.Bcat, UserId.Zero, 0);
var attribute = new SaveDataAttribute(applicationId, SaveDataType.Bcat, UserId.InvalidId, 0);
var createInfo = new SaveDataCreationInfo
{
@ -93,9 +94,9 @@ namespace LibHac.Fs.Shim
SpaceId = SaveDataSpaceId.User
};
var metaInfo = new SaveMetaCreateInfo();
var metaInfo = new SaveDataMetaInfo();
return fsProxy.CreateSaveDataFileSystem(ref attribute, ref createInfo, ref metaInfo);
return fsProxy.CreateSaveDataFileSystem(in attribute, in createInfo, in metaInfo);
},
() => $", applicationid: 0x{applicationId.Value:X}, save_data_size: {size}");
}
@ -108,7 +109,7 @@ namespace LibHac.Fs.Shim
{
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
var attribute = new SaveDataAttribute(applicationId, SaveDataType.Device, UserId.Zero, 0);
var attribute = new SaveDataAttribute(applicationId, SaveDataType.Device, UserId.InvalidId, 0);
var createInfo = new SaveDataCreationInfo
{
@ -120,9 +121,9 @@ namespace LibHac.Fs.Shim
SpaceId = SaveDataSpaceId.User
};
var metaInfo = new SaveMetaCreateInfo();
var metaInfo = new SaveDataMetaInfo();
return fsProxy.CreateSaveDataFileSystem(ref attribute, ref createInfo, ref metaInfo);
return fsProxy.CreateSaveDataFileSystem(in attribute, in createInfo, in metaInfo);
},
() => $", applicationid: 0x{applicationId.Value:X}, save_data_owner_id: 0x{ownerId:X}, save_data_size: {size}, save_data_journal_size: {journalSize}, save_data_flags: 0x{(int)flags:X8}");
}
@ -134,7 +135,7 @@ namespace LibHac.Fs.Shim
{
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
var attribute = new SaveDataAttribute(applicationId, SaveDataType.Temporary, UserId.Zero, 0);
var attribute = new SaveDataAttribute(applicationId, SaveDataType.Temporary, UserId.InvalidId, 0);
var createInfo = new SaveDataCreationInfo
{
@ -145,22 +146,22 @@ namespace LibHac.Fs.Shim
SpaceId = SaveDataSpaceId.Temporary
};
var metaInfo = new SaveMetaCreateInfo();
var metaInfo = new SaveDataMetaInfo();
return fsProxy.CreateSaveDataFileSystem(ref attribute, ref createInfo, ref metaInfo);
return fsProxy.CreateSaveDataFileSystem(in attribute, in createInfo, in metaInfo);
},
() => $", applicationid: 0x{applicationId.Value:X}, save_data_owner_id: 0x{ownerId:X}, save_data_size: {size}, save_data_flags: 0x{(int)flags:X8}");
}
public static Result CreateCacheStorage(this FileSystemClient fs, Ncm.ApplicationId applicationId,
SaveDataSpaceId spaceId, ulong ownerId, short index, long size, long journalSize, SaveDataFlags flags)
SaveDataSpaceId spaceId, ulong ownerId, ushort index, long size, long journalSize, SaveDataFlags flags)
{
return fs.RunOperationWithAccessLog(AccessLogTarget.System,
() =>
{
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
var attribute = new SaveDataAttribute(applicationId, SaveDataType.Cache, UserId.Zero, 0, index);
var attribute = new SaveDataAttribute(applicationId, SaveDataType.Cache, UserId.InvalidId, 0, index);
var creationInfo = new SaveDataCreationInfo
{
@ -172,9 +173,9 @@ namespace LibHac.Fs.Shim
SpaceId = spaceId
};
var metaInfo = new SaveMetaCreateInfo();
var metaInfo = new SaveDataMetaInfo();
return fsProxy.CreateSaveDataFileSystem(ref attribute, ref creationInfo, ref metaInfo);
return fsProxy.CreateSaveDataFileSystem(in attribute, in creationInfo, in metaInfo);
},
() => $", applicationid: 0x{applicationId.Value:X}, savedataspaceid: {spaceId}, save_data_owner_id: 0x{ownerId:X}, save_data_size: {size}, save_data_journal_size: {journalSize}, save_data_flags: 0x{(int)flags:X8}");
}
@ -211,7 +212,7 @@ namespace LibHac.Fs.Shim
SpaceId = spaceId
};
return fsProxy.CreateSaveDataFileSystemBySystemSaveDataId(ref attribute, ref createInfo);
return fsProxy.CreateSaveDataFileSystemBySystemSaveDataId(in attribute, in createInfo);
},
() => $", savedataspaceid: {spaceId}, savedataid: 0x{saveDataId:X}, userid: 0x{userId.Id.High:X16}{userId.Id.Low:X16}, save_data_owner_id: 0x{ownerId:X}, save_data_size: {size}, save_data_journal_size: {journalSize}, save_data_flags: 0x{(int)flags:x8}");
}
@ -231,19 +232,19 @@ namespace LibHac.Fs.Shim
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, ulong ownerId, long size,
long journalSize, SaveDataFlags flags)
{
return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, UserId.Zero, ownerId, size, journalSize, flags);
return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, UserId.InvalidId, ownerId, size, journalSize, flags);
}
public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, long size,
long journalSize, SaveDataFlags flags)
{
return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, UserId.Zero, 0, size, journalSize, flags);
return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, UserId.InvalidId, 0, size, journalSize, flags);
}
public static Result CreateSystemSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId,
ulong ownerId, long size, long journalSize, SaveDataFlags flags)
{
return CreateSystemSaveData(fs, spaceId, saveDataId, UserId.Zero, ownerId, size, journalSize, flags);
return CreateSystemSaveData(fs, spaceId, saveDataId, UserId.InvalidId, ownerId, size, journalSize, flags);
}
public static Result DeleteSaveData(this FileSystemClient fs, ulong saveDataId)
@ -283,8 +284,8 @@ namespace LibHac.Fs.Shim
tempInfo = new SaveDataInfo();
Result rc = fsProxy.FindSaveDataWithFilter(out long count, SpanHelpers.AsByteSpan(ref tempInfo),
spaceId, ref tempFilter);
Result rc = fsProxy.FindSaveDataWithFilter(out long count, OutBuffer.FromStruct(ref tempInfo),
spaceId, in tempFilter);
if (rc.IsFailure()) return rc;
if (count == 0)
@ -329,49 +330,64 @@ namespace LibHac.Fs.Shim
{
var tempIterator = new SaveDataIterator();
Result result = fs.RunOperationWithAccessLog(AccessLogTarget.System,
() =>
{
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
try
{
Result result = fs.RunOperationWithAccessLog(AccessLogTarget.System,
() =>
{
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
Result rc = fsProxy.OpenSaveDataInfoReaderBySaveDataSpaceId(
out ReferenceCountedDisposable<ISaveDataInfoReader> reader, spaceId);
if (rc.IsFailure()) return rc;
Result rc = fsProxy.OpenSaveDataInfoReaderBySaveDataSpaceId(
out ReferenceCountedDisposable<ISaveDataInfoReader> reader, spaceId);
if (rc.IsFailure()) return rc;
tempIterator = new SaveDataIterator(fs, reader);
tempIterator = new SaveDataIterator(fs, reader);
return Result.Success;
},
() => $", savedataspaceid: {spaceId}");
return Result.Success;
},
() => $", savedataspaceid: {spaceId}");
iterator = result.IsSuccess() ? tempIterator : default;
iterator = result.IsSuccess() ? tempIterator : default;
tempIterator = default;
return result;
return result;
}
finally
{
tempIterator.Dispose();
}
}
public static Result OpenSaveDataIterator(this FileSystemClient fs, out SaveDataIterator iterator, SaveDataSpaceId spaceId, ref SaveDataFilter filter)
{
ReferenceCountedDisposable<ISaveDataInfoReader> reader = null;
var tempIterator = new SaveDataIterator();
SaveDataFilter tempFilter = filter;
Result result = fs.RunOperationWithAccessLog(AccessLogTarget.System,
() =>
{
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
try
{
Result result = fs.RunOperationWithAccessLog(AccessLogTarget.System,
() =>
{
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
Result rc = fsProxy.OpenSaveDataInfoReaderWithFilter(
out ReferenceCountedDisposable<ISaveDataInfoReader> reader, spaceId, ref tempFilter);
if (rc.IsFailure()) return rc;
Result rc = fsProxy.OpenSaveDataInfoReaderWithFilter(out reader, spaceId, in tempFilter);
if (rc.IsFailure()) return rc;
tempIterator = new SaveDataIterator(fs, reader);
tempIterator = new SaveDataIterator(fs, reader);
return Result.Success;
},
() => $", savedataspaceid: {spaceId}");
return Result.Success;
},
() => $", savedataspaceid: {spaceId}");
iterator = result.IsSuccess() ? tempIterator : default;
iterator = result.IsSuccess() ? tempIterator : default;
return result;
return result;
}
finally
{
reader?.Dispose();
}
}
public static void DisableAutoSaveDataCreation(this FileSystemClient fsClient)
@ -395,14 +411,14 @@ namespace LibHac.Fs.Shim
internal SaveDataIterator(FileSystemClient fsClient, ReferenceCountedDisposable<ISaveDataInfoReader> reader)
{
FsClient = fsClient;
Reader = reader;
Reader = reader.AddReference();
}
public Result ReadSaveDataInfo(out long readCount, Span<SaveDataInfo> buffer)
{
Result rc;
Span<byte> byteBuffer = MemoryMarshal.Cast<SaveDataInfo, byte>(buffer);
var byteBuffer = new OutBuffer(MemoryMarshal.Cast<SaveDataInfo, byte>(buffer));
if (FsClient.IsEnabledAccessLog(AccessLogTarget.System))
{

View File

@ -1,7 +1,9 @@
using System;
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.Fs.Impl;
using LibHac.FsSrv;
using LibHac.FsSrv.Sf;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
namespace LibHac.Fs.Shim
{
@ -41,31 +43,44 @@ namespace LibHac.Fs.Shim
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
rc = fsProxy.OpenSdCardFileSystem(out IFileSystem fileSystem);
rc = fsProxy.OpenSdCardFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem);
if (rc.IsFailure()) return rc;
return fs.Register(mountName, fileSystem);
using (fileSystem)
{
var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem);
return fs.Register(mountName, fileSystemAdapter);
}
}
}
public static bool IsSdCardInserted(this FileSystemClient fs)
{
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
ReferenceCountedDisposable<IDeviceOperator> deviceOperator = null;
try
{
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
Result rc = fsProxy.OpenDeviceOperator(out IDeviceOperator deviceOperator);
if (rc.IsFailure()) throw new HorizonResultException(rc, "Abort");
Result rc = fsProxy.OpenDeviceOperator(out deviceOperator);
if (rc.IsFailure()) throw new HorizonResultException(rc, "Abort");
rc = deviceOperator.IsSdCardInserted(out bool isInserted);
if (rc.IsFailure()) throw new HorizonResultException(rc, "Abort");
rc = deviceOperator.Target.IsSdCardInserted(out bool isInserted);
if (rc.IsFailure()) throw new HorizonResultException(rc, "Abort");
return isInserted;
return isInserted;
}
finally
{
deviceOperator?.Dispose();
}
}
public static Result SetSdCardEncryptionSeed(this FileSystemClient fs, ref EncryptionSeed seed)
public static Result SetSdCardEncryptionSeed(this FileSystemClient fs, in EncryptionSeed seed)
{
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
Result rc = fsProxy.SetSdCardEncryptionSeed(ref seed);
Result rc = fsProxy.SetSdCardEncryptionSeed(in seed);
if (rc.IsFailure()) throw new HorizonResultException(rc, "Abort");
return Result.Success;

View File

@ -1,15 +1,17 @@
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.Fs.Impl;
using LibHac.FsSrv;
using LibHac.Ncm;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
namespace LibHac.Fs.Shim
{
public static class SystemSaveData
{
public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName, SaveDataSpaceId spaceId, ulong saveDataId)
public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName, SaveDataSpaceId spaceId,
ulong saveDataId)
{
return MountSystemSaveData(fs, mountName, spaceId, saveDataId, UserId.Zero);
return MountSystemSaveData(fs, mountName, spaceId, saveDataId, UserId.InvalidId);
}
public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName,
@ -22,10 +24,20 @@ namespace LibHac.Fs.Shim
var attribute = new SaveDataAttribute(ProgramId.InvalidId, SaveDataType.System, userId, saveDataId);
rc = fsProxy.OpenSaveDataFileSystemBySystemSaveDataId(out IFileSystem fileSystem, spaceId, ref attribute);
if (rc.IsFailure()) return rc;
ReferenceCountedDisposable<IFileSystemSf> saveFs = null;
return fs.Register(mountName, fileSystem);
try
{
rc = fsProxy.OpenSaveDataFileSystemBySystemSaveDataId(out saveFs, spaceId, in attribute);
if (rc.IsFailure()) return rc;
var fileSystemAdapter = new FileSystemServiceObjectAdapter(saveFs);
return fs.Register(mountName, fileSystemAdapter);
}
finally
{
saveFs?.Dispose();
}
}
}
}

View File

@ -0,0 +1,53 @@
using System;
using LibHac.Common;
using LibHac.Fs.Accessors;
using LibHac.FsSrv.Sf;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
namespace LibHac.Fs.Shim
{
public static class UserFileSystem
{
public static Result Commit(this FileSystemClient fs, ReadOnlySpan<U8String> mountNames)
{
// Todo: Add access log
if (mountNames.Length > 10)
return ResultFs.InvalidCommitNameCount.Log();
if (mountNames.Length == 0)
return Result.Success;
ReferenceCountedDisposable<IMultiCommitManager> commitManager = null;
ReferenceCountedDisposable<IFileSystemSf> fileSystem = null;
try
{
Result rc = fs.GetFileSystemProxyServiceObject().OpenMultiCommitManager(out commitManager);
if (rc.IsFailure()) return rc;
for (int i = 0; i < mountNames.Length; i++)
{
rc = fs.MountTable.Find(mountNames[i].ToString(), out FileSystemAccessor accessor);
if (rc.IsFailure()) return rc;
fileSystem = accessor.GetMultiCommitTarget();
if (fileSystem is null)
return ResultFs.UnsupportedCommitTarget.Log();
rc = commitManager.Target.Add(fileSystem);
if (rc.IsFailure()) return rc;
}
rc = commitManager.Target.Commit();
if (rc.IsFailure()) return rc;
return Result.Success;
}
finally
{
commitManager?.Dispose();
fileSystem?.Dispose();
}
}
}
}

View File

@ -9,7 +9,7 @@ namespace LibHac.Fs
[StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct UserId : IEquatable<UserId>, IComparable<UserId>, IComparable
{
public static UserId Zero => default;
public static UserId InvalidId => default;
public readonly Id128 Id;

View File

@ -0,0 +1,66 @@
using System;
using LibHac.Fs;
using LibHac.FsSrv.Impl;
using LibHac.Sf;
namespace LibHac.FsSrv
{
internal readonly struct AccessLogService
{
private readonly AccessLogServiceImpl _serviceImpl;
private readonly ulong _processId;
public AccessLogService(AccessLogServiceImpl serviceImpl, ulong processId)
{
_serviceImpl = serviceImpl;
_processId = processId;
}
public Result SetAccessLogMode(GlobalAccessLogMode mode)
{
Result rc = GetProgramInfo(out ProgramInfo programInfo);
if (rc.IsFailure()) return rc;
if (!programInfo.AccessControl.CanCall(OperationType.SetGlobalAccessLogMode))
return ResultFs.PermissionDenied.Log();
_serviceImpl.SetAccessLogMode(mode);
return Result.Success;
}
public Result GetAccessLogMode(out GlobalAccessLogMode mode)
{
mode = _serviceImpl.GetAccessLogMode();
return Result.Success;
}
public Result OutputAccessLogToSdCard(InBuffer textBuffer)
{
Result rc = GetProgramInfo(out ProgramInfo programInfo);
if (rc.IsFailure()) return rc;
return _serviceImpl.OutputAccessLogToSdCard(textBuffer.Buffer, programInfo.ProgramIdValue, _processId);
}
public Result OutputMultiProgramTagAccessLog()
{
_serviceImpl.OutputAccessLogToSdCard(MultiProgramTag, _processId).IgnoreResult();
return Result.Success;
}
private Result GetProgramInfo(out ProgramInfo programInfo)
{
return _serviceImpl.GetProgramInfo(out programInfo, _processId);
}
private static ReadOnlySpan<byte> MultiProgramTag => // FS_ACCESS: { multi_program_tag: true }\n
new[]
{
(byte) 'F', (byte) 'S', (byte) '_', (byte) 'A', (byte) 'C', (byte) 'C', (byte) 'E', (byte) 'S',
(byte) 'S', (byte) ':', (byte) ' ', (byte) '{', (byte) ' ', (byte) 'm', (byte) 'u', (byte) 'l',
(byte) 't', (byte) 'i', (byte) '_', (byte) 'p', (byte) 'r', (byte) 'o', (byte) 'g', (byte) 'r',
(byte) 'a', (byte) 'm', (byte) '_', (byte) 't', (byte) 'a', (byte) 'g', (byte) ':', (byte) ' ',
(byte) 't', (byte) 'r', (byte) 'u', (byte) 'e', (byte) ' ', (byte) '}', (byte) '\n'
};
}
}

View File

@ -0,0 +1,56 @@
using System;
using LibHac.Fs;
using LibHac.FsSrv.Impl;
namespace LibHac.FsSrv
{
public class AccessLogServiceImpl : IDisposable
{
private Configuration _config;
private GlobalAccessLogMode _accessLogMode;
public AccessLogServiceImpl(in Configuration configuration)
{
_config = configuration;
}
public void Dispose()
{
}
public struct Configuration
{
public ulong MinimumProgramIdForSdCardLog;
// LibHac additions
public HorizonClient HorizonClient;
public ProgramRegistryImpl ProgramRegistry;
}
public void SetAccessLogMode(GlobalAccessLogMode mode)
{
_accessLogMode = mode;
}
public GlobalAccessLogMode GetAccessLogMode()
{
return _accessLogMode;
}
public Result OutputAccessLogToSdCard(ReadOnlySpan<byte> text, ulong processId)
{
throw new NotImplementedException();
}
public Result OutputAccessLogToSdCard(ReadOnlySpan<byte> text, ulong programId, ulong processId)
{
throw new NotImplementedException();
}
internal Result GetProgramInfo(out ProgramInfo programInfo, ulong processId)
{
return _config.ProgramRegistry.GetProgramInfo(out programInfo, processId);
}
}
}

View File

@ -0,0 +1,257 @@
using LibHac.Fs;
using LibHac.FsSrv.Impl;
using LibHac.FsSrv.Sf;
using LibHac.Sf;
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
namespace LibHac.FsSrv
{
public readonly struct BaseFileSystemService
{
private readonly BaseFileSystemServiceImpl _serviceImpl;
private readonly ulong _processId;
public BaseFileSystemService(BaseFileSystemServiceImpl serviceImpl, ulong processId)
{
_serviceImpl = serviceImpl;
_processId = processId;
}
public Result OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, in FspPath rootPath,
BisPartitionId partitionId)
{
fileSystem = default;
Result rc = GetProgramInfo(out ProgramInfo programInfo);
if (rc.IsFailure()) return rc;
// Get the permissions the caller needs
AccessibilityType requiredAccess = partitionId switch
{
BisPartitionId.CalibrationFile => AccessibilityType.MountBisCalibrationFile,
BisPartitionId.SafeMode => AccessibilityType.MountBisSafeMode,
BisPartitionId.User => AccessibilityType.MountBisUser,
BisPartitionId.System => AccessibilityType.MountBisSystem,
BisPartitionId.SystemProperPartition => AccessibilityType.MountBisSystemProperPartition,
_ => AccessibilityType.NotMount
};
// Reject opening invalid partitions
if (requiredAccess == AccessibilityType.NotMount)
return ResultFs.InvalidArgument.Log();
// Verify the caller has the required permissions
Accessibility accessibility = programInfo.AccessControl.GetAccessibilityFor(requiredAccess);
if (!accessibility.CanRead || !accessibility.CanWrite)
return ResultFs.PermissionDenied.Log();
// Normalize the path
var normalizer = new PathNormalizer(rootPath, PathNormalizer.Option.AcceptEmpty);
if (normalizer.Result.IsFailure()) return normalizer.Result;
ReferenceCountedDisposable<IFileSystem> fs = null;
try
{
// Open the file system
rc = _serviceImpl.OpenBisFileSystem(out fs, normalizer.Path,
partitionId);
if (rc.IsFailure()) return rc;
// Create an SF adapter for the file system
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref fs);
return Result.Success;
}
finally
{
fs?.Dispose();
}
}
public Result CreatePaddingFile(long size)
{
// File size must be non-negative
if (size < 0)
return ResultFs.InvalidSize.Log();
// Caller must have the FillBis permission
Result rc = GetProgramInfo(out ProgramInfo programInfo);
if (rc.IsFailure()) return rc;
if (!programInfo.AccessControl.CanCall(OperationType.FillBis))
return ResultFs.PermissionDenied.Log();
return _serviceImpl.CreatePaddingFile(size);
}
public Result DeleteAllPaddingFiles()
{
// Caller must have the FillBis permission
Result rc = GetProgramInfo(out ProgramInfo programInfo);
if (rc.IsFailure()) return rc;
if (!programInfo.AccessControl.CanCall(OperationType.FillBis))
return ResultFs.PermissionDenied.Log();
return _serviceImpl.DeleteAllPaddingFiles();
}
public Result OpenGameCardFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, GameCardHandle handle,
GameCardPartition partitionId)
{
fileSystem = default;
Result rc = GetProgramInfo(out ProgramInfo programInfo);
if (rc.IsFailure()) return rc;
if (!programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountGameCard).CanRead)
return ResultFs.PermissionDenied.Log();
ReferenceCountedDisposable<IFileSystem> fs = null;
try
{
rc = _serviceImpl.OpenGameCardFileSystem(out fs, handle, partitionId);
if (rc.IsFailure()) return rc;
// Create an SF adapter for the file system
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref fs);
return Result.Success;
}
finally
{
fs?.Dispose();
}
}
public Result OpenSdCardFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem)
{
fileSystem = default;
Result rc = GetProgramInfo(out ProgramInfo programInfo);
if (rc.IsFailure()) return rc;
Accessibility accessibility = programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountSdCard);
if (!accessibility.CanRead || !accessibility.CanWrite)
return ResultFs.PermissionDenied.Log();
ReferenceCountedDisposable<IFileSystem> fs = null;
try
{
rc = _serviceImpl.OpenSdCardProxyFileSystem(out fs);
if (rc.IsFailure()) return rc;
// Create an SF adapter for the file system
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref fs);
return Result.Success;
}
finally
{
fs?.Dispose();
}
}
public Result FormatSdCardFileSystem()
{
// Caller must have the FormatSdCard permission
Result rc = GetProgramInfo(out ProgramInfo programInfo);
if (rc.IsFailure()) return rc;
if (!programInfo.AccessControl.CanCall(OperationType.FormatSdCard))
return ResultFs.PermissionDenied.Log();
return _serviceImpl.FormatSdCardProxyFileSystem();
}
public Result FormatSdCardDryRun()
{
// No permissions are needed to call this method
return _serviceImpl.FormatSdCardProxyFileSystem();
}
public Result IsExFatSupported(out bool isSupported)
{
// No permissions are needed to call this method
isSupported = _serviceImpl.IsExFatSupported();
return Result.Success;
}
public Result OpenImageDirectoryFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem,
ImageDirectoryId directoryId)
{
fileSystem = default;
// Caller must have the MountImageAndVideoStorage permission
Result rc = GetProgramInfo(out ProgramInfo programInfo);
if (rc.IsFailure()) return rc;
Accessibility accessibility =
programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountImageAndVideoStorage);
if (!accessibility.CanRead || !accessibility.CanWrite)
return ResultFs.PermissionDenied.Log();
// Get the base file system ID
int id;
switch (directoryId)
{
case ImageDirectoryId.Nand:
id = 0;
break;
case ImageDirectoryId.SdCard:
id = 1;
break;
default:
return ResultFs.InvalidArgument.Log();
}
ReferenceCountedDisposable<IFileSystem> fs = null;
try
{
rc = _serviceImpl.OpenBaseFileSystem(out fs, id);
if (rc.IsFailure()) return rc;
// Create an SF adapter for the file system
fileSystem = FileSystemInterfaceAdapter.CreateShared(ref fs);
return Result.Success;
}
finally
{
fs?.Dispose();
}
}
public Result OpenBisWiper(out ReferenceCountedDisposable<IWiper> bisWiper, NativeHandle transferMemoryHandle, ulong transferMemorySize)
{
bisWiper = default;
// Caller must have the OpenBisWiper permission
Result rc = GetProgramInfo(out ProgramInfo programInfo);
if (rc.IsFailure()) return rc;
if (!programInfo.AccessControl.CanCall(OperationType.OpenBisWiper))
return ResultFs.PermissionDenied.Log();
rc = _serviceImpl.OpenBisWiper(out IWiper wiper, transferMemoryHandle, transferMemorySize);
if (rc.IsFailure()) return rc;
bisWiper = new ReferenceCountedDisposable<IWiper>(wiper);
return Result.Success;
}
private Result GetProgramInfo(out ProgramInfo programInfo)
{
return _serviceImpl.GetProgramInfo(out programInfo, _processId);
}
}
}

View File

@ -0,0 +1,133 @@
using System;
using LibHac.Common;
using LibHac.Fs;
using LibHac.FsSrv.Creators;
using LibHac.FsSrv.Impl;
using LibHac.FsSrv.Sf;
using LibHac.Sf;
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
namespace LibHac.FsSrv
{
public class BaseFileSystemServiceImpl
{
private Configuration _config;
public delegate Result BisWiperCreator(out IWiper wiper, NativeHandle transferMemoryHandle,
ulong transferMemorySize);
public BaseFileSystemServiceImpl(in Configuration configuration)
{
_config = configuration;
}
public struct Configuration
{
public IBuiltInStorageFileSystemCreator BisFileSystemCreator;
public IGameCardFileSystemCreator GameCardFileSystemCreator;
public ISdCardProxyFileSystemCreator SdCardFileSystemCreator;
// CurrentTimeFunction
// FatFileSystemCacheManager
// AlbumDirectoryFileSystemManager
public BisWiperCreator BisWiperCreator;
// Note: The program registry service is global as of FS 10.0.0
public ProgramRegistryImpl ProgramRegistry;
}
public Result OpenBaseFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, int fileSystemId)
{
throw new NotImplementedException();
}
public Result OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, U8Span rootPath,
BisPartitionId partitionId)
{
return OpenBisFileSystem(out fileSystem, rootPath, partitionId, false);
}
public Result OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, U8Span rootPath,
BisPartitionId partitionId, bool caseSensitive)
{
fileSystem = default;
Result rc = _config.BisFileSystemCreator.Create(out IFileSystem fs, rootPath.ToString(), partitionId);
if (rc.IsFailure()) return rc;
fileSystem = new ReferenceCountedDisposable<IFileSystem>(fs);
return Result.Success;
}
public Result CreatePaddingFile(long size)
{
throw new NotImplementedException();
}
public Result DeleteAllPaddingFiles()
{
throw new NotImplementedException();
}
public Result OpenGameCardFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, GameCardHandle handle,
GameCardPartition partitionId)
{
Result rc;
int tries = 0;
do
{
rc = _config.GameCardFileSystemCreator.Create(out fileSystem, handle, partitionId);
if (!ResultFs.DataCorrupted.Includes(rc))
break;
tries++;
} while (tries < 2);
return rc;
}
public Result OpenSdCardProxyFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem)
{
return OpenSdCardProxyFileSystem(out fileSystem, false);
}
public Result OpenSdCardProxyFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, bool openCaseSensitive)
{
fileSystem = default;
// Todo: Shared
Result rc = _config.SdCardFileSystemCreator.Create(out IFileSystem fs, openCaseSensitive);
if (rc.IsFailure()) return rc;
fileSystem = new ReferenceCountedDisposable<IFileSystem>(fs);
return Result.Success;
}
public Result FormatSdCardProxyFileSystem()
{
return _config.SdCardFileSystemCreator.Format();
}
public Result FormatSdCardDryRun()
{
throw new NotImplementedException();
}
public bool IsExFatSupported()
{
// Returning false should probably be fine
return false;
}
public Result OpenBisWiper(out IWiper wiper, NativeHandle transferMemoryHandle, ulong transferMemorySize)
{
return _config.BisWiperCreator(out wiper, transferMemoryHandle, transferMemorySize);
}
internal Result GetProgramInfo(out ProgramInfo programInfo, ulong processId)
{
return _config.ProgramRegistry.GetProgramInfo(out programInfo, processId);
}
}
}

View File

@ -0,0 +1,240 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Fs;
using LibHac.FsSrv.Creators;
using LibHac.FsSrv.Impl;
using LibHac.FsSrv.Sf;
using LibHac.FsSystem;
using IStorage = LibHac.Fs.IStorage;
using IStorageSf = LibHac.FsSrv.Sf.IStorage;
namespace LibHac.FsSrv
{
public readonly struct BaseStorageService
{
private readonly BaseStorageServiceImpl _serviceImpl;
private readonly ulong _processId;
public BaseStorageService(BaseStorageServiceImpl serviceImpl, ulong processId)
{
_serviceImpl = serviceImpl;
_processId = processId;
}
public Result OpenBisStorage(out ReferenceCountedDisposable<IStorageSf> storage, BisPartitionId id)
{
storage = default;
var storageFlag = StorageType.Bis;
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(storageFlag);
Result rc = GetProgramInfo(out ProgramInfo programInfo);
if (rc.IsFailure()) return rc;
rc = GetAccessibilityForOpenBisPartition(out Accessibility accessibility, programInfo, id);
if (rc.IsFailure()) return rc;
bool canAccess = accessibility.CanRead && accessibility.CanWrite;
if (!canAccess)
return ResultFs.PermissionDenied.Log();
ReferenceCountedDisposable<IStorage> tempStorage = null;
try
{
rc = _serviceImpl.OpenBisStorage(out tempStorage, id);
if (rc.IsFailure()) return rc;
tempStorage = StorageLayoutTypeSetStorage.CreateShared(ref tempStorage, storageFlag);
// Todo: Async storage
storage = StorageInterfaceAdapter.CreateShared(ref tempStorage);
return Result.Success;
}
finally
{
tempStorage?.Dispose();
}
}
public Result InvalidateBisCache()
{
Result rc = GetProgramInfo(out ProgramInfo programInfo);
if (rc.IsFailure()) return rc;
if (!programInfo.AccessControl.CanCall(OperationType.InvalidateBisCache))
return ResultFs.PermissionDenied.Log();
return _serviceImpl.InvalidateBisCache();
}
public Result OpenGameCardStorage(out ReferenceCountedDisposable<IStorageSf> storage, GameCardHandle handle,
GameCardPartitionRaw partitionId)
{
storage = default;
Result rc = GetProgramInfo(out ProgramInfo programInfo);
if (rc.IsFailure()) return rc;
Accessibility accessibility =
programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.OpenGameCardStorage);
bool canAccess = accessibility.CanRead && accessibility.CanWrite;
if (!canAccess)
return ResultFs.PermissionDenied.Log();
ReferenceCountedDisposable<IStorage> tempStorage = null;
try
{
rc = _serviceImpl.OpenGameCardPartition(out tempStorage, handle, partitionId);
if (rc.IsFailure()) return rc;
// Todo: Async storage
storage = StorageInterfaceAdapter.CreateShared(ref tempStorage);
return Result.Success;
}
finally
{
tempStorage?.Dispose();
}
}
public Result OpenDeviceOperator(out ReferenceCountedDisposable<IDeviceOperator> deviceOperator)
{
deviceOperator = _serviceImpl.Config.DeviceOperator.AddReference();
return Result.Success;
}
public Result OpenSdCardDetectionEventNotifier(out ReferenceCountedDisposable<IEventNotifier> eventNotifier)
{
eventNotifier = default;
Result rc = GetProgramInfo(out ProgramInfo programInfo);
if (rc.IsFailure()) return rc;
if (!programInfo.AccessControl.CanCall(OperationType.OpenSdCardDetectionEventNotifier))
return ResultFs.PermissionDenied.Log();
throw new NotImplementedException();
}
public Result OpenGameCardDetectionEventNotifier(out ReferenceCountedDisposable<IEventNotifier> eventNotifier)
{
eventNotifier = default;
Result rc = GetProgramInfo(out ProgramInfo programInfo);
if (rc.IsFailure()) return rc;
if (!programInfo.AccessControl.CanCall(OperationType.OpenGameCardDetectionEventNotifier))
return ResultFs.PermissionDenied.Log();
throw new NotImplementedException();
}
public Result SimulateDeviceDetectionEvent(SdmmcPort port, SimulatingDeviceDetectionMode mode, bool signalEvent)
{
Result rc = GetProgramInfo(out ProgramInfo programInfo);
if (rc.IsFailure()) return rc;
if (!programInfo.AccessControl.CanCall(OperationType.SimulateDevice))
return ResultFs.PermissionDenied.Log();
throw new NotImplementedException();
}
private Result GetProgramInfo(out ProgramInfo programInfo)
{
return _serviceImpl.GetProgramInfo(out programInfo, _processId);
}
private Result GetAccessibilityForOpenBisPartition(out Accessibility accessibility, ProgramInfo programInfo,
BisPartitionId partitionId)
{
Unsafe.SkipInit(out accessibility);
AccessibilityType type = partitionId switch
{
BisPartitionId.BootPartition1Root => AccessibilityType.OpenBisPartitionBootPartition1Root,
BisPartitionId.BootPartition2Root => AccessibilityType.OpenBisPartitionBootPartition2Root,
BisPartitionId.UserDataRoot => AccessibilityType.OpenBisPartitionUserDataRoot,
BisPartitionId.BootConfigAndPackage2Part1 => AccessibilityType.OpenBisPartitionBootConfigAndPackage2Part1,
BisPartitionId.BootConfigAndPackage2Part2 => AccessibilityType.OpenBisPartitionBootConfigAndPackage2Part2,
BisPartitionId.BootConfigAndPackage2Part3 => AccessibilityType.OpenBisPartitionBootConfigAndPackage2Part3,
BisPartitionId.BootConfigAndPackage2Part4 => AccessibilityType.OpenBisPartitionBootConfigAndPackage2Part4,
BisPartitionId.BootConfigAndPackage2Part5 => AccessibilityType.OpenBisPartitionBootConfigAndPackage2Part5,
BisPartitionId.BootConfigAndPackage2Part6 => AccessibilityType.OpenBisPartitionBootConfigAndPackage2Part6,
BisPartitionId.CalibrationBinary => AccessibilityType.OpenBisPartitionCalibrationBinary,
BisPartitionId.CalibrationFile => AccessibilityType.OpenBisPartitionCalibrationFile,
BisPartitionId.SafeMode => AccessibilityType.OpenBisPartitionSafeMode,
BisPartitionId.User => AccessibilityType.OpenBisPartitionUser,
BisPartitionId.System => AccessibilityType.OpenBisPartitionSystem,
BisPartitionId.SystemProperEncryption => AccessibilityType.OpenBisPartitionSystemProperEncryption,
BisPartitionId.SystemProperPartition => AccessibilityType.OpenBisPartitionSystemProperPartition,
_ => (AccessibilityType)(-1)
};
if (type == (AccessibilityType)(-1))
return ResultFs.InvalidArgument.Log();
accessibility = programInfo.AccessControl.GetAccessibilityFor(type);
return Result.Success;
}
}
public class BaseStorageServiceImpl
{
internal Configuration Config;
public BaseStorageServiceImpl(in Configuration configuration)
{
Config = configuration;
}
public struct Configuration
{
public IBuiltInStorageCreator BisStorageCreator;
public IGameCardStorageCreator GameCardStorageCreator;
// LibHac additions
public ProgramRegistryImpl ProgramRegistry;
// Todo: The DeviceOperator in FS uses mostly global state. Decide how to handle this.
public ReferenceCountedDisposable<IDeviceOperator> DeviceOperator;
}
public Result OpenBisStorage(out ReferenceCountedDisposable<IStorage> storage, BisPartitionId partitionId)
{
return Config.BisStorageCreator.Create(out storage, partitionId);
}
public Result InvalidateBisCache()
{
return Config.BisStorageCreator.InvalidateCache();
}
public Result OpenGameCardPartition(out ReferenceCountedDisposable<IStorage> storage, GameCardHandle handle,
GameCardPartitionRaw partitionId)
{
switch (partitionId)
{
case GameCardPartitionRaw.NormalReadOnly:
return Config.GameCardStorageCreator.CreateReadOnly(handle, out storage);
case GameCardPartitionRaw.SecureReadOnly:
return Config.GameCardStorageCreator.CreateSecureReadOnly(handle, out storage);
case GameCardPartitionRaw.RootWriteOnly:
return Config.GameCardStorageCreator.CreateWriteOnly(handle, out storage);
default:
storage = default;
return ResultFs.InvalidArgument.Log();
}
}
internal Result GetProgramInfo(out ProgramInfo programInfo, ulong processId)
{
return Config.ProgramRegistry.GetProgramInfo(out programInfo, processId);
}
}
}

View File

@ -1,6 +1,8 @@
using System.Diagnostics;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSrv.Impl;
namespace LibHac.FsSrv.Creators
{
@ -85,6 +87,53 @@ namespace LibHac.FsSrv.Creators
return Util.CreateSubFileSystemImpl(out fileSystem, subFileSystem, rootPath);
}
// Todo: Make case sensitive
public Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, U8Span rootPath,
BisPartitionId partitionId, bool caseSensitive)
{
fileSystem = default;
if (!IsValidPartitionId(partitionId)) return ResultFs.InvalidArgument.Log();
if (rootPath.IsNull()) return ResultFs.NullptrArgument.Log();
if (Config.TryGetFileSystem(out IFileSystem fs, partitionId))
{
fileSystem = new ReferenceCountedDisposable<IFileSystem>(fs);
return Result.Success;
}
if (Config.RootFileSystem == null)
{
return ResultFs.PreconditionViolation.Log();
}
var partitionPath = GetPartitionPath(partitionId).ToU8String();
ReferenceCountedDisposable<IFileSystem> partitionFileSystem = null;
ReferenceCountedDisposable<IFileSystem> sharedRootFs = null;
try
{
sharedRootFs = new ReferenceCountedDisposable<IFileSystem>(Config.RootFileSystem);
Result rc = Utility.WrapSubDirectory(out partitionFileSystem, ref sharedRootFs, partitionPath, true);
if (rc.IsFailure()) return rc;
if (rootPath.IsEmpty())
{
Shared.Move(out fileSystem, ref partitionFileSystem);
return Result.Success;
}
return Utility.CreateSubDirectoryFileSystem(out fileSystem, ref partitionFileSystem, rootPath);
}
finally
{
partitionFileSystem?.Dispose();
sharedRootFs?.Dispose();
}
}
public Result CreateFatFileSystem(out IFileSystem fileSystem, BisPartitionId partitionId)
{
fileSystem = default;

View File

@ -14,6 +14,7 @@ namespace LibHac.FsSrv.Creators
StorageCreator = storageCreator;
GameCard = gameCard;
}
public Result Create(out IFileSystem fileSystem, GameCardHandle handle, GameCardPartition partitionType)
{
// Use the old xci code temporarily
@ -31,5 +32,24 @@ namespace LibHac.FsSrv.Creators
fileSystem = xci.OpenPartition((XciPartitionType)partitionType);
return Result.Success;
}
public Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, GameCardHandle handle, GameCardPartition partitionType)
{
// Use the old xci code temporarily
fileSystem = default;
Result rc = GameCard.GetXci(out Xci xci, handle);
if (rc.IsFailure()) return rc;
if (!xci.HasPartition((XciPartitionType)partitionType))
{
return ResultFs.PartitionNotFound.Log();
}
XciPartition fs = xci.OpenPartition((XciPartitionType)partitionType);
fileSystem = new ReferenceCountedDisposable<IFileSystem>(fs);
return Result.Success;
}
}
}

View File

@ -12,7 +12,7 @@ namespace LibHac.FsSrv.Creators
GameCard = gameCard;
}
public Result CreateNormal(GameCardHandle handle, out IStorage storage)
public Result CreateReadOnly(GameCardHandle handle, out ReferenceCountedDisposable<IStorage> storage)
{
storage = default;
@ -21,16 +21,17 @@ namespace LibHac.FsSrv.Creators
return ResultFs.InvalidGameCardHandleOnOpenNormalPartition.Log();
}
var baseStorage = new ReadOnlyGameCardStorage(GameCard, handle);
var baseStorage = new ReferenceCountedDisposable<IStorage>(new ReadOnlyGameCardStorage(GameCard, handle));
Result rc = GameCard.GetCardInfo(out GameCardInfo cardInfo, handle);
if (rc.IsFailure()) return rc;
storage = new SubStorage(baseStorage, 0, cardInfo.SecureAreaOffset);
storage = new ReferenceCountedDisposable<IStorage>(
new SubStorage(baseStorage, 0, cardInfo.SecureAreaOffset));
return Result.Success;
}
public Result CreateSecure(GameCardHandle handle, out IStorage storage)
public Result CreateSecureReadOnly(GameCardHandle handle, out ReferenceCountedDisposable<IStorage> storage)
{
storage = default;
@ -48,16 +49,19 @@ namespace LibHac.FsSrv.Creators
rc = GameCard.GetGameCardImageHash(imageHash);
if (rc.IsFailure()) return rc;
var baseStorage = new ReadOnlyGameCardStorage(GameCard, handle, deviceId, imageHash);
var baseStorage =
new ReferenceCountedDisposable<IStorage>(new ReadOnlyGameCardStorage(GameCard, handle, deviceId,
imageHash));
rc = GameCard.GetCardInfo(out GameCardInfo cardInfo, handle);
if (rc.IsFailure()) return rc;
storage = new SubStorage(baseStorage, cardInfo.SecureAreaOffset, cardInfo.SecureAreaSize);
storage = new ReferenceCountedDisposable<IStorage>(new SubStorage(baseStorage, cardInfo.SecureAreaOffset,
cardInfo.SecureAreaSize));
return Result.Success;
}
public Result CreateWritable(GameCardHandle handle, out IStorage storage)
public Result CreateWriteOnly(GameCardHandle handle, out ReferenceCountedDisposable<IStorage> storage)
{
throw new NotImplementedException();
}

View File

@ -4,7 +4,7 @@ using LibHac.Fs.Fsa;
namespace LibHac.FsSrv.Creators
{
public class EmulatedSdFileSystemCreator : ISdFileSystemCreator
public class EmulatedSdCardFileSystemCreator : ISdCardProxyFileSystemCreator
{
private const string DefaultPath = "/sdcard";
@ -14,20 +14,20 @@ namespace LibHac.FsSrv.Creators
private IFileSystem SdCardFileSystem { get; set; }
public EmulatedSdFileSystemCreator(EmulatedSdCard sdCard, IFileSystem rootFileSystem)
public EmulatedSdCardFileSystemCreator(EmulatedSdCard sdCard, IFileSystem rootFileSystem)
{
SdCard = sdCard;
RootFileSystem = rootFileSystem;
}
public EmulatedSdFileSystemCreator(EmulatedSdCard sdCard, IFileSystem rootFileSystem, string path)
public EmulatedSdCardFileSystemCreator(EmulatedSdCard sdCard, IFileSystem rootFileSystem, string path)
{
SdCard = sdCard;
RootFileSystem = rootFileSystem;
Path = path;
}
public Result Create(out IFileSystem fileSystem)
public Result Create(out IFileSystem fileSystem, bool isCaseSensitive)
{
fileSystem = default;
@ -61,7 +61,12 @@ namespace LibHac.FsSrv.Creators
return Result.Success;
}
public Result Format(bool closeOpenEntries)
public Result Format(bool removeFromFatFsCache)
{
throw new NotImplementedException();
}
public Result Format()
{
throw new NotImplementedException();
}

View File

@ -29,7 +29,27 @@ namespace LibHac.FsSrv.Creators
KeySet.SetSdSeed(encryptionSeed.ToArray());
encryptedFileSystem = new AesXtsFileSystem(baseFileSystem,
KeySet.SdCardEncryptionKeys[(int) keyId].DataRo.ToArray(), 0x4000);
KeySet.SdCardEncryptionKeys[(int)keyId].DataRo.ToArray(), 0x4000);
return Result.Success;
}
public Result Create(out ReferenceCountedDisposable<IFileSystem> encryptedFileSystem, ReferenceCountedDisposable<IFileSystem> baseFileSystem,
EncryptedFsKeyId keyId, in EncryptionSeed encryptionSeed)
{
encryptedFileSystem = default;
if (keyId < EncryptedFsKeyId.Save || keyId > EncryptedFsKeyId.CustomStorage)
{
return ResultFs.InvalidArgument.Log();
}
// todo: "proper" key generation instead of a lazy hack
KeySet.SetSdSeed(encryptionSeed.Value);
// Todo: pass ReferenceCountedDisposable to AesXtsFileSystem
var fs = new AesXtsFileSystem(baseFileSystem, KeySet.SdCardEncryptionKeys[(int)keyId].DataRo.ToArray(), 0x4000);
encryptedFileSystem = new ReferenceCountedDisposable<IFileSystem>(fs);
return Result.Success;
}

View File

@ -17,6 +17,6 @@
public IEncryptedFileSystemCreator EncryptedFileSystemCreator { get; set; }
public IMemoryStorageCreator MemoryStorageCreator { get; set; }
public IBuiltInStorageFileSystemCreator BuiltInStorageFileSystemCreator { get; set; }
public ISdFileSystemCreator SdFileSystemCreator { get; set; }
public ISdCardProxyFileSystemCreator SdCardFileSystemCreator { get; set; }
}
}

View File

@ -4,6 +4,7 @@ namespace LibHac.FsSrv.Creators
{
public interface IBuiltInStorageCreator
{
Result Create(out IStorage storage, BisPartitionId partitionId);
Result Create(out ReferenceCountedDisposable<IStorage> storage, BisPartitionId partitionId);
Result InvalidateCache();
}
}

View File

@ -1,11 +1,14 @@
using LibHac.Fs;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
namespace LibHac.FsSrv.Creators
{
public interface IBuiltInStorageFileSystemCreator
{
// Todo: Remove raw IFileSystem overload
Result Create(out IFileSystem fileSystem, string rootPath, BisPartitionId partitionId);
Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, U8Span rootPath, BisPartitionId partitionId, bool caseSensitive);
Result CreateFatFileSystem(out IFileSystem fileSystem, BisPartitionId partitionId);
Result SetBisRootForHost(BisPartitionId partitionId, string rootPath);
}

View File

@ -1,12 +1,18 @@
using System;
using LibHac.Fs;
using LibHac.Fs.Fsa;
namespace LibHac.FsSrv.Creators
{
public interface IEncryptedFileSystemCreator
{
// Todo: remove the function using raw IFileSystems
Result Create(out IFileSystem encryptedFileSystem, IFileSystem baseFileSystem, EncryptedFsKeyId keyId,
ReadOnlySpan<byte> encryptionSeed);
Result Create(out ReferenceCountedDisposable<IFileSystem> encryptedFileSystem,
ReferenceCountedDisposable<IFileSystem> baseFileSystem, EncryptedFsKeyId keyId,
in EncryptionSeed encryptionSeed);
}
public enum EncryptedFsKeyId

View File

@ -6,5 +6,6 @@ namespace LibHac.FsSrv.Creators
public interface IGameCardFileSystemCreator
{
Result Create(out IFileSystem fileSystem, GameCardHandle handle, GameCardPartition partitionType);
Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, GameCardHandle handle, GameCardPartition partitionType);
}
}

View File

@ -4,8 +4,8 @@ namespace LibHac.FsSrv.Creators
{
public interface IGameCardStorageCreator
{
Result CreateNormal(GameCardHandle handle, out IStorage storage);
Result CreateSecure(GameCardHandle handle, out IStorage storage);
Result CreateWritable(GameCardHandle handle, out IStorage storage);
Result CreateReadOnly(GameCardHandle handle, out ReferenceCountedDisposable<IStorage> storage);
Result CreateSecureReadOnly(GameCardHandle handle, out ReferenceCountedDisposable<IStorage> storage);
Result CreateWriteOnly(GameCardHandle handle, out ReferenceCountedDisposable<IStorage> storage);
}
}

View File

@ -5,6 +5,8 @@ namespace LibHac.FsSrv.Creators
{
public interface IPartitionFileSystemCreator
{
// Todo: Remove non-shared overload
Result Create(out IFileSystem fileSystem, IStorage pFsStorage);
Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, ReferenceCountedDisposable<IStorage> pFsStorage);
}
}

View File

@ -5,6 +5,6 @@ namespace LibHac.FsSrv.Creators
{
public interface IRomFileSystemCreator
{
Result Create(out IFileSystem fileSystem, IStorage romFsStorage);
Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, ReferenceCountedDisposable<IStorage> romFsStorage);
}
}

View File

@ -2,7 +2,7 @@
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem.Save;
using LibHac.FsSystem;
namespace LibHac.FsSrv.Creators
{
@ -10,9 +10,10 @@ namespace LibHac.FsSrv.Creators
{
Result CreateFile(out IFile file, IFileSystem sourceFileSystem, ulong saveDataId, OpenMode openMode);
Result Create(out IFileSystem fileSystem, out ISaveDataExtraDataAccessor extraDataAccessor,
IFileSystem sourceFileSystem, ulong saveDataId, bool allowDirectorySaveData, bool useDeviceUniqueMac,
SaveDataType type, ITimeStampGenerator timeStampGenerator);
Result Create(out IFileSystem fileSystem,
out ReferenceCountedDisposable<ISaveDataExtraDataAccessor> extraDataAccessor, IFileSystem sourceFileSystem,
ulong saveDataId, bool allowDirectorySaveData, bool useDeviceUniqueMac, SaveDataType type,
ITimeStampGenerator timeStampGenerator);
void SetSdCardEncryptionSeed(ReadOnlySpan<byte> seed);
}

View File

@ -0,0 +1,23 @@
using LibHac.Fs.Fsa;
namespace LibHac.FsSrv.Creators
{
public interface ISdCardProxyFileSystemCreator
{
Result Create(out IFileSystem fileSystem, bool isCaseSensitive);
/// <summary>
/// Formats the SD card.
/// </summary>
/// <param name="removeFromFatFsCache">Should the SD card file system be removed from the
/// FAT file system cache?</param>
/// <returns>The <see cref="Result"/> of the operation.</returns>
Result Format(bool removeFromFatFsCache);
/// <summary>
/// Automatically closes all open proxy file system entries and formats the SD card.
/// </summary>
/// <returns>The <see cref="Result"/> of the operation.</returns>
Result Format();
}
}

View File

@ -1,10 +0,0 @@
using LibHac.Fs.Fsa;
namespace LibHac.FsSrv.Creators
{
public interface ISdFileSystemCreator
{
Result Create(out IFileSystem fileSystem);
Result Format(bool closeOpenEntries);
}
}

View File

@ -6,8 +6,8 @@ namespace LibHac.FsSrv.Creators
{
public interface IStorageOnNcaCreator
{
Result Create(out IStorage storage, out NcaFsHeader fsHeader, Nca nca, int fsIndex, bool isCodeFs);
Result CreateWithPatch(out IStorage storage, out NcaFsHeader fsHeader, Nca baseNca, Nca patchNca, int fsIndex, bool isCodeFs);
Result Create(out ReferenceCountedDisposable<IStorage> storage, out NcaFsHeader fsHeader, Nca nca, int fsIndex, bool isCodeFs);
Result CreateWithPatch(out ReferenceCountedDisposable<IStorage> storage, out NcaFsHeader fsHeader, Nca baseNca, Nca patchNca, int fsIndex, bool isCodeFs);
Result OpenNca(out Nca nca, IStorage ncaStorage);
Result VerifyAcidSignature(IFileSystem codeFileSystem, Nca nca);
}

View File

@ -5,7 +5,7 @@ namespace LibHac.FsSrv.Creators
{
public interface ISubDirectoryFileSystemCreator
{
Result Create(out IFileSystem subDirFileSystem, IFileSystem baseFileSystem, U8Span path);
Result Create(out IFileSystem subDirFileSystem, IFileSystem baseFileSystem, U8Span path, bool preserveUnc);
Result Create(out ReferenceCountedDisposable<IFileSystem> subDirFileSystem, ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path);
Result Create(out ReferenceCountedDisposable<IFileSystem> subDirFileSystem, ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path, bool preserveUnc);
}
}

View File

@ -5,7 +5,9 @@ namespace LibHac.FsSrv.Creators
{
public interface ITargetManagerFileSystemCreator
{
// Todo: Remove raw IFilesystem function
Result Create(out IFileSystem fileSystem, bool openCaseSensitive);
Result GetCaseSensitivePath(out bool isSuccess, Span<byte> path);
Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, bool openCaseSensitive);
Result NormalizeCaseOfPath(out bool isSupported, Span<byte> path);
}
}

View File

@ -21,5 +21,20 @@ namespace LibHac.FsSrv.Creators
fileSystem = partitionFs;
return Result.Success;
}
public Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, ReferenceCountedDisposable<IStorage> pFsStorage)
{
var partitionFs = new PartitionFileSystemCore<StandardEntry>();
Result rc = partitionFs.Initialize(pFsStorage);
if (rc.IsFailure())
{
fileSystem = default;
return rc;
}
fileSystem = new ReferenceCountedDisposable<IFileSystem>(partitionFs);
return Result.Success;
}
}
}

View File

@ -7,9 +7,10 @@ namespace LibHac.FsSrv.Creators
public class RomFileSystemCreator : IRomFileSystemCreator
{
// todo: Implement properly
public Result Create(out IFileSystem fileSystem, IStorage romFsStorage)
public Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, ReferenceCountedDisposable<IStorage> romFsStorage)
{
fileSystem = new RomFsFileSystem(romFsStorage);
// Todo: Properly use shared references
fileSystem = new ReferenceCountedDisposable<IFileSystem>(new RomFsFileSystem(romFsStorage.AddReference().Target));
return Result.Success;
}
}

View File

@ -22,9 +22,10 @@ namespace LibHac.FsSrv.Creators
throw new NotImplementedException();
}
public Result Create(out IFileSystem fileSystem, out ISaveDataExtraDataAccessor extraDataAccessor,
IFileSystem sourceFileSystem, ulong saveDataId, bool allowDirectorySaveData, bool useDeviceUniqueMac,
SaveDataType type, ITimeStampGenerator timeStampGenerator)
public Result Create(out IFileSystem fileSystem,
out ReferenceCountedDisposable<ISaveDataExtraDataAccessor> extraDataAccessor, IFileSystem sourceFileSystem,
ulong saveDataId, bool allowDirectorySaveData, bool useDeviceUniqueMac, SaveDataType type,
ITimeStampGenerator timeStampGenerator)
{
fileSystem = default;
extraDataAccessor = default;
@ -42,13 +43,15 @@ namespace LibHac.FsSrv.Creators
case DirectoryEntryType.Directory:
if (!allowDirectorySaveData) return ResultFs.InvalidSaveDataEntryType.Log();
rc = SubdirectoryFileSystem.CreateNew(out SubdirectoryFileSystem subDirFs, sourceFileSystem, saveDataPath);
rc = SubdirectoryFileSystem.CreateNew(out SubdirectoryFileSystem subDirFs, sourceFileSystem,
saveDataPath);
if (rc.IsFailure()) return rc;
bool isPersistentSaveData = type != SaveDataType.Temporary;
bool isUserSaveData = type == SaveDataType.Account || type == SaveDataType.Device;
rc = DirectorySaveDataFileSystem.CreateNew(out DirectorySaveDataFileSystem saveFs, subDirFs, isPersistentSaveData, isUserSaveData);
rc = DirectorySaveDataFileSystem.CreateNew(out DirectorySaveDataFileSystem saveFs, subDirFs,
isPersistentSaveData, isUserSaveData);
if (rc.IsFailure()) return rc;
fileSystem = saveFs;
@ -62,7 +65,8 @@ namespace LibHac.FsSrv.Creators
if (rc.IsFailure()) return rc;
var saveDataStorage = new DisposingFileStorage(saveDataFile);
fileSystem = new SaveDataFileSystem(KeySet, saveDataStorage, IntegrityCheckLevel.ErrorOnInvalid, false);
fileSystem = new SaveDataFileSystem(KeySet, saveDataStorage, IntegrityCheckLevel.ErrorOnInvalid,
false);
// Todo: ISaveDataExtraDataAccessor

View File

@ -20,7 +20,8 @@ namespace LibHac.FsSrv.Creators
}
// todo: Implement NcaReader and other Nca classes
public Result Create(out IStorage storage, out NcaFsHeader fsHeader, Nca nca, int fsIndex, bool isCodeFs)
public Result Create(out ReferenceCountedDisposable<IStorage> storage, out NcaFsHeader fsHeader, Nca nca,
int fsIndex, bool isCodeFs)
{
storage = default;
fsHeader = default;
@ -40,13 +41,14 @@ namespace LibHac.FsSrv.Creators
}
}
storage = storageTemp;
storage = new ReferenceCountedDisposable<IStorage>(storageTemp);
fsHeader = nca.GetFsHeader(fsIndex);
return Result.Success;
}
public Result CreateWithPatch(out IStorage storage, out NcaFsHeader fsHeader, Nca baseNca, Nca patchNca, int fsIndex, bool isCodeFs)
public Result CreateWithPatch(out ReferenceCountedDisposable<IStorage> storage, out NcaFsHeader fsHeader,
Nca baseNca, Nca patchNca, int fsIndex, bool isCodeFs)
{
throw new NotImplementedException();
}

View File

@ -6,21 +6,30 @@ namespace LibHac.FsSrv.Creators
{
public class SubDirectoryFileSystemCreator : ISubDirectoryFileSystemCreator
{
public Result Create(out IFileSystem subDirFileSystem, IFileSystem baseFileSystem, U8Span path)
public Result Create(out ReferenceCountedDisposable<IFileSystem> subDirFileSystem,
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path)
{
return Create(out subDirFileSystem, baseFileSystem, path, false);
return Create(out subDirFileSystem, ref baseFileSystem, path, false);
}
public Result Create(out IFileSystem subDirFileSystem, IFileSystem baseFileSystem, U8Span path, bool preserveUnc)
public Result Create(out ReferenceCountedDisposable<IFileSystem> subDirFileSystem,
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, U8Span path, bool preserveUnc)
{
subDirFileSystem = default;
Result rc = baseFileSystem.OpenDirectory(out IDirectory _, path, OpenDirectoryMode.Directory);
// Verify the sub-path exists
Result rc = baseFileSystem.Target.OpenDirectory(out IDirectory _, path, OpenDirectoryMode.Directory);
if (rc.IsFailure()) return rc;
rc = SubdirectoryFileSystem.CreateNew(out SubdirectoryFileSystem fs, baseFileSystem, path.ToU8String(), preserveUnc);
subDirFileSystem = fs;
return rc;
// Initialize the SubdirectoryFileSystem
var subDir = new SubdirectoryFileSystem(ref baseFileSystem, preserveUnc);
using var subDirShared = new ReferenceCountedDisposable<SubdirectoryFileSystem>(subDir);
rc = subDirShared.Target.Initialize(path);
if (rc.IsFailure()) return rc;
subDirFileSystem = subDirShared.AddReference<IFileSystem>();
return Result.Success;
}
}
}

View File

@ -10,7 +10,12 @@ namespace LibHac.FsSrv.Creators
throw new NotImplementedException();
}
public Result GetCaseSensitivePath(out bool isSuccess, Span<byte> path)
public Result Create(out ReferenceCountedDisposable<IFileSystem> fileSystem, bool openCaseSensitive)
{
throw new NotImplementedException();
}
public Result NormalizeCaseOfPath(out bool isSupported, Span<byte> path)
{
throw new NotImplementedException();
}

View File

@ -1,6 +1,7 @@
using LibHac.Common.Keys;
using LibHac.Fs.Fsa;
using LibHac.FsSrv.Creators;
using LibHac.FsSrv.Sf;
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
namespace LibHac.FsSrv
{
@ -29,7 +30,7 @@ namespace LibHac.FsSrv
creators.GameCardFileSystemCreator = new EmulatedGameCardFsCreator(gcStorageCreator, gameCard);
creators.EncryptedFileSystemCreator = new EncryptedFileSystemCreator(keySet);
creators.BuiltInStorageFileSystemCreator = new EmulatedBisFileSystemCreator(rootFileSystem);
creators.SdFileSystemCreator = new EmulatedSdFileSystemCreator(sdCard, rootFileSystem);
creators.SdCardFileSystemCreator = new EmulatedSdCardFileSystemCreator(sdCard, rootFileSystem);
var deviceOperator = new EmulatedDeviceOperator(gameCard, sdCard);

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

View File

@ -1,4 +1,5 @@
using LibHac.Fs;
using LibHac.FsSrv.Sf;
namespace LibHac.FsSrv
{
@ -13,6 +14,8 @@ namespace LibHac.FsSrv
SdCard = sdCard;
}
public void Dispose() { }
public Result IsSdCardInserted(out bool isInserted)
{
isInserted = SdCard.IsSdCardInserted();

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,12 @@ namespace LibHac.FsSrv
public class FileSystemProxyConfiguration
{
public FileSystemCreators FsCreatorInterfaces { get; set; }
public ProgramRegistryServiceImpl ProgramRegistryServiceImpl { get; set; }
public BaseStorageServiceImpl BaseStorageService { get; set; }
public BaseFileSystemServiceImpl BaseFileSystemService { get; set; }
public NcaFileSystemServiceImpl NcaFileSystemService { get; set; }
public SaveDataFileSystemServiceImpl SaveDataFileSystemService { get; set; }
public TimeServiceImpl TimeService { get; set; }
public ProgramRegistryServiceImpl ProgramRegistryService { get; set; }
public AccessLogServiceImpl AccessLogService { get; set; }
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,128 @@
using System;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.Fs.Shim;
using LibHac.FsSrv.Creators;
namespace LibHac.FsSrv
{
public class FileSystemProxyCoreImpl
{
internal FileSystemProxyConfiguration Config { get; }
private FileSystemCreators FsCreators => Config.FsCreatorInterfaces;
internal ProgramRegistryImpl ProgramRegistry { get; }
private byte[] SdEncryptionSeed { get; } = new byte[0x10];
private const string NintendoDirectoryName = "Nintendo";
public FileSystemProxyCoreImpl(FileSystemProxyConfiguration config)
{
Config = config;
ProgramRegistry = new ProgramRegistryImpl(Config.ProgramRegistryService);
}
public Result OpenCustomStorageFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem,
CustomStorageId storageId)
{
fileSystem = default;
switch (storageId)
{
case CustomStorageId.SdCard:
{
Result rc = FsCreators.SdCardFileSystemCreator.Create(out IFileSystem sdFs, false);
if (rc.IsFailure()) return rc;
string customStorageDir = CustomStorage.GetCustomStorageDirectoryName(CustomStorageId.SdCard);
string subDirName = $"/{NintendoDirectoryName}/{customStorageDir}";
rc = Util.CreateSubFileSystem(out IFileSystem subFs, sdFs, subDirName, true);
if (rc.IsFailure()) return rc;
rc = FsCreators.EncryptedFileSystemCreator.Create(out IFileSystem encryptedFs, subFs,
EncryptedFsKeyId.CustomStorage, SdEncryptionSeed);
if (rc.IsFailure()) return rc;
fileSystem = new ReferenceCountedDisposable<IFileSystem>(encryptedFs);
return Result.Success;
}
case CustomStorageId.System:
{
Result rc = FsCreators.BuiltInStorageFileSystemCreator.Create(out IFileSystem userFs, string.Empty,
BisPartitionId.User);
if (rc.IsFailure()) return rc;
string customStorageDir = CustomStorage.GetCustomStorageDirectoryName(CustomStorageId.System);
string subDirName = $"/{customStorageDir}";
rc = Util.CreateSubFileSystem(out IFileSystem subFs, userFs, subDirName, true);
if (rc.IsFailure()) return rc;
// Todo: Get shared object from earlier functions
fileSystem = new ReferenceCountedDisposable<IFileSystem>(subFs);
return Result.Success;
}
default:
return ResultFs.InvalidArgument.Log();
}
}
public Result OpenHostFileSystem(out ReferenceCountedDisposable<IFileSystem> fileSystem, U8Span path,
bool openCaseSensitive)
{
fileSystem = default;
Result rc;
if (!path.IsEmpty())
{
rc = Util.VerifyHostPath(path);
if (rc.IsFailure()) return rc;
}
// Todo: Return shared fs from Create
rc = FsCreators.TargetManagerFileSystemCreator.Create(out IFileSystem hostFs, openCaseSensitive);
if (rc.IsFailure()) return rc;
ReferenceCountedDisposable<IFileSystem> sharedHostFs = null;
ReferenceCountedDisposable<IFileSystem> subDirFs = null;
try
{
sharedHostFs = new ReferenceCountedDisposable<IFileSystem>(hostFs);
if (path.IsEmpty())
{
ReadOnlySpan<byte> rootHostPath = new[] { (byte)'C', (byte)':', (byte)'/' };
rc = sharedHostFs.Target.GetEntryType(out _, new U8Span(rootHostPath));
// Nintendo ignores all results other than this one
if (ResultFs.TargetNotFound.Includes(rc))
return rc;
Shared.Move(out fileSystem, ref sharedHostFs);
return Result.Success;
}
rc = FsCreators.SubDirectoryFileSystemCreator.Create(out subDirFs, ref sharedHostFs, path,
preserveUnc: true);
if (rc.IsFailure()) return rc;
fileSystem = subDirFs;
return Result.Success;
}
finally
{
sharedHostFs?.Dispose();
subDirFs?.Dispose();
}
}
public Result SetSdCardEncryptionSeed(in EncryptionSeed seed)
{
seed.Value.CopyTo(SdEncryptionSeed);
return Result.Success;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,8 @@ using LibHac.Fs;
using LibHac.Fs.Impl;
using LibHac.Fs.Shim;
using LibHac.FsSrv.Creators;
using LibHac.FsSrv.Impl;
using LibHac.FsSrv.Sf;
using LibHac.Sm;
namespace LibHac.FsSrv
@ -11,7 +13,10 @@ namespace LibHac.FsSrv
{
internal const ulong SaveIndexerId = 0x8000000000000000;
private FileSystemProxyCore FsProxyCore { get; }
private const ulong SpeedEmulationProgramIdMinimum = 0x100000000000000;
private const ulong SpeedEmulationProgramIdMaximum = 0x100000000001FFF;
private FileSystemProxyCoreImpl FsProxyCore { get; }
/// <summary>The client instance to be used for internal operations like save indexer access.</summary>
public HorizonClient Hos { get; }
@ -36,23 +41,23 @@ namespace LibHac.FsSrv
IsDebugMode = false;
ExternalKeySet externalKeySet = config.ExternalKeySet ?? new ExternalKeySet();
Timer = config.TimeSpanGenerator ?? new StopWatchTimeSpanGenerator();
var fspConfig = new FileSystemProxyConfiguration
{
FsCreatorInterfaces = config.FsCreators,
ProgramRegistryServiceImpl = new ProgramRegistryServiceImpl(this)
};
FileSystemProxyConfiguration fspConfig = InitializeFileSystemProxyConfiguration(config);
FsProxyCore = new FileSystemProxyCore(fspConfig, externalKeySet, config.DeviceOperator);
FsProxyCore = new FileSystemProxyCoreImpl(fspConfig);
FsProxyCore.SetSaveDataIndexerManager(new SaveDataIndexerManager(Hos.Fs, SaveIndexerId,
new ArrayPoolMemoryResource(), new SdHandleManager(), false));
FileSystemProxyImpl fsProxy = GetFileSystemProxyServiceObject();
ulong processId = Hos.Os.GetCurrentProcessId().Value;
fsProxy.SetCurrentProcess(processId).IgnoreResult();
FileSystemProxy fsProxy = GetFileSystemProxyServiceObject();
fsProxy.SetCurrentProcess(Hos.Os.GetCurrentProcessId().Value).IgnoreResult();
fsProxy.CleanUpTemporaryStorage().IgnoreResult();
var saveService = new SaveDataFileSystemService(fspConfig.SaveDataFileSystemService, processId);
saveService.CleanUpTemporaryStorage().IgnoreResult();
saveService.CleanUpSaveData().IgnoreResult();
saveService.CompleteSaveDataExtension().IgnoreResult();
saveService.FixSaveData().IgnoreResult();
saveService.RecoverMultiCommit().IgnoreResult();
Hos.Sm.RegisterService(new FileSystemProxyService(this), "fsp-srv").IgnoreResult();
Hos.Sm.RegisterService(new FileSystemProxyForLoaderService(this), "fsp-ldr").IgnoreResult();
@ -81,19 +86,100 @@ namespace LibHac.FsSrv
return new FileSystemClient(Hos);
}
private FileSystemProxy GetFileSystemProxyServiceObject()
private FileSystemProxyConfiguration InitializeFileSystemProxyConfiguration(FileSystemServerConfig config)
{
return new FileSystemProxy(Hos, FsProxyCore);
var saveDataIndexerManager = new SaveDataIndexerManager(Hos.Fs, SaveIndexerId,
new ArrayPoolMemoryResource(), new SdHandleManager(), false);
var programRegistryService = new ProgramRegistryServiceImpl(this);
var programRegistry = new ProgramRegistryImpl(programRegistryService);
var baseStorageConfig = new BaseStorageServiceImpl.Configuration();
baseStorageConfig.BisStorageCreator = config.FsCreators.BuiltInStorageCreator;
baseStorageConfig.GameCardStorageCreator = config.FsCreators.GameCardStorageCreator;
baseStorageConfig.ProgramRegistry = programRegistry;
baseStorageConfig.DeviceOperator = new ReferenceCountedDisposable<IDeviceOperator>(config.DeviceOperator);
var baseStorageService = new BaseStorageServiceImpl(in baseStorageConfig);
var timeServiceConfig = new TimeServiceImpl.Configuration();
timeServiceConfig.HorizonClient = Hos;
timeServiceConfig.ProgramRegistry = programRegistry;
var timeService = new TimeServiceImpl(in timeServiceConfig);
var baseFsServiceConfig = new BaseFileSystemServiceImpl.Configuration();
baseFsServiceConfig.BisFileSystemCreator = config.FsCreators.BuiltInStorageFileSystemCreator;
baseFsServiceConfig.GameCardFileSystemCreator = config.FsCreators.GameCardFileSystemCreator;
baseFsServiceConfig.SdCardFileSystemCreator = config.FsCreators.SdCardFileSystemCreator;
baseFsServiceConfig.BisWiperCreator = BisWiper.CreateWiper;
baseFsServiceConfig.ProgramRegistry = programRegistry;
var baseFsService = new BaseFileSystemServiceImpl(in baseFsServiceConfig);
var ncaFsServiceConfig = new NcaFileSystemServiceImpl.Configuration();
ncaFsServiceConfig.BaseFsService = baseFsService;
ncaFsServiceConfig.HostFsCreator = config.FsCreators.HostFileSystemCreator;
ncaFsServiceConfig.TargetManagerFsCreator = config.FsCreators.TargetManagerFileSystemCreator;
ncaFsServiceConfig.PartitionFsCreator = config.FsCreators.PartitionFileSystemCreator;
ncaFsServiceConfig.RomFsCreator = config.FsCreators.RomFileSystemCreator;
ncaFsServiceConfig.StorageOnNcaCreator = config.FsCreators.StorageOnNcaCreator;
ncaFsServiceConfig.SubDirectoryFsCreator = config.FsCreators.SubDirectoryFileSystemCreator;
ncaFsServiceConfig.EncryptedFsCreator = config.FsCreators.EncryptedFileSystemCreator;
ncaFsServiceConfig.ProgramRegistryService = programRegistryService;
ncaFsServiceConfig.HorizonClient = Hos;
ncaFsServiceConfig.ProgramRegistry = programRegistry;
ncaFsServiceConfig.SpeedEmulationRange =
new InternalProgramIdRangeForSpeedEmulation(SpeedEmulationProgramIdMinimum,
SpeedEmulationProgramIdMaximum);
var ncaFsService = new NcaFileSystemServiceImpl(in ncaFsServiceConfig, config.ExternalKeySet);
var saveFsServiceConfig = new SaveDataFileSystemServiceImpl.Configuration();
saveFsServiceConfig.BaseFsService = baseFsService;
saveFsServiceConfig.HostFsCreator = config.FsCreators.HostFileSystemCreator;
saveFsServiceConfig.TargetManagerFsCreator = config.FsCreators.TargetManagerFileSystemCreator;
saveFsServiceConfig.SaveFsCreator = config.FsCreators.SaveDataFileSystemCreator;
saveFsServiceConfig.EncryptedFsCreator = config.FsCreators.EncryptedFileSystemCreator;
saveFsServiceConfig.ProgramRegistryService = programRegistryService;
saveFsServiceConfig.ShouldCreateDirectorySaveData = () => true;
saveFsServiceConfig.SaveIndexerManager = saveDataIndexerManager;
saveFsServiceConfig.HorizonClient = Hos;
saveFsServiceConfig.ProgramRegistry = programRegistry;
var saveFsService = new SaveDataFileSystemServiceImpl(in saveFsServiceConfig);
var accessLogServiceConfig = new AccessLogServiceImpl.Configuration();
accessLogServiceConfig.MinimumProgramIdForSdCardLog = 0x0100000000003000;
accessLogServiceConfig.HorizonClient = Hos;
accessLogServiceConfig.ProgramRegistry = programRegistry;
var accessLogService = new AccessLogServiceImpl(in accessLogServiceConfig);
var fspConfig = new FileSystemProxyConfiguration
{
FsCreatorInterfaces = config.FsCreators,
BaseStorageService = baseStorageService,
BaseFileSystemService = baseFsService,
NcaFileSystemService = ncaFsService,
SaveDataFileSystemService = saveFsService,
TimeService = timeService,
ProgramRegistryService = programRegistryService,
AccessLogService = accessLogService
};
return fspConfig;
}
private FileSystemProxy GetFileSystemProxyForLoaderServiceObject()
private FileSystemProxyImpl GetFileSystemProxyServiceObject()
{
return new FileSystemProxy(Hos, FsProxyCore);
return new FileSystemProxyImpl(FsProxyCore);
}
private FileSystemProxyImpl GetFileSystemProxyForLoaderServiceObject()
{
return new FileSystemProxyImpl(FsProxyCore);
}
private ProgramRegistryImpl GetProgramRegistryServiceObject()
{
return new ProgramRegistryImpl(FsProxyCore.Config.ProgramRegistryServiceImpl);
return new ProgramRegistryImpl(FsProxyCore.Config.ProgramRegistryService);
}
private class FileSystemProxyService : IServiceObject

View File

@ -1,107 +1,119 @@
using System;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem;
using LibHac.Fs;
using LibHac.FsSrv.Sf;
using LibHac.Ncm;
using LibHac.Sf;
using LibHac.Spl;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
using IFileSf = LibHac.FsSrv.Sf.IFile;
using IStorageSf = LibHac.FsSrv.Sf.IStorage;
namespace LibHac.FsSrv
{
public interface IFileSystemProxy
{
Result SetCurrentProcess(ulong processId);
Result OpenDataFileSystemByCurrentProcess(out IFileSystem fileSystem);
Result OpenFileSystemWithPatch(out IFileSystem fileSystem, ProgramId programId, FileSystemProxyType type);
Result OpenFileSystemWithId(out IFileSystem fileSystem, ref FsPath path, ulong id, FileSystemProxyType type);
Result OpenDataFileSystemByProgramId(out IFileSystem fileSystem, ProgramId programId);
Result OpenBisFileSystem(out IFileSystem fileSystem, ref FsPath rootPath, BisPartitionId partitionId);
Result OpenBisStorage(out IStorage storage, BisPartitionId partitionId);
Result OpenDataFileSystemByCurrentProcess(out ReferenceCountedDisposable<IFileSystemSf> fileSystem);
Result OpenFileSystemWithPatch(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, ProgramId programId, FileSystemProxyType fsType);
Result OpenFileSystemWithId(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, in FspPath path, ulong id, FileSystemProxyType fsType);
Result OpenDataFileSystemByProgramId(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, ProgramId programId);
Result OpenBisFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, in FspPath rootPath, BisPartitionId partitionId);
Result OpenBisStorage(out ReferenceCountedDisposable<IStorageSf> storage, BisPartitionId partitionId);
Result InvalidateBisCache();
Result OpenHostFileSystemWithOption(out IFileSystem fileSystem, ref FsPath path, MountHostOption option);
Result OpenHostFileSystem(out IFileSystem fileSystem, ref FsPath path);
Result OpenSdCardFileSystem(out IFileSystem fileSystem);
Result OpenHostFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, in FspPath path);
Result OpenSdCardFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem);
Result FormatSdCardFileSystem();
Result DeleteSaveDataFileSystem(ulong saveDataId);
Result CreateSaveDataFileSystem(ref SaveDataAttribute attribute, ref SaveDataCreationInfo creationInfo, ref SaveMetaCreateInfo metaCreateInfo);
Result CreateSaveDataFileSystemBySystemSaveDataId(ref SaveDataAttribute attribute, ref SaveDataCreationInfo creationInfo);
Result RegisterSaveDataFileSystemAtomicDeletion(ReadOnlySpan<ulong> saveDataIds);
Result CreateSaveDataFileSystem(in SaveDataAttribute attribute, in SaveDataCreationInfo creationInfo, in SaveDataMetaInfo metaInfo);
Result CreateSaveDataFileSystemBySystemSaveDataId(in SaveDataAttribute attribute, in SaveDataCreationInfo creationInfo);
Result RegisterSaveDataFileSystemAtomicDeletion(InBuffer saveDataIds);
Result DeleteSaveDataFileSystemBySaveDataSpaceId(SaveDataSpaceId spaceId, ulong saveDataId);
Result FormatSdCardDryRun();
Result IsExFatSupported(out bool isSupported);
Result DeleteSaveDataFileSystemBySaveDataAttribute(SaveDataSpaceId spaceId, ref SaveDataAttribute attribute);
Result OpenGameCardStorage(out IStorage storage, GameCardHandle handle, GameCardPartitionRaw partitionId);
Result OpenGameCardFileSystem(out IFileSystem fileSystem, GameCardHandle handle, GameCardPartition partitionId);
Result DeleteSaveDataFileSystemBySaveDataAttribute(SaveDataSpaceId spaceId, in SaveDataAttribute attribute);
Result OpenGameCardStorage(out ReferenceCountedDisposable<IStorageSf> storage, GameCardHandle handle, GameCardPartitionRaw partitionId);
Result OpenGameCardFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, GameCardHandle handle, GameCardPartition partitionId);
Result ExtendSaveDataFileSystem(SaveDataSpaceId spaceId, ulong saveDataId, long dataSize, long journalSize);
Result DeleteCacheStorage(short index);
Result GetCacheStorageSize(out long dataSize, out long journalSize, short index);
Result CreateSaveDataFileSystemWithHashSalt(ref SaveDataAttribute attribute, ref SaveDataCreationInfo creationInfo, ref SaveMetaCreateInfo metaCreateInfo, ref HashSalt hashSalt);
Result OpenSaveDataFileSystem(out IFileSystem fileSystem, SaveDataSpaceId spaceId, ref SaveDataAttribute attribute);
Result OpenSaveDataFileSystemBySystemSaveDataId(out IFileSystem fileSystem, SaveDataSpaceId spaceId, ref SaveDataAttribute attribute);
Result OpenReadOnlySaveDataFileSystem(out IFileSystem fileSystem, SaveDataSpaceId spaceId, ref SaveDataAttribute attribute);
Result ReadSaveDataFileSystemExtraDataBySaveDataSpaceId(Span<byte> extraDataBuffer, SaveDataSpaceId spaceId, ulong saveDataId);
Result ReadSaveDataFileSystemExtraData(Span<byte> extraDataBuffer, ulong saveDataId);
Result WriteSaveDataFileSystemExtraData(ulong saveDataId, SaveDataSpaceId spaceId, ReadOnlySpan<byte> extraDataBuffer);
Result DeleteCacheStorage(ushort index);
Result GetCacheStorageSize(out long dataSize, out long journalSize, ushort index);
Result CreateSaveDataFileSystemWithHashSalt(in SaveDataAttribute attribute, in SaveDataCreationInfo creationInfo, in SaveDataMetaInfo metaInfo, in HashSalt hashSalt);
Result OpenHostFileSystemWithOption(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, in FspPath path, MountHostOption option);
Result OpenSaveDataFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, SaveDataSpaceId spaceId, in SaveDataAttribute attribute);
Result OpenSaveDataFileSystemBySystemSaveDataId(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, SaveDataSpaceId spaceId, in SaveDataAttribute attribute);
Result OpenReadOnlySaveDataFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, SaveDataSpaceId spaceId, in SaveDataAttribute attribute);
Result ReadSaveDataFileSystemExtraDataBySaveDataSpaceId(OutBuffer extraDataBuffer, SaveDataSpaceId spaceId, ulong saveDataId);
Result ReadSaveDataFileSystemExtraData(OutBuffer extraDataBuffer, ulong saveDataId);
Result WriteSaveDataFileSystemExtraData(ulong saveDataId, SaveDataSpaceId spaceId, InBuffer extraDataBuffer);
Result OpenSaveDataInfoReader(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader);
Result OpenSaveDataInfoReaderBySaveDataSpaceId(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader, SaveDataSpaceId spaceId);
Result OpenSaveDataInfoReaderOnlyCacheStorage(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader);
Result OpenSaveDataInternalStorageFileSystem(out IFileSystem fileSystem, SaveDataSpaceId spaceId, ulong saveDataId);
Result OpenSaveDataInternalStorageFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, SaveDataSpaceId spaceId, ulong saveDataId);
Result UpdateSaveDataMacForDebug(SaveDataSpaceId spaceId, ulong saveDataId);
Result WriteSaveDataFileSystemExtraDataWithMask(ulong saveDataId, SaveDataSpaceId spaceId, ReadOnlySpan<byte> extraDataBuffer, ReadOnlySpan<byte> maskBuffer);
Result FindSaveDataWithFilter(out long count, Span<byte> saveDataInfoBuffer, SaveDataSpaceId spaceId, ref SaveDataFilter filter);
Result OpenSaveDataInfoReaderWithFilter(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader, SaveDataSpaceId spaceId, ref SaveDataFilter filter);
Result ReadSaveDataFileSystemExtraDataBySaveDataAttribute(Span<byte> extraDataBuffer, SaveDataSpaceId spaceId, ref SaveDataAttribute attribute);
Result WriteSaveDataFileSystemExtraDataBySaveDataAttribute(ref SaveDataAttribute attribute, SaveDataSpaceId spaceId, ReadOnlySpan<byte> extraDataBuffer, ReadOnlySpan<byte> maskBuffer);
Result OpenSaveDataMetaFile(out IFile file, SaveDataSpaceId spaceId, ref SaveDataAttribute attribute, SaveDataMetaType type);
Result WriteSaveDataFileSystemExtraDataWithMask(ulong saveDataId, SaveDataSpaceId spaceId, InBuffer extraDataBuffer, InBuffer maskBuffer);
Result FindSaveDataWithFilter(out long count, OutBuffer saveDataInfoBuffer, SaveDataSpaceId spaceId, in SaveDataFilter filter);
Result OpenSaveDataInfoReaderWithFilter(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader, SaveDataSpaceId spaceId, in SaveDataFilter filter);
Result ReadSaveDataFileSystemExtraDataBySaveDataAttribute(OutBuffer extraDataBuffer, SaveDataSpaceId spaceId, in SaveDataAttribute attribute);
Result WriteSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(in SaveDataAttribute attribute, SaveDataSpaceId spaceId, InBuffer extraDataBuffer, InBuffer maskBuffer);
Result ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(OutBuffer extraDataBuffer, SaveDataSpaceId spaceId, in SaveDataAttribute attribute, InBuffer maskBuffer);
Result OpenSaveDataMetaFile(out ReferenceCountedDisposable<IFileSf> file, SaveDataSpaceId spaceId, in SaveDataAttribute attribute, SaveDataMetaType type);
Result ListAccessibleSaveDataOwnerId(out int readCount, Span<Ncm.ApplicationId> idBuffer, ProgramId programId, int startIndex, int bufferIdCount);
Result OpenImageDirectoryFileSystem(out IFileSystem fileSystem, ImageDirectoryId dirId);
Result OpenContentStorageFileSystem(out IFileSystem fileSystem, ContentStorageId storageId);
Result OpenCloudBackupWorkStorageFileSystem(out IFileSystem fileSystem, CloudBackupWorkStorageId storageId);
Result OpenCustomStorageFileSystem(out IFileSystem fileSystem, CustomStorageId storageId);
Result OpenDataStorageByCurrentProcess(out IStorage storage);
Result OpenDataStorageByProgramId(out IStorage storage, ProgramId programId);
Result OpenDataStorageByDataId(out IStorage storage, DataId dataId, StorageId storageId);
Result OpenPatchDataStorageByCurrentProcess(out IStorage storage);
Result OpenDataFileSystemWithProgramIndex(out IFileSystem fileSystem, byte programIndex);
Result OpenDataStorageWithProgramIndex(out IStorage storage, byte programIndex);
Result OpenDeviceOperator(out IDeviceOperator deviceOperator);
Result ListAccessibleSaveDataOwnerId(out int readCount, OutBuffer idBuffer, ProgramId programId, int startIndex, int bufferIdCount);
Result OpenSaveDataMover(out ReferenceCountedDisposable<ISaveDataMover> saveMover, SaveDataSpaceId sourceSpaceId, SaveDataSpaceId destinationSpaceId, NativeHandle workBufferHandle, ulong workBufferSize);
Result OpenImageDirectoryFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, ImageDirectoryId directoryId);
Result OpenContentStorageFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, ContentStorageId storageId);
Result OpenCloudBackupWorkStorageFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, CloudBackupWorkStorageId storageId);
Result OpenCustomStorageFileSystem(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, CustomStorageId storageId);
Result OpenDataStorageByCurrentProcess(out ReferenceCountedDisposable<IStorageSf> storage);
Result OpenDataStorageByProgramId(out ReferenceCountedDisposable<IStorageSf> storage, ProgramId programId);
Result OpenDataStorageByDataId(out ReferenceCountedDisposable<IStorageSf> storage, DataId dataId, StorageId storageId);
Result OpenPatchDataStorageByCurrentProcess(out ReferenceCountedDisposable<IStorageSf> storage);
Result OpenDataFileSystemWithProgramIndex(out ReferenceCountedDisposable<IFileSystemSf> fileSystem, byte programIndex);
Result OpenDataStorageWithProgramIndex(out ReferenceCountedDisposable<IStorageSf> storage, byte programIndex);
Result OpenDeviceOperator(out ReferenceCountedDisposable<IDeviceOperator> deviceOperator);
Result OpenSdCardDetectionEventNotifier(out ReferenceCountedDisposable<IEventNotifier> eventNotifier);
Result OpenGameCardDetectionEventNotifier(out ReferenceCountedDisposable<IEventNotifier> eventNotifier);
Result OpenSystemDataUpdateEventNotifier(out ReferenceCountedDisposable<IEventNotifier> eventNotifier);
Result NotifySystemDataUpdateEvent();
Result SimulateDeviceDetectionEvent(SdmmcPort port, SimulatingDeviceDetectionMode mode, bool signalEvent);
Result QuerySaveDataTotalSize(out long totalSize, long dataSize, long journalSize);
Result VerifySaveDataFileSystem(ulong saveDataId, Span<byte> readBuffer);
Result VerifySaveDataFileSystem(ulong saveDataId, OutBuffer readBuffer);
Result CorruptSaveDataFileSystem(ulong saveDataId);
Result CreatePaddingFile(long size);
Result DeleteAllPaddingFiles();
Result GetRightsId(out RightsId rightsId, ProgramId programId, StorageId storageId);
Result RegisterExternalKey(ref RightsId rightsId, ref AccessKey externalKey);
Result RegisterExternalKey(in RightsId rightsId, in AccessKey externalKey);
Result UnregisterAllExternalKey();
Result GetRightsIdByPath(out RightsId rightsId, ref FsPath path);
Result GetRightsIdAndKeyGenerationByPath(out RightsId rightsId, out byte keyGeneration, ref FsPath path);
Result SetCurrentPosixTimeWithTimeDifference(long time, int difference);
Result GetRightsIdByPath(out RightsId rightsId, in FspPath path);
Result GetRightsIdAndKeyGenerationByPath(out RightsId rightsId, out byte keyGeneration, in FspPath path);
Result SetCurrentPosixTimeWithTimeDifference(long currentTime, int timeDifference);
Result GetFreeSpaceSizeForSaveData(out long freeSpaceSize, SaveDataSpaceId spaceId);
Result VerifySaveDataFileSystemBySaveDataSpaceId(SaveDataSpaceId spaceId, ulong saveDataId, Span<byte> readBuffer);
Result VerifySaveDataFileSystemBySaveDataSpaceId(SaveDataSpaceId spaceId, ulong saveDataId, OutBuffer readBuffer);
Result CorruptSaveDataFileSystemBySaveDataSpaceId(SaveDataSpaceId spaceId, ulong saveDataId);
Result QuerySaveDataInternalStorageTotalSize(out long size, SaveDataSpaceId spaceId, ulong saveDataId);
Result GetSaveDataCommitId(out long commitId, SaveDataSpaceId spaceId, ulong saveDataId);
Result UnregisterExternalKey(ref RightsId rightsId);
Result SetSdCardEncryptionSeed(ref EncryptionSeed seed);
Result UnregisterExternalKey(in RightsId rightsId);
Result SetSdCardEncryptionSeed(in EncryptionSeed seed);
Result SetSdCardAccessibility(bool isAccessible);
Result IsSdCardAccessible(out bool isAccessible);
Result RegisterProgramIndexMapInfo(ReadOnlySpan<byte> programIndexMapInfoBuffer, int programCount);
Result SetBisRootForHost(BisPartitionId partitionId, ref FsPath path);
Result RegisterProgramIndexMapInfo(InBuffer programIndexMapInfoBuffer, int programCount);
Result SetBisRootForHost(BisPartitionId partitionId, in FspPath path);
Result SetSaveDataSize(long saveDataSize, long saveDataJournalSize);
Result SetSaveDataRootPath(ref FsPath path);
Result SetSaveDataRootPath(in FspPath path);
Result DisableAutoSaveDataCreation();
Result SetGlobalAccessLogMode(GlobalAccessLogMode mode);
Result GetGlobalAccessLogMode(out GlobalAccessLogMode mode);
Result OutputAccessLogToSdCard(U8Span logString);
Result OutputAccessLogToSdCard(InBuffer textBuffer);
Result RegisterUpdatePartition();
Result OpenRegisteredUpdatePartition(out IFileSystem fileSystem);
Result OpenRegisteredUpdatePartition(out ReferenceCountedDisposable<IFileSystemSf> fileSystem);
Result GetProgramIndexForAccessLog(out int programIndex, out int programCount);
Result OverrideSaveDataTransferTokenSignVerificationKey(ReadOnlySpan<byte> key);
Result UnsetSaveDataRootPath();
Result OutputMultiProgramTagAccessLog();
Result OverrideSaveDataTransferTokenSignVerificationKey(InBuffer key);
Result CorruptSaveDataFileSystemByOffset(SaveDataSpaceId spaceId, ulong saveDataId, long offset);
Result OpenMultiCommitManager(out IMultiCommitManager commitManager);
Result OpenMultiCommitManager(out ReferenceCountedDisposable<IMultiCommitManager> commitManager);
Result OpenBisWiper(out ReferenceCountedDisposable<IWiper> bisWiper, NativeHandle transferMemoryHandle, ulong transferMemorySize);
}
}

View File

@ -1,10 +0,0 @@
using LibHac.Fs.Fsa;
namespace LibHac.FsSrv
{
public interface IMultiCommitManager
{
Result Add(IFileSystem fileSystem);
Result Commit();
}
}

View File

@ -139,10 +139,10 @@ namespace LibHac.FsSrv
int GetIndexCount();
/// <summary>
/// Returns an <see cref="ISaveDataInfoReader"/> that iterates through the <see cref="SaveDataIndexer"/>.
/// Returns an <see cref="SaveDataInfoReaderImpl"/> that iterates through the <see cref="SaveDataIndexer"/>.
/// </summary>
/// <param name="infoReader">If the method returns successfully, contains the created <see cref="ISaveDataInfoReader"/>.</param>
/// <param name="infoReader">If the method returns successfully, contains the created <see cref="SaveDataInfoReaderImpl"/>.</param>
/// <returns>The <see cref="Result"/> of the operation.</returns>
Result OpenSaveDataInfoReader(out ReferenceCountedDisposable<ISaveDataInfoReader> infoReader);
Result OpenSaveDataInfoReader(out ReferenceCountedDisposable<SaveDataInfoReaderImpl> infoReader);
}
}

View File

@ -4,8 +4,8 @@ namespace LibHac.FsSrv
{
public interface ISaveDataIndexerManager
{
Result OpenAccessor(out SaveDataIndexerAccessor accessor, out bool neededInit, SaveDataSpaceId spaceId);
void ResetTemporaryStorageIndexer(SaveDataSpaceId spaceId);
void InvalidateSdCardIndexer(SaveDataSpaceId spaceId);
Result OpenSaveDataIndexerAccessor(out SaveDataIndexerAccessor accessor, out bool neededInit, SaveDataSpaceId spaceId);
void ResetIndexer(SaveDataSpaceId spaceId);
void InvalidateIndexer(SaveDataSpaceId spaceId);
}
}

View File

@ -830,6 +830,7 @@ namespace LibHac.FsSrv.Impl
RegisterProgramIndexMapInfo,
ChallengeCardExistence,
CreateOwnSaveData,
DeleteOwnSaveData,
ReadOwnSaveDataFileSystemExtraData,
ExtendOwnSaveData,
OpenOwnSaveDataTransferProhibiter,

View File

@ -0,0 +1,27 @@
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem;
namespace LibHac.FsSrv.Impl
{
public class AsynchronousAccessFileSystem : ForwardingFileSystem
{
protected AsynchronousAccessFileSystem(ref ReferenceCountedDisposable<IFileSystem> baseFileSystem) : base(
ref baseFileSystem)
{ }
public static ReferenceCountedDisposable<IFileSystem> CreateShared(
ref ReferenceCountedDisposable<IFileSystem> fileSystem)
{
return new ReferenceCountedDisposable<IFileSystem>(new AsynchronousAccessFileSystem(ref fileSystem));
}
// ReSharper disable once RedundantOverriddenMember
protected override Result DoOpenFile(out IFile file, U8Span path, OpenMode mode)
{
// Todo: Implement
return base.DoOpenFile(out file, path, mode);
}
}
}

View File

@ -0,0 +1,33 @@
using System;
using LibHac.FsSrv.Sf;
using LibHac.Sf;
namespace LibHac.FsSrv.Impl
{
internal class BisWiper : IWiper
{
// ReSharper disable UnusedParameter.Local
public BisWiper(NativeHandle memoryHandle, ulong memorySize) { }
// ReSharper restore UnusedParameter.Local
public Result Startup(out long spaceToWipe)
{
throw new NotImplementedException();
}
public Result Process(out long remainingSpaceToWipe)
{
throw new NotImplementedException();
}
public static Result CreateWiper(out IWiper wiper, NativeHandle memoryHandle, ulong memorySize)
{
wiper = new BisWiper(memoryHandle, memorySize);
return Result.Success;
}
public void Dispose()
{
}
}
}

View File

@ -0,0 +1,58 @@
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem;
namespace LibHac.FsSrv.Impl
{
public class DeepRetryFileSystem : ForwardingFileSystem
{
private ReferenceCountedDisposable<DeepRetryFileSystem>.WeakReference SelfReference { get; set; }
private ReferenceCountedDisposable<IRomFileSystemAccessFailureManager> AccessFailureManager { get; set; }
protected DeepRetryFileSystem(ref ReferenceCountedDisposable<IFileSystem> baseFileSystem,
ref ReferenceCountedDisposable<IRomFileSystemAccessFailureManager> accessFailureManager) : base(
ref baseFileSystem)
{
AccessFailureManager = Shared.Move(ref accessFailureManager);
}
public static ReferenceCountedDisposable<IFileSystem> CreateShared(
ref ReferenceCountedDisposable<IFileSystem> fileSystem,
ref ReferenceCountedDisposable<IRomFileSystemAccessFailureManager> accessFailureManager)
{
ReferenceCountedDisposable<DeepRetryFileSystem> sharedRetryFileSystem = null;
try
{
var retryFileSystem = new DeepRetryFileSystem(ref fileSystem, ref accessFailureManager);
sharedRetryFileSystem = new ReferenceCountedDisposable<DeepRetryFileSystem>(retryFileSystem);
retryFileSystem.SelfReference =
new ReferenceCountedDisposable<DeepRetryFileSystem>.WeakReference(sharedRetryFileSystem);
return sharedRetryFileSystem.AddReference<IFileSystem>();
}
finally
{
sharedRetryFileSystem?.Dispose();
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
AccessFailureManager?.Dispose();
}
base.Dispose(disposing);
}
// ReSharper disable once RedundantOverriddenMember
protected override Result DoOpenFile(out IFile file, U8Span path, OpenMode mode)
{
// Todo: Implement
return base.DoOpenFile(out file, path, mode);
}
}
}

View File

@ -0,0 +1,65 @@
using System;
using System.Runtime.InteropServices;
using LibHac.Fs;
using LibHac.Sf;
using IDirectory = LibHac.Fs.Fsa.IDirectory;
using IDirectorySf = LibHac.FsSrv.Sf.IDirectory;
namespace LibHac.FsSrv.Impl
{
internal class DirectoryInterfaceAdapter : IDirectorySf
{
private ReferenceCountedDisposable<FileSystemInterfaceAdapter> ParentFs { get; }
private IDirectory BaseDirectory { get; }
public DirectoryInterfaceAdapter(IDirectory baseDirectory,
ref ReferenceCountedDisposable<FileSystemInterfaceAdapter> parentFileSystem)
{
BaseDirectory = baseDirectory;
ParentFs = parentFileSystem;
parentFileSystem = null;
}
public Result Read(out long entriesRead, OutBuffer entryBuffer)
{
const int maxTryCount = 2;
entriesRead = default;
Span<DirectoryEntry> entries = MemoryMarshal.Cast<byte, DirectoryEntry>(entryBuffer.Buffer);
Result rc = Result.Success;
long tmpEntriesRead = 0;
for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
{
rc = BaseDirectory.Read(out tmpEntriesRead, entries);
// Retry on ResultDataCorrupted
if (!ResultFs.DataCorrupted.Includes(rc))
break;
}
if (rc.IsFailure()) return rc;
entriesRead = tmpEntriesRead;
return Result.Success;
}
public Result GetEntryCount(out long entryCount)
{
entryCount = default;
Result rc = BaseDirectory.GetEntryCount(out long tmpEntryCount);
if (rc.IsFailure()) return rc;
entryCount = tmpEntryCount;
return Result.Success;
}
public void Dispose()
{
BaseDirectory?.Dispose();
ParentFs?.Dispose();
}
}
}

View File

@ -0,0 +1,131 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Fs;
using IFile = LibHac.Fs.Fsa.IFile;
using IFileSf = LibHac.FsSrv.Sf.IFile;
namespace LibHac.FsSrv.Impl
{
internal class FileInterfaceAdapter : IFileSf
{
private ReferenceCountedDisposable<FileSystemInterfaceAdapter> ParentFs { get; }
private IFile BaseFile { get; }
public FileInterfaceAdapter(IFile baseFile,
ref ReferenceCountedDisposable<FileSystemInterfaceAdapter> parentFileSystem)
{
BaseFile = baseFile;
ParentFs = parentFileSystem;
parentFileSystem = null;
}
public Result Read(out long bytesRead, long offset, Span<byte> destination, ReadOption option)
{
const int maxTryCount = 2;
bytesRead = default;
if (offset < 0)
return ResultFs.InvalidOffset.Log();
if (destination.Length < 0)
return ResultFs.InvalidSize.Log();
Result rc = Result.Success;
long tmpBytesRead = 0;
for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
{
rc = BaseFile.Read(out tmpBytesRead, offset, destination, option);
// Retry on ResultDataCorrupted
if (!ResultFs.DataCorrupted.Includes(rc))
break;
}
if (rc.IsFailure()) return rc;
bytesRead = tmpBytesRead;
return Result.Success;
}
public Result Write(long offset, ReadOnlySpan<byte> source, WriteOption option)
{
if (offset < 0)
return ResultFs.InvalidOffset.Log();
if (source.Length < 0)
return ResultFs.InvalidSize.Log();
// Note: Thread priority is temporarily when writing in FS
return BaseFile.Write(offset, source, option);
}
public Result Flush()
{
return BaseFile.Flush();
}
public Result SetSize(long size)
{
if (size < 0)
return ResultFs.InvalidSize.Log();
return BaseFile.SetSize(size);
}
public Result GetSize(out long size)
{
const int maxTryCount = 2;
size = default;
Result rc = Result.Success;
long tmpSize = 0;
for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
{
rc = BaseFile.GetSize(out tmpSize);
// Retry on ResultDataCorrupted
if (!ResultFs.DataCorrupted.Includes(rc))
break;
}
if (rc.IsFailure()) return rc;
size = tmpSize;
return Result.Success;
}
public Result OperateRange(out QueryRangeInfo rangeInfo, int operationId, long offset, long size)
{
rangeInfo = new QueryRangeInfo();
if (operationId == (int)OperationId.InvalidateCache)
{
Result rc = BaseFile.OperateRange(Span<byte>.Empty, OperationId.InvalidateCache, offset, size,
ReadOnlySpan<byte>.Empty);
if (rc.IsFailure()) return rc;
}
else if (operationId == (int)OperationId.QueryRange)
{
Unsafe.SkipInit(out QueryRangeInfo info);
Result rc = BaseFile.OperateRange(SpanHelpers.AsByteSpan(ref info), OperationId.QueryRange, offset, size,
ReadOnlySpan<byte>.Empty);
if (rc.IsFailure()) return rc;
rangeInfo.Merge(in info);
}
return Result.Success;
}
public void Dispose()
{
BaseFile?.Dispose();
ParentFs?.Dispose();
}
}
}

View File

@ -0,0 +1,292 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSrv.Sf;
using LibHac.Util;
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
using IFileSf = LibHac.FsSrv.Sf.IFile;
using IDirectorySf = LibHac.FsSrv.Sf.IDirectory;
namespace LibHac.FsSrv.Impl
{
internal class FileSystemInterfaceAdapter : IFileSystemSf
{
private ReferenceCountedDisposable<IFileSystem> BaseFileSystem { get; }
private bool IsHostFsRoot { get; }
// In FS, FileSystemInterfaceAdapter is derived from ISharedObject, so that's used for ref-counting when
// creating files and directories. We don't have an ISharedObject, so a self-reference is used instead.
private ReferenceCountedDisposable<FileSystemInterfaceAdapter>.WeakReference _selfReference;
/// <summary>
/// Initializes a new <see cref="FileSystemInterfaceAdapter"/> by creating
/// a new reference to <paramref name="fileSystem"/>.
/// </summary>
/// <param name="fileSystem">The base file system.</param>
/// <param name="isHostFsRoot">Does the base file system come from the root directory of a host file system?</param>
private FileSystemInterfaceAdapter(ReferenceCountedDisposable<IFileSystem> fileSystem,
bool isHostFsRoot = false)
{
BaseFileSystem = fileSystem.AddReference();
IsHostFsRoot = isHostFsRoot;
}
/// <summary>
/// Initializes a new <see cref="FileSystemInterfaceAdapter"/> by moving the file system object.
/// Avoids allocations from incrementing and then decrementing the ref-count.
/// </summary>
/// <param name="fileSystem">The base file system. Will be null upon returning.</param>
/// <param name="isHostFsRoot">Does the base file system come from the root directory of a host file system?</param>
private FileSystemInterfaceAdapter(ref ReferenceCountedDisposable<IFileSystem> fileSystem,
bool isHostFsRoot = false)
{
BaseFileSystem = fileSystem;
fileSystem = null;
IsHostFsRoot = isHostFsRoot;
}
/// <summary>
/// Initializes a new <see cref="FileSystemInterfaceAdapter"/>, creating a copy of the input file system object.
/// </summary>
/// <param name="baseFileSystem">The base file system.</param>
/// <param name="isHostFsRoot">Does the base file system come from the root directory of a host file system?</param>
public static ReferenceCountedDisposable<FileSystemInterfaceAdapter> CreateShared(
ReferenceCountedDisposable<IFileSystem> baseFileSystem, bool isHostFsRoot = false)
{
var adapter = new FileSystemInterfaceAdapter(baseFileSystem, isHostFsRoot);
return ReferenceCountedDisposable<FileSystemInterfaceAdapter>.Create(adapter, out adapter._selfReference);
}
/// <summary>
/// Initializes a new <see cref="FileSystemInterfaceAdapter"/> cast to an <see cref="IFileSystemSf"/>
/// by moving the input file system object. Avoids allocations from incrementing and then decrementing the ref-count.
/// </summary>
/// <param name="baseFileSystem">The base file system. Will be null upon returning.</param>
/// <param name="isHostFsRoot">Does the base file system come from the root directory of a host file system?</param>
public static ReferenceCountedDisposable<IFileSystemSf> CreateShared(
ref ReferenceCountedDisposable<IFileSystem> baseFileSystem, bool isHostFsRoot = false)
{
var adapter = new FileSystemInterfaceAdapter(ref baseFileSystem, isHostFsRoot);
return ReferenceCountedDisposable<IFileSystemSf>.Create(adapter, out adapter._selfReference);
}
private static ReadOnlySpan<byte> RootDir => new[] { (byte)'/' };
public Result GetImpl(out ReferenceCountedDisposable<IFileSystem> fileSystem)
{
fileSystem = BaseFileSystem.AddReference();
return Result.Success;
}
public Result CreateFile(in Path path, long size, int option)
{
if (size < 0)
return ResultFs.InvalidSize.Log();
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
if (normalizer.Result.IsFailure()) return normalizer.Result;
return BaseFileSystem.Target.CreateFile(normalizer.Path, size, (CreateFileOptions)option);
}
public Result DeleteFile(in Path path)
{
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
if (normalizer.Result.IsFailure()) return normalizer.Result;
return BaseFileSystem.Target.DeleteFile(normalizer.Path);
}
public Result CreateDirectory(in Path path)
{
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
if (normalizer.Result.IsFailure()) return normalizer.Result;
if (StringUtils.Compare(RootDir, normalizer.Path) != 0)
return ResultFs.PathAlreadyExists.Log();
return BaseFileSystem.Target.CreateDirectory(normalizer.Path);
}
public Result DeleteDirectory(in Path path)
{
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
if (normalizer.Result.IsFailure()) return normalizer.Result;
if (StringUtils.Compare(RootDir, normalizer.Path) != 0)
return ResultFs.DirectoryNotDeletable.Log();
return BaseFileSystem.Target.DeleteDirectory(normalizer.Path);
}
public Result DeleteDirectoryRecursively(in Path path)
{
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
if (normalizer.Result.IsFailure()) return normalizer.Result;
if (StringUtils.Compare(RootDir, normalizer.Path) != 0)
return ResultFs.DirectoryNotDeletable.Log();
return BaseFileSystem.Target.DeleteDirectoryRecursively(normalizer.Path);
}
public Result RenameFile(in Path oldPath, in Path newPath)
{
var normalizerOldPath = new PathNormalizer(new U8Span(oldPath.Str), GetPathNormalizerOption());
if (normalizerOldPath.Result.IsFailure()) return normalizerOldPath.Result;
var normalizerNewPath = new PathNormalizer(new U8Span(newPath.Str), GetPathNormalizerOption());
if (normalizerNewPath.Result.IsFailure()) return normalizerNewPath.Result;
return BaseFileSystem.Target.RenameFile(new U8Span(normalizerOldPath.Path),
new U8Span(normalizerNewPath.Path));
}
public Result RenameDirectory(in Path oldPath, in Path newPath)
{
var normalizerOldPath = new PathNormalizer(new U8Span(oldPath.Str), GetPathNormalizerOption());
if (normalizerOldPath.Result.IsFailure()) return normalizerOldPath.Result;
var normalizerNewPath = new PathNormalizer(new U8Span(newPath.Str), GetPathNormalizerOption());
if (normalizerNewPath.Result.IsFailure()) return normalizerNewPath.Result;
if (PathTool.IsSubpath(normalizerOldPath.Path, normalizerNewPath.Path))
return ResultFs.DirectoryNotRenamable.Log();
return BaseFileSystem.Target.RenameDirectory(normalizerOldPath.Path, normalizerNewPath.Path);
}
public Result GetEntryType(out uint entryType, in Path path)
{
entryType = default;
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
if (normalizer.Result.IsFailure()) return normalizer.Result;
ref DirectoryEntryType type = ref Unsafe.As<uint, DirectoryEntryType>(ref entryType);
return BaseFileSystem.Target.GetEntryType(out type, new U8Span(normalizer.Path));
}
public Result OpenFile(out ReferenceCountedDisposable<IFileSf> file, in Path path, uint mode)
{
const int maxTryCount = 2;
file = default;
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
if (normalizer.Result.IsFailure()) return normalizer.Result;
Result rc = Result.Success;
Fs.Fsa.IFile fileInterface = null;
for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
{
rc = BaseFileSystem.Target.OpenFile(out fileInterface, new U8Span(normalizer.Path), (OpenMode)mode);
// Retry on ResultDataCorrupted
if (!ResultFs.DataCorrupted.Includes(rc))
break;
}
if (rc.IsFailure()) return rc;
ReferenceCountedDisposable<FileSystemInterfaceAdapter> selfReference = _selfReference.TryAddReference();
var adapter = new FileInterfaceAdapter(fileInterface, ref selfReference);
file = new ReferenceCountedDisposable<IFileSf>(adapter);
return Result.Success;
}
public Result OpenDirectory(out ReferenceCountedDisposable<IDirectorySf> directory, in Path path, uint mode)
{
const int maxTryCount = 2;
directory = default;
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
if (normalizer.Result.IsFailure()) return normalizer.Result;
Result rc = Result.Success;
Fs.Fsa.IDirectory dirInterface = null;
for (int tryNum = 0; tryNum < maxTryCount; tryNum++)
{
rc = BaseFileSystem.Target.OpenDirectory(out dirInterface, new U8Span(normalizer.Path), (OpenDirectoryMode)mode);
// Retry on ResultDataCorrupted
if (!ResultFs.DataCorrupted.Includes(rc))
break;
}
if (rc.IsFailure()) return rc;
ReferenceCountedDisposable<FileSystemInterfaceAdapter> selfReference = _selfReference.TryAddReference();
var adapter = new DirectoryInterfaceAdapter(dirInterface, ref selfReference);
directory = new ReferenceCountedDisposable<IDirectorySf>(adapter);
return Result.Success;
}
public Result Commit()
{
return BaseFileSystem.Target.Commit();
}
public Result GetFreeSpaceSize(out long freeSpace, in Path path)
{
freeSpace = default;
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
if (normalizer.Result.IsFailure()) return normalizer.Result;
return BaseFileSystem.Target.GetFreeSpaceSize(out freeSpace, normalizer.Path);
}
public Result GetTotalSpaceSize(out long totalSpace, in Path path)
{
totalSpace = default;
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
if (normalizer.Result.IsFailure()) return normalizer.Result;
return BaseFileSystem.Target.GetTotalSpaceSize(out totalSpace, normalizer.Path);
}
public Result CleanDirectoryRecursively(in Path path)
{
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
if (normalizer.Result.IsFailure()) return normalizer.Result;
return BaseFileSystem.Target.CleanDirectoryRecursively(normalizer.Path);
}
public Result GetFileTimeStampRaw(out FileTimeStampRaw timeStamp, in Path path)
{
timeStamp = default;
var normalizer = new PathNormalizer(new U8Span(path.Str), GetPathNormalizerOption());
if (normalizer.Result.IsFailure()) return normalizer.Result;
return BaseFileSystem.Target.GetFileTimeStampRaw(out timeStamp, normalizer.Path);
}
public Result QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, int queryId, in Path path)
{
return BaseFileSystem.Target.QueryEntry(outBuffer, inBuffer, (QueryId)queryId, new U8Span(path.Str));
}
public void Dispose()
{
BaseFileSystem?.Dispose();
}
private PathNormalizer.Option GetPathNormalizerOption()
{
return IsHostFsRoot ? PathNormalizer.Option.PreserveUnc : PathNormalizer.Option.None;
}
}
}

View File

@ -0,0 +1,10 @@
using System;
using LibHac.FsSystem;
namespace LibHac.FsSrv.Impl
{
public interface IEntryOpenCountSemaphoreManager : IDisposable
{
Result TryAcquireEntryOpenCountSemaphore(out IUniqueLock semaphore);
}
}

Some files were not shown because too many files have changed in this diff Show More