From 6641109d94066ee8d3fb28e7c94556bbd67a107b Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Mon, 1 Mar 2021 01:22:59 -0700 Subject: [PATCH] Update FS shims for new Horizon and access log code Makes all current FS shims more accurate, including using rewritten access log and system tick handling code. --- build/CodeGen/results.csv | 1 + src/LibHac/Diag/Abort.cs | 11 + src/LibHac/Fs/AccessLog.cs | 291 +++++- src/LibHac/Fs/Accessors/DirectoryAccessor.cs | 44 - src/LibHac/Fs/Accessors/FileAccessor.cs | 113 --- src/LibHac/Fs/Accessors/FileSystemAccessor.cs | 184 ---- src/LibHac/Fs/Accessors/MountTable.cs | 57 -- src/LibHac/Fs/CommonPaths.cs | 1 + src/LibHac/Fs/FileOptions.cs | 2 +- src/LibHac/Fs/FileSystemClient.AccessLog.cs | 158 ---- src/LibHac/Fs/FileSystemClient.cs | 77 +- src/LibHac/Fs/FileSystemClientUtils.cs | 11 - src/LibHac/Fs/FsEnums.cs | 7 - src/LibHac/Fs/Fsa/MountUtility.cs | 25 +- src/LibHac/Fs/Fsa/UserDirectory.cs | 16 +- src/LibHac/Fs/Fsa/UserFile.cs | 29 +- src/LibHac/Fs/Fsa/UserFileSystem.cs | 59 +- src/LibHac/Fs/Fsa/UserFileSystemPrivate.cs | 17 +- src/LibHac/Fs/Host.cs | 31 + src/LibHac/Fs/Impl/CommonMountNames.cs | 81 ++ src/LibHac/Fs/Impl/SaveDataMetaPolicy.cs | 43 + src/LibHac/Fs/MountHelpers.cs | 39 - src/LibHac/Fs/MountName.cs | 16 + src/LibHac/Fs/ResultFs.cs | 2 + src/LibHac/Fs/SaveData.cs | 2 + src/LibHac/Fs/SaveDataStructs.cs | 46 +- src/LibHac/Fs/Shim/Application.cs | 57 +- src/LibHac/Fs/Shim/BcatSaveData.cs | 76 +- src/LibHac/Fs/Shim/Bis.cs | 138 +-- src/LibHac/Fs/Shim/Code.cs | 81 +- src/LibHac/Fs/Shim/Content.cs | 280 ++++-- src/LibHac/Fs/Shim/ContentStorage.cs | 155 ++- src/LibHac/Fs/Shim/CustomStorage.cs | 53 +- .../Fs/Shim/FileSystemProxyServiceObject.cs | 23 +- src/LibHac/Fs/Shim/GameCard.cs | 242 +++-- src/LibHac/Fs/Shim/Host.cs | 617 +++++++----- src/LibHac/Fs/Shim/LoaderApi.cs | 4 +- src/LibHac/Fs/Shim/ProgramIndexMapInfo.cs | 4 +- src/LibHac/Fs/Shim/ProgramRegistry.cs | 10 +- src/LibHac/Fs/Shim/SaveData.cs | 324 ++++--- src/LibHac/Fs/Shim/SaveDataManagement.cs | 887 ++++++++++++------ src/LibHac/Fs/Shim/SdCard.cs | 194 +++- src/LibHac/Fs/Shim/SystemSaveData.cs | 92 +- src/LibHac/FsSrv/FileSystemProxyImpl.cs | 2 +- src/LibHac/FsSrv/FileSystemServer.cs | 9 +- src/LibHac/FsSrv/GameCardHandle.cs | 2 +- src/LibHac/FsSrv/Impl/SaveDataProperties.cs | 3 + src/LibHac/Horizon.cs | 13 +- src/LibHac/HorizonClient.cs | 6 +- src/LibHac/HorizonConfiguration.cs | 22 + src/LibHac/HorizonFactory.cs | 30 +- src/LibHac/Os/Impl/OsResourceManager.cs | 4 +- src/LibHac/Os/Impl/TickManager-os.net.cs | 10 +- src/LibHac/Os/Impl/TickManager.cs | 2 +- src/LibHac/Os/OsState.cs | 4 +- src/hactoolnet/Program.cs | 11 +- .../FileSystemServerFactory.cs | 2 +- tests/LibHac.Tests/HorizonFactory.cs | 2 +- 58 files changed, 2720 insertions(+), 2002 deletions(-) delete mode 100644 src/LibHac/Fs/Accessors/DirectoryAccessor.cs delete mode 100644 src/LibHac/Fs/Accessors/FileAccessor.cs delete mode 100644 src/LibHac/Fs/Accessors/FileSystemAccessor.cs delete mode 100644 src/LibHac/Fs/Accessors/MountTable.cs delete mode 100644 src/LibHac/Fs/FileSystemClient.AccessLog.cs create mode 100644 src/LibHac/Fs/Host.cs create mode 100644 src/LibHac/Fs/Impl/CommonMountNames.cs create mode 100644 src/LibHac/Fs/Impl/SaveDataMetaPolicy.cs delete mode 100644 src/LibHac/Fs/MountHelpers.cs create mode 100644 src/LibHac/Fs/MountName.cs create mode 100644 src/LibHac/HorizonConfiguration.cs diff --git a/build/CodeGen/results.csv b/build/CodeGen/results.csv index 31764a44..89fc2eb6 100644 --- a/build/CodeGen/results.csv +++ b/build/CodeGen/results.csv @@ -175,6 +175,7 @@ Module,DescriptionStart,DescriptionEnd,Flags,Namespace,Name,Summary 2,3005,,,,OutOfRange, 2,3100,,,,SystemPartitionNotReady, +2,3101,,,,StorageDeviceNotReady, 2,3200,3499,,,AllocationMemoryFailed, 2,3211,,,,AllocationMemoryFailedInFileSystemAccessorA, diff --git a/src/LibHac/Diag/Abort.cs b/src/LibHac/Diag/Abort.cs index 25865ef8..5d236f7d 100644 --- a/src/LibHac/Diag/Abort.cs +++ b/src/LibHac/Diag/Abort.cs @@ -16,6 +16,17 @@ namespace LibHac.Diag throw new LibHacException($"Abort: {message}"); } + [DoesNotReturn] + public static void DoAbort(string message = null) + { + if (string.IsNullOrWhiteSpace(message)) + { + throw new LibHacException("Abort."); + } + + throw new LibHacException($"Abort: {message}"); + } + public static void DoAbortUnless([DoesNotReturnIf(false)] bool condition, string message = null) { if (condition) diff --git a/src/LibHac/Fs/AccessLog.cs b/src/LibHac/Fs/AccessLog.cs index 9bab1306..83a780c1 100644 --- a/src/LibHac/Fs/AccessLog.cs +++ b/src/LibHac/Fs/AccessLog.cs @@ -18,6 +18,11 @@ namespace LibHac.Fs public bool IsAccessLogInitialized; public SdkMutexType MutexForAccessLogInitialization; + + public void Initialize(FileSystemClient _) + { + MutexForAccessLogInitialization.Initialize(); + } } [StructLayout(LayoutKind.Sequential, Size = 0x20)] @@ -40,35 +45,18 @@ namespace LibHac.Fs public static class AccessLog { - private static bool HasFileSystemServer(FileSystemClient fs) - { - return fs.Hos is not null; - } - public static Result GetGlobalAccessLogMode(this FileSystemClient fs, out GlobalAccessLogMode mode) { - if (HasFileSystemServer(fs)) - { - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - return fsProxy.Target.GetGlobalAccessLogMode(out mode); - } - - mode = fs.Globals.AccessLog.GlobalAccessLogMode; - return Result.Success; + return fsProxy.Target.GetGlobalAccessLogMode(out mode); } public static Result SetGlobalAccessLogMode(this FileSystemClient fs, GlobalAccessLogMode mode) { - if (HasFileSystemServer(fs)) - { - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - return fsProxy.Target.SetGlobalAccessLogMode(mode); - } - - fs.Globals.AccessLog.GlobalAccessLogMode = mode; - return Result.Success; + return fsProxy.Target.SetGlobalAccessLogMode(mode); } public static void SetLocalAccessLog(this FileSystemClient fs, bool enabled) @@ -104,6 +92,27 @@ namespace LibHac.Fs fs.Globals.AccessLog.LocalAccessLogTarget &= ~AccessLogTarget.Application; } } + + public static Result RunOperationWithAccessLog(this FileSystemClient fs, AccessLogTarget logTarget, + Func operation, Func textGenerator, [CallerMemberName] string caller = "") + { + Result rc; + + if (fs.Impl.IsEnabledAccessLog(logTarget)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = operation(); + Tick end = fs.Hos.Os.GetSystemTick(); + + fs.Impl.OutputAccessLog(rc, start, end, null, textGenerator().ToU8Span(), caller); + } + else + { + rc = operation(); + } + + return rc; + } } } @@ -257,10 +266,10 @@ namespace LibHac.Fs.Impl public ReadOnlySpan ToString(MountHostOption value) { - switch (value) + switch (value.Flags) { - case MountHostOption.PseudoCaseSensitive: return new[] { (byte)'M', (byte)'o', (byte)'u', (byte)'n', (byte)'t', (byte)'H', (byte)'o', (byte)'s', (byte)'t', (byte)'O', (byte)'p', (byte)'t', (byte)'i', (byte)'o', (byte)'n', (byte)'F', (byte)'l', (byte)'a', (byte)'g', (byte)'_', (byte)'P', (byte)'s', (byte)'e', (byte)'u', (byte)'d', (byte)'o', (byte)'C', (byte)'a', (byte)'s', (byte)'e', (byte)'S', (byte)'e', (byte)'n', (byte)'s', (byte)'i', (byte)'t', (byte)'i', (byte)'v', (byte)'e' }; - default: return ToValueString((int)value); + case MountHostOptionFlag.PseudoCaseSensitive: return new[] { (byte)'M', (byte)'o', (byte)'u', (byte)'n', (byte)'t', (byte)'H', (byte)'o', (byte)'s', (byte)'t', (byte)'O', (byte)'p', (byte)'t', (byte)'i', (byte)'o', (byte)'n', (byte)'F', (byte)'l', (byte)'a', (byte)'g', (byte)'_', (byte)'P', (byte)'s', (byte)'e', (byte)'u', (byte)'d', (byte)'o', (byte)'C', (byte)'a', (byte)'s', (byte)'e', (byte)'S', (byte)'e', (byte)'n', (byte)'s', (byte)'i', (byte)'t', (byte)'i', (byte)'v', (byte)'e' }; + default: return ToValueString((int)value.Flags); } } } @@ -438,16 +447,242 @@ namespace LibHac.Fs.Impl public static ReadOnlySpan ConvertFromBoolToAccessLogBooleanValue(bool value) { - return value ? LogTrue : LogFalse; + return value ? AccessLogStrings.LogTrue : AccessLogStrings.LogFalse; } + } - private static ReadOnlySpan LogTrue => // "true" + internal static class AccessLogStrings + { + public static byte LogQuote => (byte)'"'; + + public static ReadOnlySpan LogTrue => // "true" new[] { (byte)'t', (byte)'r', (byte)'u', (byte)'e' }; - private static ReadOnlySpan LogFalse => // "false" + public static ReadOnlySpan LogFalse => // "false" + new[] { (byte)'f', (byte)'a', (byte)'l', (byte)'s', (byte)'e' }; + + public static ReadOnlySpan LogEntryBufferCount => // ", entry_buffer_count: " new[] { - (byte)'f', (byte)'a', (byte)'l', (byte)'s', (byte)'e' + (byte)',', (byte)' ', (byte)'e', (byte)'n', (byte)'t', (byte)'r', (byte)'y', (byte)'_', + (byte)'b', (byte)'u', (byte)'f', (byte)'f', (byte)'e', (byte)'r', (byte)'_', (byte)'c', + (byte)'o', (byte)'u', (byte)'n', (byte)'t', (byte)':', (byte)' ' + }; + + public static ReadOnlySpan LogEntryCount => // ", entry_count: " + new[] + { + (byte)',', (byte)' ', (byte)'e', (byte)'n', (byte)'t', (byte)'r', (byte)'y', (byte)'_', + (byte)'c', (byte)'o', (byte)'u', (byte)'n', (byte)'t', (byte)':', (byte)' ' + }; + + public static ReadOnlySpan LogOffset => // ", offset: " + new[] + { + (byte)',', (byte)' ', (byte)'o', (byte)'f', (byte)'f', (byte)'s', (byte)'e', (byte)'t', + (byte)':', (byte)' ' + }; + + public static ReadOnlySpan LogSize => // ", size: " + new[] + { + (byte)',', (byte)' ', (byte)'s', (byte)'i', (byte)'z', (byte)'e', (byte)':', (byte)' ' + }; + + public static ReadOnlySpan LogWriteOptionFlush => // ", write_option: Flush" + new[] + { + (byte)',', (byte)' ', (byte)'w', (byte)'r', (byte)'i', (byte)'t', (byte)'e', (byte)'_', + (byte)'o', (byte)'p', (byte)'t', (byte)'i', (byte)'o', (byte)'n', (byte)':', (byte)' ', + (byte)'F', (byte)'l', (byte)'u', (byte)'s', (byte)'h' + }; + + public static ReadOnlySpan LogOpenMode => // ", open_mode: 0x" + new[] + { + (byte)',', (byte)' ', (byte)'o', (byte)'p', (byte)'e', (byte)'n', (byte)'_', (byte)'m', + (byte)'o', (byte)'d', (byte)'e', (byte)':', (byte)' ', (byte)'0', (byte)'x' + }; + + public static ReadOnlySpan LogPath => // ", path: "" + new[] + { + (byte)',', (byte)' ', (byte)'p', (byte)'a', (byte)'t', (byte)'h', (byte)':', (byte)' ', + (byte)'"' + }; + + public static ReadOnlySpan LogNewPath => // "", new_path: "" + new[] + { + (byte)'"', (byte)',', (byte)' ', (byte)'n', (byte)'e', (byte)'w', (byte)'_', (byte)'p', + (byte)'a', (byte)'t', (byte)'h', (byte)':', (byte)' ', (byte)'"' + }; + + public static ReadOnlySpan LogEntryType => // "", entry_type: " + new[] + { + (byte)'"', (byte)',', (byte)' ', (byte)'e', (byte)'n', (byte)'t', (byte)'r', (byte)'y', + (byte)'_', (byte)'t', (byte)'y', (byte)'p', (byte)'e', (byte)':', (byte)' ' + }; + + public static ReadOnlySpan LogName => // ", name: "" + new[] + { + (byte)',', (byte)' ', (byte)'n', (byte)'a', (byte)'m', (byte)'e', (byte)':', (byte)' ', + (byte)'"' + }; + + public static ReadOnlySpan LogCommitOption => // "", commit_option: 0x" + new[] + { + (byte)'"', (byte)',', (byte)' ', (byte)'c', (byte)'o', (byte)'m', (byte)'m', (byte)'i', + (byte)'t', (byte)'_', (byte)'o', (byte)'p', (byte)'t', (byte)'i', (byte)'o', (byte)'n', + (byte)':', (byte)' ', (byte)'0', (byte)'x' + }; + + public static ReadOnlySpan LogIsMounted => // "", is_mounted: "" + new[] + { + (byte)'"', (byte)',', (byte)' ', (byte)'i', (byte)'s', (byte)'_', (byte)'m', (byte)'o', + (byte)'u', (byte)'n', (byte)'t', (byte)'e', (byte)'d', (byte)':', (byte)' ', (byte)'"' + }; + + public static ReadOnlySpan LogApplicationId => // ", applicationid: 0x" + new[] + { + (byte)',', (byte)' ', (byte)'a', (byte)'p', (byte)'p', (byte)'l', (byte)'i', (byte)'c', + (byte)'a', (byte)'t', (byte)'i', (byte)'o', (byte)'n', (byte)'i', (byte)'d', (byte)':', + (byte)' ', (byte)'0', (byte)'x' + }; + + public static ReadOnlySpan LogProgramId => // ", programid: 0x" + new[] + { + (byte)',', (byte)' ', (byte)'p', (byte)'r', (byte)'o', (byte)'g', (byte)'r', (byte)'a', + (byte)'m', (byte)'i', (byte)'d', (byte)':', (byte)' ', (byte)'0', (byte)'x' + }; + + public static ReadOnlySpan LogDataId => // ", dataid: 0x" + new[] + { + (byte)',', (byte)' ', (byte)'d', (byte)'a', (byte)'t', (byte)'a', (byte)'i', (byte)'d', + (byte)':', (byte)' ', (byte)'0', (byte)'x' + }; + + public static ReadOnlySpan LogBisPartitionId => // ", bispartitionid: " + new[] + { + (byte)',', (byte)' ', (byte)'b', (byte)'i', (byte)'s', (byte)'p', (byte)'a', (byte)'r', + (byte)'t', (byte)'i', (byte)'t', (byte)'i', (byte)'o', (byte)'n', (byte)'i', (byte)'d', + (byte)':', (byte)' ' + }; + + public static ReadOnlySpan LogContentType => // ", content_type: " + new[] + { + (byte)',', (byte)' ', (byte)'c', (byte)'o', (byte)'n', (byte)'t', (byte)'e', (byte)'n', + (byte)'t', (byte)'_', (byte)'t', (byte)'y', (byte)'p', (byte)'e', (byte)':', (byte)' ' + }; + + public static ReadOnlySpan LogContentStorageId => // ", contentstorageid: " + new[] + { + (byte)',', (byte)' ', (byte)'c', (byte)'o', (byte)'n', (byte)'t', (byte)'e', (byte)'n', + (byte)'t', (byte)'s', (byte)'t', (byte)'o', (byte)'r', (byte)'a', (byte)'g', (byte)'e', + (byte)'i', (byte)'d', (byte)':', (byte)' ' + }; + + public static ReadOnlySpan LogGameCardHandle => // ", gamecard_handle: 0x" + new[] + { + (byte)',', (byte)' ', (byte)'g', (byte)'a', (byte)'m', (byte)'e', (byte)'c', (byte)'a', + (byte)'r', (byte)'d', (byte)'_', (byte)'h', (byte)'a', (byte)'n', (byte)'d', (byte)'l', + (byte)'e', (byte)':', (byte)' ', (byte)'0', (byte)'x' + }; + + public static ReadOnlySpan LogGameCardPartition => // ", gamecard_partition: " + new[] + { + (byte)',', (byte)' ', (byte)'g', (byte)'a', (byte)'m', (byte)'e', (byte)'c', (byte)'a', + (byte)'r', (byte)'d', (byte)'_', (byte)'p', (byte)'a', (byte)'r', (byte)'t', (byte)'i', + (byte)'t', (byte)'i', (byte)'o', (byte)'n', (byte)':', (byte)' ' + }; + + public static ReadOnlySpan LogMountHostOption => // ", mount_host_option: " + new[] + { + (byte)',', (byte)' ', (byte)'m', (byte)'o', (byte)'u', (byte)'n', (byte)'t', (byte)'_', + (byte)'h', (byte)'o', (byte)'s', (byte)'t', (byte)'_', (byte)'o', (byte)'p', (byte)'t', + (byte)'i', (byte)'o', (byte)'n', (byte)':', (byte)' ' + }; + + public static ReadOnlySpan LogRootPath => // ", root_path: "" + new[] + { + (byte)',', (byte)' ', (byte)'r', (byte)'o', (byte)'o', (byte)'t', (byte)'_', (byte)'p', + (byte)'a', (byte)'t', (byte)'h', (byte)':', (byte)' ', (byte)'"' + }; + + public static ReadOnlySpan LogUserId => // ", userid: 0x" + new[] + { + (byte)',', (byte)' ', (byte)'u', (byte)'s', (byte)'e', (byte)'r', (byte)'i', (byte)'d', + (byte)':', (byte)' ', (byte)'0', (byte)'x' + }; + + public static ReadOnlySpan LogIndex => // ", index: " + new[] + { + (byte)',', (byte)' ', (byte)'i', (byte)'n', (byte)'d', (byte)'e', (byte)'x', (byte)':', + (byte)' ' + }; + + public static ReadOnlySpan LogSaveDataOwnerId => // ", save_data_owner_id: 0x" + new[] + { + (byte)',', (byte)' ', (byte)'s', (byte)'a', (byte)'v', (byte)'e', (byte)'_', (byte)'d', + (byte)'a', (byte)'t', (byte)'a', (byte)'_', (byte)'o', (byte)'w', (byte)'n', (byte)'e', + (byte)'r', (byte)'_', (byte)'i', (byte)'d', (byte)':', (byte)' ', (byte)'0', (byte)'x' + }; + + public static ReadOnlySpan LogSaveDataSize => // ", save_data_size: " + new[] + { + (byte)',', (byte)' ', (byte)'s', (byte)'a', (byte)'v', (byte)'e', (byte)'_', (byte)'d', + (byte)'a', (byte)'t', (byte)'a', (byte)'_', (byte)'s', (byte)'i', (byte)'z', (byte)'e', + (byte)':', (byte)' ' + }; + + public static ReadOnlySpan LogSaveDataJournalSize => // ", save_data_journal_size: " + new[] + { + (byte)',', (byte)' ', (byte)'s', (byte)'a', (byte)'v', (byte)'e', (byte)'_', (byte)'d', + (byte)'a', (byte)'t', (byte)'a', (byte)'_', (byte)'j', (byte)'o', (byte)'u', (byte)'r', + (byte)'n', (byte)'a', (byte)'l', (byte)'_', (byte)'s', (byte)'i', (byte)'z', (byte)'e', + (byte)':', (byte)' ' + }; + + public static ReadOnlySpan LogSaveDataFlags => // ", save_data_flags: 0x" + new[] + { + (byte)',', (byte)' ', (byte)'s', (byte)'a', (byte)'v', (byte)'e', (byte)'_', (byte)'d', + (byte)'a', (byte)'t', (byte)'a', (byte)'_', (byte)'f', (byte)'l', (byte)'a', (byte)'g', + (byte)'s', (byte)':', (byte)' ', (byte)'0', (byte)'x' + }; + + public static ReadOnlySpan LogSaveDataId => // ", savedataid: 0x" + new[] + { + (byte)',', (byte)' ', (byte)'s', (byte)'a', (byte)'v', (byte)'e', (byte)'d', (byte)'a', + (byte)'t', (byte)'a', (byte)'i', (byte)'d', (byte)':', (byte)' ', (byte)'0', (byte)'x' + }; + + public static ReadOnlySpan LogSaveDataSpaceId => // ", savedataspaceid: " + new[] + { + (byte)',', (byte)' ', (byte)'s', (byte)'a', (byte)'v', (byte)'e', (byte)'d', (byte)'a', + (byte)'t', (byte)'a', (byte)'s', (byte)'p', (byte)'a', (byte)'c', (byte)'e', (byte)'i', + (byte)'d', (byte)':', (byte)' ' }; } } diff --git a/src/LibHac/Fs/Accessors/DirectoryAccessor.cs b/src/LibHac/Fs/Accessors/DirectoryAccessor.cs deleted file mode 100644 index 21cea24d..00000000 --- a/src/LibHac/Fs/Accessors/DirectoryAccessor.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using LibHac.Fs.Fsa; - -namespace LibHac.Fs.Accessors -{ - public class DirectoryAccessor : IDisposable - { - private IDirectory Directory { get; set; } - - public FileSystemAccessor Parent { get; } - - public DirectoryAccessor(IDirectory baseDirectory, FileSystemAccessor parent) - { - Directory = baseDirectory; - Parent = parent; - } - - public Result Read(out long entriesRead, Span entryBuffer) - { - return Directory.Read(out entriesRead, entryBuffer); - } - - public Result GetEntryCount(out long entryCount) - { - CheckIfDisposed(); - - return Directory.GetEntryCount(out entryCount); - } - - public void Dispose() - { - if (Directory == null) return; - - Parent?.NotifyCloseDirectory(this); - - Directory = null; - } - - private void CheckIfDisposed() - { - if (Directory == null) throw new ObjectDisposedException(null, "Cannot access closed directory."); - } - } -} diff --git a/src/LibHac/Fs/Accessors/FileAccessor.cs b/src/LibHac/Fs/Accessors/FileAccessor.cs deleted file mode 100644 index 1c341f27..00000000 --- a/src/LibHac/Fs/Accessors/FileAccessor.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System; -using LibHac.Fs.Fsa; - -namespace LibHac.Fs.Accessors -{ - public class FileAccessor : IFile - { - private IFile File { get; set; } - - public FileSystemAccessor Parent { get; } - public WriteState WriteState { get; private set; } - public OpenMode OpenMode { get; } - - public FileAccessor(IFile baseFile, FileSystemAccessor parent, OpenMode mode) - { - File = baseFile; - Parent = parent; - OpenMode = mode; - } - - protected override Result DoRead(out long bytesRead, long offset, Span destination, - in ReadOption option) - { - CheckIfDisposed(); - - return File.Read(out bytesRead, offset, destination, in option); - } - - protected override Result DoWrite(long offset, ReadOnlySpan source, in WriteOption option) - { - CheckIfDisposed(); - - if (source.Length == 0) - { - WriteState = (WriteState)(~option.Flags & WriteOptionFlag.Flush); - - return Result.Success; - } - - Result rc = File.Write(offset, source, in option); - - if (rc.IsSuccess()) - { - WriteState = (WriteState)(~option.Flags & WriteOptionFlag.Flush); - } - - return rc; - } - - protected override Result DoFlush() - { - CheckIfDisposed(); - - Result rc = File.Flush(); - - if (rc.IsSuccess()) - { - WriteState = WriteState.None; - } - - return rc; - } - - protected override Result DoGetSize(out long size) - { - CheckIfDisposed(); - - return File.GetSize(out size); - } - - protected override Result DoOperateRange(Span outBuffer, OperationId operationId, long offset, long size, ReadOnlySpan inBuffer) - { - return ResultFs.NotImplemented.Log(); - } - - protected override Result DoSetSize(long size) - { - CheckIfDisposed(); - - return File.SetSize(size); - } - - protected override void Dispose(bool disposing) - { - if (File == null) return; - - if (WriteState == WriteState.Unflushed) - { - // Original FS code would return an error: - // ThrowHelper.ThrowResult(ResultsFs.ResultFsWriteStateUnflushed); - - Flush(); - } - - File.Dispose(); - Parent?.NotifyCloseFile(this); - - File = null; - } - - private void CheckIfDisposed() - { - if (File == null) throw new ObjectDisposedException(null, "Cannot access closed file."); - } - } - - public enum WriteState - { - None, - Unflushed, - Error - } -} diff --git a/src/LibHac/Fs/Accessors/FileSystemAccessor.cs b/src/LibHac/Fs/Accessors/FileSystemAccessor.cs deleted file mode 100644 index 6ee3fcfa..00000000 --- a/src/LibHac/Fs/Accessors/FileSystemAccessor.cs +++ /dev/null @@ -1,184 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using LibHac.Common; -using LibHac.Fs.Fsa; -using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem; - -namespace LibHac.Fs.Accessors -{ - public class FileSystemAccessor - { - public string Name { get; } - - private IFileSystem FileSystem { get; } - internal FileSystemClient FsClient { get; } - private ICommonMountNameGenerator MountNameGenerator { get; } - - private HashSet OpenFiles { get; } = new HashSet(); - private HashSet OpenDirectories { get; } = new HashSet(); - - private readonly object _locker = new object(); - - internal bool IsAccessLogEnabled { get; set; } - public IMultiCommitTarget MultiCommitTarget { get; } - - public FileSystemAccessor(U8Span name, IMultiCommitTarget multiCommitTarget, IFileSystem baseFileSystem, - FileSystemClient fsClient, ICommonMountNameGenerator nameGenerator) - { - Name = name.ToString(); - MultiCommitTarget = multiCommitTarget; - FileSystem = baseFileSystem; - FsClient = fsClient; - MountNameGenerator = nameGenerator; - } - - public Result CreateDirectory(U8Span path) - { - return FileSystem.CreateDirectory(path); - } - - public Result CreateFile(U8Span path, long size, CreateFileOptions options) - { - return FileSystem.CreateFile(path, size, options); - } - - public Result DeleteDirectory(U8Span path) - { - return FileSystem.DeleteDirectory(path); - } - - public Result DeleteDirectoryRecursively(U8Span path) - { - return FileSystem.DeleteDirectoryRecursively(path); - } - - public Result CleanDirectoryRecursively(U8Span path) - { - return FileSystem.CleanDirectoryRecursively(path); - } - - public Result DeleteFile(U8Span path) - { - return FileSystem.DeleteFile(path); - } - - public Result OpenDirectory(out DirectoryAccessor directory, U8Span path, OpenDirectoryMode mode) - { - directory = default; - - Result rc = FileSystem.OpenDirectory(out IDirectory rawDirectory, path, mode); - if (rc.IsFailure()) return rc; - - var accessor = new DirectoryAccessor(rawDirectory, this); - - lock (_locker) - { - OpenDirectories.Add(accessor); - } - - directory = accessor; - return Result.Success; - } - - public Result OpenFile(out FileAccessor file, U8Span path, OpenMode mode) - { - file = default; - - Result rc = FileSystem.OpenFile(out IFile rawFile, path, mode); - if (rc.IsFailure()) return rc; - - var accessor = new FileAccessor(rawFile, this, mode); - - lock (_locker) - { - OpenFiles.Add(accessor); - } - - file = accessor; - return Result.Success; - } - - public Result RenameDirectory(U8Span oldPath, U8Span newPath) - { - return FileSystem.RenameDirectory(oldPath, newPath); - } - - public Result RenameFile(U8Span oldPath, U8Span newPath) - { - return FileSystem.RenameFile(oldPath, newPath); - } - - public Result GetEntryType(out DirectoryEntryType type, U8Span path) - { - return FileSystem.GetEntryType(out type, path); - } - - public Result GetFreeSpaceSize(out long freeSpace, U8Span path) - { - return FileSystem.GetFreeSpaceSize(out freeSpace, path); - } - - public Result GetTotalSpaceSize(out long totalSpace, U8Span path) - { - return FileSystem.GetTotalSpaceSize(out totalSpace, path); - } - - public Result GetFileTimeStampRaw(out FileTimeStampRaw timeStamp, U8Span path) - { - return FileSystem.GetFileTimeStampRaw(out timeStamp, path); - } - - public Result Commit() - { - if (OpenFiles.Any(x => (x.OpenMode & OpenMode.Write) != 0)) - { - return ResultFs.WriteModeFileNotClosed.Log(); - } - - return FileSystem.Commit(); - } - - public Result QueryEntry(Span outBuffer, ReadOnlySpan inBuffer, U8Span path, QueryId queryId) - { - return FileSystem.QueryEntry(outBuffer, inBuffer, queryId, path); - } - - public Result GetCommonMountName(Span nameBuffer) - { - if (MountNameGenerator == null) return ResultFs.PreconditionViolation.Log(); - - return MountNameGenerator.GenerateCommonMountName(nameBuffer); - } - - public ReferenceCountedDisposable GetMultiCommitTarget() - { - return MultiCommitTarget?.GetMultiCommitTarget(); - } - - internal void NotifyCloseFile(FileAccessor file) - { - lock (_locker) - { - OpenFiles.Remove(file); - } - } - - internal void NotifyCloseDirectory(DirectoryAccessor directory) - { - lock (_locker) - { - OpenDirectories.Remove(directory); - } - } - - internal void Close() - { - // Todo: Possibly check for open files and directories - // Nintendo checks for them in DumpUnclosedAccessorList in - // FileSystemAccessor's destructor - - FileSystem?.Dispose(); - } - } -} diff --git a/src/LibHac/Fs/Accessors/MountTable.cs b/src/LibHac/Fs/Accessors/MountTable.cs deleted file mode 100644 index 349dafca..00000000 --- a/src/LibHac/Fs/Accessors/MountTable.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System.Collections.Generic; - -namespace LibHac.Fs.Accessors -{ - public class MountTable - { - private Dictionary Table { get; } = new Dictionary(); - - private readonly object _locker = new object(); - - public Result Mount(FileSystemAccessor fileSystem) - { - lock (_locker) - { - string mountName = fileSystem.Name; - - if (Table.ContainsKey(mountName)) - { - return ResultFs.MountNameAlreadyExists.Log(); - } - - Table.Add(mountName, fileSystem); - - return Result.Success; - } - } - - public Result Find(string name, out FileSystemAccessor fileSystem) - { - lock (_locker) - { - if (!Table.TryGetValue(name, out fileSystem)) - { - return ResultFs.NotMounted.Log(); - } - - return Result.Success; - } - } - - public Result Unmount(string name) - { - lock (_locker) - { - if (!Table.TryGetValue(name, out FileSystemAccessor fsAccessor)) - { - return ResultFs.NotMounted.Log(); - } - - Table.Remove(name); - fsAccessor.Close(); - - return Result.Success; - } - } - } -} diff --git a/src/LibHac/Fs/CommonPaths.cs b/src/LibHac/Fs/CommonPaths.cs index 950b7c9d..cc5d9e7b 100644 --- a/src/LibHac/Fs/CommonPaths.cs +++ b/src/LibHac/Fs/CommonPaths.cs @@ -3,6 +3,7 @@ using LibHac.Common; namespace LibHac.Fs { + // Todo: Migrate to LibHac.Fs.Impl.CommonMountNames and remove internal static class CommonPaths { public const char ReservedMountNamePrefixCharacter = '@'; diff --git a/src/LibHac/Fs/FileOptions.cs b/src/LibHac/Fs/FileOptions.cs index b844520b..0693760e 100644 --- a/src/LibHac/Fs/FileOptions.cs +++ b/src/LibHac/Fs/FileOptions.cs @@ -20,7 +20,7 @@ namespace LibHac.Fs public WriteOption(int flags) { - Flags = (WriteOptionFlag) flags; + Flags = (WriteOptionFlag)flags; } public WriteOption(WriteOptionFlag flags) diff --git a/src/LibHac/Fs/FileSystemClient.AccessLog.cs b/src/LibHac/Fs/FileSystemClient.AccessLog.cs deleted file mode 100644 index 436a41e4..00000000 --- a/src/LibHac/Fs/FileSystemClient.AccessLog.cs +++ /dev/null @@ -1,158 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using LibHac.Common; -using LibHac.Fs.Impl; -using LibHac.Fs.Shim; -using LibHac.FsSrv.Sf; -using LibHac.Sf; -using FileSystemAccessor = LibHac.Fs.Accessors.FileSystemAccessor; - -namespace LibHac.Fs -{ - public partial class FileSystemClient - { - private GlobalAccessLogMode GlobalAccessLogMode { get; set; } - private AccessLogTarget AccessLogTarget { get; set; } - private bool AccessLogInitialized { get; set; } - - private readonly object _accessLogInitLocker = new object(); - - public void SetAccessLogObject(IAccessLog accessLog) - { - AccessLog = accessLog; - } - - internal bool IsEnabledAccessLog(AccessLogTarget target) - { - if ((AccessLogTarget & target) == 0) - { - return false; - } - - if (AccessLogInitialized) - { - return GlobalAccessLogMode != GlobalAccessLogMode.None; - } - - lock (_accessLogInitLocker) - { - if (!AccessLogInitialized) - { - if (HasFileSystemServer()) - { - using ReferenceCountedDisposable fsProxy = - Impl.GetFileSystemProxyServiceObject(); - - Result rc = fsProxy.Target.GetGlobalAccessLogMode(out GlobalAccessLogMode globalMode); - GlobalAccessLogMode = globalMode; - - if (rc.IsFailure()) - { - throw new LibHacException("Abort"); - } - } - else - { - GlobalAccessLogMode = GlobalAccessLogMode.Log; - } - - if (GlobalAccessLogMode != GlobalAccessLogMode.None) - { - InitAccessLog(); - } - - AccessLogInitialized = true; - } - } - - return GlobalAccessLogMode != GlobalAccessLogMode.None; - } - - private void InitAccessLog() - { - - } - - internal void EnableFileSystemAccessorAccessLog(U8Span mountName) - { - if (MountTable.Find(mountName.ToString(), out FileSystemAccessor accessor).IsFailure()) - { - throw new LibHacException("abort"); - } - - accessor.IsAccessLogEnabled = true; - } - - internal void OutputAccessLog(Result result, System.TimeSpan startTime, System.TimeSpan endTime, string message, [CallerMemberName] string caller = "") - { - OutputAccessLogImpl(result, startTime, endTime, 0, message, caller); - } - - internal void OutputAccessLogUnlessResultSuccess(Result result, System.TimeSpan startTime, System.TimeSpan endTime, string message, [CallerMemberName] string caller = "") - { - if (result.IsFailure()) - { - OutputAccessLogImpl(result, startTime, endTime, 0, message, caller); - } - } - - internal void OutputAccessLogImpl(Result result, System.TimeSpan startTime, System.TimeSpan endTime, int handleId, - string message, [CallerMemberName] string caller = "") - { - if (GlobalAccessLogMode.HasFlag(GlobalAccessLogMode.Log)) - { - AccessLog?.Log(result, startTime, endTime, handleId, message, caller); - } - - if (GlobalAccessLogMode.HasFlag(GlobalAccessLogMode.SdCard)) - { - string logString = AccessLogHelpers.BuildDefaultLogLine(result, startTime, endTime, handleId, message, caller); - - using ReferenceCountedDisposable fsProxy = Impl.GetFileSystemProxyServiceObject(); - fsProxy.Target.OutputAccessLogToSdCard(new InBuffer(logString.ToU8Span())).IgnoreResult(); - } - } - - public Result RunOperationWithAccessLog(AccessLogTarget logTarget, Func operation, - Func textGenerator, [CallerMemberName] string caller = "") - { - Result rc; - - if (IsEnabledAccessLog(logTarget)) - { - System.TimeSpan startTime = Time.GetCurrent(); - rc = operation(); - System.TimeSpan endTime = Time.GetCurrent(); - - OutputAccessLog(rc, startTime, endTime, textGenerator(), caller); - } - else - { - rc = operation(); - } - - return rc; - } - - public Result RunOperationWithAccessLogOnFailure(AccessLogTarget logTarget, Func operation, - Func textGenerator, [CallerMemberName] string caller = "") - { - Result rc; - - if (IsEnabledAccessLog(logTarget)) - { - System.TimeSpan startTime = Time.GetCurrent(); - rc = operation(); - System.TimeSpan endTime = Time.GetCurrent(); - - OutputAccessLogUnlessResultSuccess(rc, startTime, endTime, textGenerator(), caller); - } - else - { - rc = operation(); - } - - return rc; - } - } -} diff --git a/src/LibHac/Fs/FileSystemClient.cs b/src/LibHac/Fs/FileSystemClient.cs index 2281acae..2360ea7a 100644 --- a/src/LibHac/Fs/FileSystemClient.cs +++ b/src/LibHac/Fs/FileSystemClient.cs @@ -1,22 +1,19 @@ -using System; -using System.Diagnostics; -using System.Runtime.InteropServices; -using LibHac.Common; -using LibHac.Diag; -using LibHac.Fs.Accessors; -using LibHac.Fs.Fsa; +using LibHac.Fs.Fsa; using LibHac.Fs.Shim; namespace LibHac.Fs { - // Functions in the nn::fssrv::detail namespace use this struct. - public readonly struct FileSystemClientImpl + public class FileSystemClient { - internal readonly FileSystemClient Fs; - internal HorizonClient Hos => Fs.Hos; - internal ref FileSystemClientGlobals Globals => ref Fs.Globals; + internal FileSystemClientGlobals Globals; - internal FileSystemClientImpl(FileSystemClient parentClient) => Fs = parentClient; + public FileSystemClientImpl Impl => new FileSystemClientImpl(this); + internal HorizonClient Hos => Globals.Hos; + + public FileSystemClient(HorizonClient horizonClient) + { + Globals.Initialize(this, horizonClient); + } } internal struct FileSystemClientGlobals @@ -28,54 +25,24 @@ namespace LibHac.Fs public FileSystemProxyServiceObjectGlobals FileSystemProxyServiceObject; public FsContextHandlerGlobals FsContextHandler; public ResultHandlingUtilityGlobals ResultHandlingUtility; - } - public partial class FileSystemClient - { - internal FileSystemClientGlobals Globals; - - public FileSystemClientImpl Impl => new FileSystemClientImpl(this); - internal HorizonClient Hos => Globals.Hos; - - internal ITimeSpanGenerator Time { get; } - private IAccessLog AccessLog { get; set; } - - internal MountTable MountTable { get; } = new MountTable(); - - public FileSystemClient(ITimeSpanGenerator timer) + public void Initialize(FileSystemClient fsClient, HorizonClient horizonClient) { - Time = timer ?? new StopWatchTimeSpanGenerator(); - } - - public FileSystemClient(HorizonClient horizonClient) - { - Time = horizonClient.Time; - - InitializeGlobals(horizonClient); - - Assert.NotNull(Time); - } - - private void InitializeGlobals(HorizonClient horizonClient) - { - Globals.Hos = horizonClient; - Globals.InitMutex = new object(); - Globals.UserMountTable.Initialize(this); - Globals.FsContextHandler.Initialize(this); - } - - public bool HasFileSystemServer() - { - return Hos != null; + Hos = horizonClient; + InitMutex = new object(); + AccessLog.Initialize(fsClient); + UserMountTable.Initialize(fsClient); + FsContextHandler.Initialize(fsClient); } } - [StructLayout(LayoutKind.Sequential, Size = 16)] - [DebuggerDisplay("{ToString()}")] - internal struct MountName + // Functions in the nn::fs::detail namespace use this struct. + public readonly struct FileSystemClientImpl { - public Span Name => SpanHelpers.AsByteSpan(ref this); + internal readonly FileSystemClient Fs; + internal HorizonClient Hos => Fs.Hos; + internal ref FileSystemClientGlobals Globals => ref Fs.Globals; - public override string ToString() => new U8Span(Name).ToString(); + internal FileSystemClientImpl(FileSystemClient parentClient) => Fs = parentClient; } } diff --git a/src/LibHac/Fs/FileSystemClientUtils.cs b/src/LibHac/Fs/FileSystemClientUtils.cs index 4a2911d9..6e3a2dc4 100644 --- a/src/LibHac/Fs/FileSystemClientUtils.cs +++ b/src/LibHac/Fs/FileSystemClientUtils.cs @@ -2,7 +2,6 @@ using System.Buffers; using System.Collections.Generic; using LibHac.Common; -using LibHac.Fs.Accessors; using LibHac.Fs.Fsa; using LibHac.FsSystem; @@ -236,15 +235,5 @@ namespace LibHac.Fs return fs.CreateFile(u8Path, size, CreateFileOptions.None); } - - internal static bool IsEnabledFileSystemAccessorAccessLog(this FileSystemClient fs, string mountName) - { - if (fs.MountTable.Find(mountName, out FileSystemAccessor accessor).IsFailure()) - { - return true; - } - - return accessor.IsAccessLogEnabled; - } } } diff --git a/src/LibHac/Fs/FsEnums.cs b/src/LibHac/Fs/FsEnums.cs index ea16af3b..236998d1 100644 --- a/src/LibHac/Fs/FsEnums.cs +++ b/src/LibHac/Fs/FsEnums.cs @@ -199,13 +199,6 @@ namespace LibHac.Fs SdCard = 2 } - [Flags] - public enum MountHostOption - { - None = 0, - PseudoCaseSensitive = 1 - } - public enum SimulatingDeviceDetectionMode { NoSimulation = 0, diff --git a/src/LibHac/Fs/Fsa/MountUtility.cs b/src/LibHac/Fs/Fsa/MountUtility.cs index 02ffe8b4..1c523ccc 100644 --- a/src/LibHac/Fs/Fsa/MountUtility.cs +++ b/src/LibHac/Fs/Fsa/MountUtility.cs @@ -6,6 +6,7 @@ using LibHac.Fs.Impl; using LibHac.FsSystem; using LibHac.Os; using LibHac.Util; +using static LibHac.Fs.Impl.AccessLogStrings; namespace LibHac.Fs.Fsa { @@ -119,7 +120,7 @@ namespace LibHac.Fs.Fsa if (fs.IsUsedReservedMountName(name)) return ResultFs.InvalidMountName.Log(); - if (fs.IsValidMountName(name)) + if (!fs.IsValidMountName(name)) return ResultFs.InvalidMountName.Log(); return Result.Success; @@ -130,7 +131,7 @@ namespace LibHac.Fs.Fsa if (name.IsNull()) return ResultFs.NullptrArgument.Log(); - if (fs.IsValidMountName(name)) + if (!fs.IsValidMountName(name)) return ResultFs.InvalidMountName.Log(); return Result.Success; @@ -183,9 +184,8 @@ namespace LibHac.Fs.Fsa var sb = new U8StringBuilder(logBuffer, true); sb.Append(LogName).Append(mountName).Append((byte)'"'); - logBuffer = sb.Buffer; - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer)); + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); } else { @@ -210,9 +210,8 @@ namespace LibHac.Fs.Fsa var sb = new U8StringBuilder(logBuffer, true); ReadOnlySpan boolString = AccessLogImpl.ConvertFromBoolToAccessLogBooleanValue(isMounted); sb.Append(LogName).Append(mountName).Append(LogIsMounted).Append(boolString).Append((byte)'"'); - logBuffer = sb.Buffer; - fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer)); + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); } else { @@ -254,19 +253,5 @@ namespace LibHac.Fs.Fsa StringUtils.Copy(commonPathBuffer.Slice(commonPathLength), subPath); return Result.Success; } - - private static ReadOnlySpan LogName => // ", name: "" - new[] - { - (byte)',', (byte)' ', (byte)'n', (byte)'a', (byte)'m', (byte)'e', (byte)':', (byte)' ', - (byte)'"' - }; - - private static ReadOnlySpan LogIsMounted => // "", is_mounted: "" - new[] - { - (byte)'"', (byte)',', (byte)' ', (byte)'i', (byte)'s', (byte)'_', (byte)'m', (byte)'o', - (byte)'u', (byte)'n', (byte)'t', (byte)'e', (byte)'d', (byte)':', (byte)' ', (byte)'"' - }; } } diff --git a/src/LibHac/Fs/Fsa/UserDirectory.cs b/src/LibHac/Fs/Fsa/UserDirectory.cs index b329b228..0894d778 100644 --- a/src/LibHac/Fs/Fsa/UserDirectory.cs +++ b/src/LibHac/Fs/Fsa/UserDirectory.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; using LibHac.Common; using LibHac.Fs.Impl; using LibHac.Os; +using static LibHac.Fs.Impl.AccessLogStrings; namespace LibHac.Fs.Fsa { @@ -81,20 +82,5 @@ namespace LibHac.Fs.Fsa Get(handle).Dispose(); } } - - private static ReadOnlySpan LogEntryBufferCount => // ", entry_buffer_count: " - new[] - { - (byte)',', (byte)' ', (byte)'e', (byte)'n', (byte)'t', (byte)'r', (byte)'y', (byte)'_', - (byte)'b', (byte)'u', (byte)'f', (byte)'f', (byte)'e', (byte)'r', (byte)'_', (byte)'c', - (byte)'o', (byte)'u', (byte)'n', (byte)'t', (byte)':', (byte)' ' - }; - - private static ReadOnlySpan LogEntryCount => // ", entry_count: " - new[] - { - (byte)',', (byte)' ', (byte)'e', (byte)'n', (byte)'t', (byte)'r', (byte)'y', (byte)'_', - (byte)'c', (byte)'o', (byte)'u', (byte)'n', (byte)'t', (byte)':', (byte)' ' - }; } } diff --git a/src/LibHac/Fs/Fsa/UserFile.cs b/src/LibHac/Fs/Fsa/UserFile.cs index 376e8a1f..fe028476 100644 --- a/src/LibHac/Fs/Fsa/UserFile.cs +++ b/src/LibHac/Fs/Fsa/UserFile.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; using LibHac.Common; using LibHac.Fs.Impl; using LibHac.Os; +using static LibHac.Fs.Impl.AccessLogStrings; namespace LibHac.Fs.Fsa { @@ -222,33 +223,5 @@ namespace LibHac.Fs.Fsa fs.Impl.AbortIfNeeded(rc); return rc; } - - private static ReadOnlySpan LogOffset => // ", offset: " - new[] - { - (byte)',', (byte)' ', (byte)'o', (byte)'f', (byte)'f', (byte)'s', (byte)'e', (byte)'t', - (byte)':', (byte)' ' - }; - - private static ReadOnlySpan LogSize => // ", size: " - new[] - { - (byte)',', (byte)' ', (byte)'s', (byte)'i', (byte)'z', (byte)'e', (byte)':', (byte)' ' - }; - - private static ReadOnlySpan LogWriteOptionFlush => // ", write_option: Flush" - new[] - { - (byte)',', (byte)' ', (byte)'w', (byte)'r', (byte)'i', (byte)'t', (byte)'e', (byte)'_', - (byte)'o', (byte)'p', (byte)'t', (byte)'i', (byte)'o', (byte)'n', (byte)':', (byte)' ', - (byte)'F', (byte)'l', (byte)'u', (byte)'s', (byte)'h' - }; - - private static ReadOnlySpan LogOpenMode => // ", open_mode: 0x" - new[] - { - (byte)',', (byte)' ', (byte)'o', (byte)'p', (byte)'e', (byte)'n', (byte)'_', (byte)'m', - (byte)'o', (byte)'d', (byte)'e', (byte)':', (byte)' ', (byte)'0', (byte)'x' - }; } } diff --git a/src/LibHac/Fs/Fsa/UserFileSystem.cs b/src/LibHac/Fs/Fsa/UserFileSystem.cs index 7592dd2a..91f453fe 100644 --- a/src/LibHac/Fs/Fsa/UserFileSystem.cs +++ b/src/LibHac/Fs/Fsa/UserFileSystem.cs @@ -5,6 +5,7 @@ using LibHac.Fs.Impl; using LibHac.Fs.Shim; using LibHac.FsSrv.Sf; using LibHac.Os; +using static LibHac.Fs.Impl.AccessLogStrings; using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem; namespace LibHac.Fs.Fsa @@ -436,7 +437,7 @@ namespace LibHac.Fs.Fsa Tick end = fs.Hos.Os.GetSystemTick(); var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogPath).Append(path).Append(LogSize) + sb.Append(LogPath).Append(path).Append((byte)'"').Append(LogSize) .AppendFormat(AccessLogImpl.DereferenceOutValue(in freeSpace, rc)); logBuffer = sb.Buffer; @@ -465,7 +466,7 @@ namespace LibHac.Fs.Fsa Tick end = fs.Hos.Os.GetSystemTick(); var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogPath).Append(path).Append(LogSize) + sb.Append(LogPath).Append(path).Append((byte)'"').Append(LogSize) .AppendFormat(AccessLogImpl.DereferenceOutValue(in freeSpace, rc)); logBuffer = sb.Buffer; @@ -495,7 +496,7 @@ namespace LibHac.Fs.Fsa Tick end = fs.Hos.Os.GetSystemTick(); var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogPath).Append(path).Append(LogOpenMode).AppendFormat((int)mode, 'X'); + sb.Append(LogPath).Append(path).Append((byte)'"').Append(LogOpenMode).AppendFormat((int)mode, 'X'); logBuffer = sb.Buffer; fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer)); @@ -552,7 +553,7 @@ namespace LibHac.Fs.Fsa Tick end = fs.Hos.Os.GetSystemTick(); var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogPath).Append(path).Append(LogOpenMode).AppendFormat((int)mode, 'X'); + sb.Append(LogPath).Append(path).Append((byte)'"').Append(LogOpenMode).AppendFormat((int)mode, 'X'); logBuffer = sb.Buffer; fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer)); @@ -751,55 +752,5 @@ namespace LibHac.Fs.Fsa { return CommitImpl(fs, mountName); } - - private static ReadOnlySpan LogPath => // ", path: "" - new[] - { - (byte)',', (byte)' ', (byte)'p', (byte)'a', (byte)'t', (byte)'h', (byte)':', (byte)' ', - (byte)'"' - }; - - private static ReadOnlySpan LogNewPath => // "", new_path: "" - new[] - { - (byte)'"', (byte)',', (byte)' ', (byte)'n', (byte)'e', (byte)'w', (byte)'_', (byte)'p', - (byte)'a', (byte)'t', (byte)'h', (byte)':', (byte)' ', (byte)'"' - }; - - private static ReadOnlySpan LogEntryType => // "", entry_type: " - new[] - { - (byte)'"', (byte)',', (byte)' ', (byte)'e', (byte)'n', (byte)'t', (byte)'r', (byte)'y', - (byte)'_', (byte)'t', (byte)'y', (byte)'p', (byte)'e', (byte)':', (byte)' ' - }; - - private static ReadOnlySpan LogSize => // "", size: " - new[] - { - (byte)'"', (byte)',', (byte)' ', (byte)'s', (byte)'i', (byte)'z', (byte)'e', (byte)':', - (byte)' ' - }; - - private static ReadOnlySpan LogOpenMode => // "", open_mode: 0x" - new[] - { - (byte)'"', (byte)',', (byte)' ', (byte)'o', (byte)'p', (byte)'e', (byte)'n', (byte)'_', - (byte)'m', (byte)'o', (byte)'d', (byte)'e', (byte)':', (byte)' ', (byte)'0', (byte)'x' - }; - - private static ReadOnlySpan LogName => // ", name: "" - new[] - { - (byte)',', (byte)' ', (byte)'n', (byte)'a', (byte)'m', (byte)'e', (byte)':', (byte)' ', - (byte)'"' - }; - - private static ReadOnlySpan LogCommitOption => // "", commit_option: 0x" - new[] - { - (byte)'"', (byte)',', (byte)' ', (byte)'c', (byte)'o', (byte)'m', (byte)'m', (byte)'i', - (byte)'t', (byte)'_', (byte)'o', (byte)'p', (byte)'t', (byte)'i', (byte)'o', (byte)'n', - (byte)':', (byte)' ', (byte)'0', (byte)'x' - }; } } diff --git a/src/LibHac/Fs/Fsa/UserFileSystemPrivate.cs b/src/LibHac/Fs/Fsa/UserFileSystemPrivate.cs index 5d95c6a7..c4d2f0f5 100644 --- a/src/LibHac/Fs/Fsa/UserFileSystemPrivate.cs +++ b/src/LibHac/Fs/Fsa/UserFileSystemPrivate.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; using LibHac.Common; using LibHac.Fs.Impl; using LibHac.Os; +using static LibHac.Fs.Impl.AccessLogStrings; namespace LibHac.Fs.Fsa { @@ -41,7 +42,7 @@ namespace LibHac.Fs.Fsa Tick end = fs.Hos.Os.GetSystemTick(); var sb = new U8StringBuilder(logBuffer, true); - sb.Append(LogPath).Append(path).Append(LogSize).AppendFormat(size); + sb.Append(LogPath).Append(path).Append((byte)'"').Append(LogSize).AppendFormat(size); logBuffer = sb.Buffer; fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer)); @@ -77,19 +78,5 @@ namespace LibHac.Fs.Fsa fs.Impl.AbortIfNeeded(rc); return rc; } - - private static ReadOnlySpan LogPath => // ", path: "" - new[] - { - (byte)',', (byte)' ', (byte)'p', (byte)'a', (byte)'t', (byte)'h', (byte)':', (byte)' ', - (byte)'"' - }; - - private static ReadOnlySpan LogSize => // "", size: " - new[] - { - (byte)'"', (byte)',', (byte)' ', (byte)'s', (byte)'i', (byte)'z', (byte)'e', (byte)':', - (byte)' ' - }; } } diff --git a/src/LibHac/Fs/Host.cs b/src/LibHac/Fs/Host.cs new file mode 100644 index 00000000..26ab6c46 --- /dev/null +++ b/src/LibHac/Fs/Host.cs @@ -0,0 +1,31 @@ +using System; + +namespace LibHac.Fs +{ + public readonly struct MountHostOption + { + public readonly MountHostOptionFlag Flags; + + public MountHostOption(int flags) + { + Flags = (MountHostOptionFlag)flags; + } + + public MountHostOption(MountHostOptionFlag flags) + { + Flags = flags; + } + + public static MountHostOption None => new MountHostOption(MountHostOptionFlag.None); + + public static MountHostOption PseudoCaseSensitive => + new MountHostOption(MountHostOptionFlag.PseudoCaseSensitive); + } + + [Flags] + public enum MountHostOptionFlag + { + None = 0, + PseudoCaseSensitive = 1 + } +} diff --git a/src/LibHac/Fs/Impl/CommonMountNames.cs b/src/LibHac/Fs/Impl/CommonMountNames.cs new file mode 100644 index 00000000..00f6f108 --- /dev/null +++ b/src/LibHac/Fs/Impl/CommonMountNames.cs @@ -0,0 +1,81 @@ +using System; + +namespace LibHac.Fs.Impl +{ + public static class CommonMountNames + { + public const char ReservedMountNamePrefixCharacter = '@'; + + // Filesystem names. + public static ReadOnlySpan HostRootFileSystemMountName => // "@Host" + new[] { (byte)'@', (byte)'H', (byte)'o', (byte)'s', (byte)'t' }; + + public static ReadOnlySpan SdCardFileSystemMountName => // "@Sdcard" + new[] { (byte)'@', (byte)'S', (byte)'d', (byte)'c', (byte)'a', (byte)'r', (byte)'d' }; + + public static ReadOnlySpan GameCardFileSystemMountName => // "@Gc" + new[] { (byte)'@', (byte)'G', (byte)'c' }; + + public static ReadOnlySpan GameCardFileSystemMountNameUpdateSuffix => // "U" + new[] { (byte)'U' }; + + public static ReadOnlySpan GameCardFileSystemMountNameNormalSuffix => // "N" + new[] { (byte)'N' }; + + public static ReadOnlySpan GameCardFileSystemMountNameSecureSuffix => // "S" + new[] { (byte)'S' }; + + // Built-in storage names. + public static ReadOnlySpan BisCalibrationFilePartitionMountName => // "@CalibFile" + new[] + { + (byte)'@', (byte)'C', (byte)'a', (byte)'l', (byte)'i', (byte)'b', (byte)'F', (byte)'i', + (byte)'l', (byte)'e' + }; + + public static ReadOnlySpan BisSafeModePartitionMountName => // "@Safe" + new[] { (byte)'@', (byte)'S', (byte)'a', (byte)'f', (byte)'e' }; + + public static ReadOnlySpan BisUserPartitionMountName => // "@User" + new[] { (byte)'@', (byte)'U', (byte)'s', (byte)'e', (byte)'r' }; + + public static ReadOnlySpan BisSystemPartitionMountName => // "@System" + new[] { (byte)'@', (byte)'S', (byte)'y', (byte)'s', (byte)'t', (byte)'e', (byte)'m' }; + + //Content storage names. + public static ReadOnlySpan ContentStorageSystemMountName => // "@SystemContent" + new[] + { + (byte)'@', (byte)'S', (byte)'y', (byte)'s', (byte)'t', (byte)'e', (byte)'m', (byte)'C', + (byte)'o', (byte)'n', (byte)'t', (byte)'e', (byte)'n', (byte)'t' + }; + + public static ReadOnlySpan ContentStorageUserMountName => // "@UserContent" + new[] + { + (byte)'@', (byte)'U', (byte)'s', (byte)'e', (byte)'r', (byte)'C', (byte)'o', (byte)'n', + (byte)'t', (byte)'e', (byte)'n', (byte)'t' + }; + + public static ReadOnlySpan ContentStorageSdCardMountName => // "@SdCardContent" + new[] + { + (byte)'@', (byte)'S', (byte)'d', (byte)'C', (byte)'a', (byte)'r', (byte)'d', (byte)'C', + (byte)'o', (byte)'n', (byte)'t', (byte)'e', (byte)'n', (byte)'t' + }; + + // Registered update partition + public static ReadOnlySpan RegisteredUpdatePartitionMountName => // "@RegUpdate" + new[] + { + (byte)'@', (byte)'R', (byte)'e', (byte)'g', (byte)'U', (byte)'p', (byte)'d', (byte)'a', + (byte)'t', (byte)'e' + }; + + public static ReadOnlySpan SdCardNintendoRootDirectoryName => // "Nintendo" + new[] + { + (byte)'N', (byte)'i', (byte)'n', (byte)'t', (byte)'e', (byte)'n', (byte)'d', (byte)'o' + }; + } +} diff --git a/src/LibHac/Fs/Impl/SaveDataMetaPolicy.cs b/src/LibHac/Fs/Impl/SaveDataMetaPolicy.cs new file mode 100644 index 00000000..a3c9a394 --- /dev/null +++ b/src/LibHac/Fs/Impl/SaveDataMetaPolicy.cs @@ -0,0 +1,43 @@ +using System.Runtime.CompilerServices; + +namespace LibHac.Fs.Impl +{ + internal readonly struct SaveDataMetaPolicy + { + private readonly SaveDataType _type; + private const int ThumbnailFileSize = 0x40060; + + public SaveDataMetaPolicy(SaveDataType saveType) + { + _type = saveType; + } + + public void GenerateMetaInfo(out SaveDataMetaInfo metaInfo) + { + Unsafe.SkipInit(out metaInfo); + + if (_type == SaveDataType.Account || _type == SaveDataType.Device) + { + metaInfo.Type = SaveDataMetaType.Thumbnail; + metaInfo.Size = ThumbnailFileSize; + } + else + { + metaInfo.Type = SaveDataMetaType.None; + metaInfo.Size = 0; + } + } + + public long GetSaveDataMetaSize() + { + GenerateMetaInfo(out SaveDataMetaInfo metaInfo); + return metaInfo.Size; + } + + public SaveDataMetaType GetSaveDataMetaType() + { + GenerateMetaInfo(out SaveDataMetaInfo metaInfo); + return metaInfo.Type; + } + } +} diff --git a/src/LibHac/Fs/MountHelpers.cs b/src/LibHac/Fs/MountHelpers.cs deleted file mode 100644 index b693509c..00000000 --- a/src/LibHac/Fs/MountHelpers.cs +++ /dev/null @@ -1,39 +0,0 @@ -using LibHac.Common; -using static LibHac.Fs.CommonPaths; - -namespace LibHac.Fs -{ - internal static class MountHelpers - { - public static Result CheckMountName(U8Span name) - { - if (name.IsNull()) return ResultFs.NullptrArgument.Log(); - - if (name.Length > 0 && name[0] == '@') return ResultFs.InvalidMountName.Log(); - if (!CheckMountNameImpl(name)) return ResultFs.InvalidMountName.Log(); - - return Result.Success; - } - - public static Result CheckMountNameAcceptingReservedMountName(U8Span name) - { - if (name.IsNull()) return ResultFs.NullptrArgument.Log(); - - if (!CheckMountNameImpl(name)) return ResultFs.InvalidMountName.Log(); - - return Result.Success; - } - - public static bool IsReservedMountName(U8Span name) - { - return (uint)name.Length > 0 && name[0] == ReservedMountNamePrefixCharacter; - } - - // ReSharper disable once UnusedParameter.Local - private static bool CheckMountNameImpl(U8Span name) - { - // Todo - return true; - } - } -} diff --git a/src/LibHac/Fs/MountName.cs b/src/LibHac/Fs/MountName.cs new file mode 100644 index 00000000..b5873c3a --- /dev/null +++ b/src/LibHac/Fs/MountName.cs @@ -0,0 +1,16 @@ +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using LibHac.Common; + +namespace LibHac.Fs +{ + [StructLayout(LayoutKind.Sequential, Size = 16)] + [DebuggerDisplay("{ToString()}")] + internal struct MountName + { + public Span Name => SpanHelpers.AsByteSpan(ref this); + + public override string ToString() => new U8Span(Name).ToString(); + } +} diff --git a/src/LibHac/Fs/ResultFs.cs b/src/LibHac/Fs/ResultFs.cs index ed9e5abb..9875c198 100644 --- a/src/LibHac/Fs/ResultFs.cs +++ b/src/LibHac/Fs/ResultFs.cs @@ -257,6 +257,8 @@ namespace LibHac.Fs public static Result.Base OutOfRange => new Result.Base(ModuleFs, 3005); /// Error code: 2002-3100; Inner value: 0x183802 public static Result.Base SystemPartitionNotReady => new Result.Base(ModuleFs, 3100); + /// Error code: 2002-3101; Inner value: 0x183a02 + public static Result.Base StorageDeviceNotReady => new Result.Base(ModuleFs, 3101); /// Error code: 2002-3200; Range: 3200-3499; Inner value: 0x190002 public static Result.Base AllocationMemoryFailed { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 3200, 3499); } diff --git a/src/LibHac/Fs/SaveData.cs b/src/LibHac/Fs/SaveData.cs index f7be3c2a..be85e166 100644 --- a/src/LibHac/Fs/SaveData.cs +++ b/src/LibHac/Fs/SaveData.cs @@ -7,5 +7,7 @@ namespace LibHac.Fs public const ulong SaveIndexerId = 0x8000000000000000; public static ProgramId InvalidProgramId => ProgramId.InvalidId; public static ProgramId AutoResolveCallerProgramId => new ProgramId(ulong.MaxValue - 1); + public static UserId InvalidUserId => UserId.InvalidId; + } } \ No newline at end of file diff --git a/src/LibHac/Fs/SaveDataStructs.cs b/src/LibHac/Fs/SaveDataStructs.cs index c88adfdc..bd1adae6 100644 --- a/src/LibHac/Fs/SaveDataStructs.cs +++ b/src/LibHac/Fs/SaveDataStructs.cs @@ -2,6 +2,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using LibHac.Common; +using LibHac.FsSrv.Impl; using LibHac.Ncm; using LibHac.Util; @@ -109,6 +110,39 @@ namespace LibHac.Fs } } + [StructLayout(LayoutKind.Explicit, Size = 0x40)] + public struct SaveDataCreationInfo + { + [FieldOffset(0x00)] public long Size; + [FieldOffset(0x08)] public long JournalSize; + [FieldOffset(0x10)] public long BlockSize; + [FieldOffset(0x18)] public ulong OwnerId; + [FieldOffset(0x20)] public SaveDataFlags Flags; + [FieldOffset(0x24)] public SaveDataSpaceId SpaceId; + [FieldOffset(0x25)] public bool Field25; + + public static Result Make(out SaveDataCreationInfo creationInfo, long size, long journalSize, ulong ownerId, + SaveDataFlags flags, SaveDataSpaceId spaceId) + { + Unsafe.SkipInit(out creationInfo); + SaveDataCreationInfo tempCreationInfo = default; + + tempCreationInfo.Size = size; + tempCreationInfo.JournalSize = journalSize; + tempCreationInfo.BlockSize = SaveDataProperties.DefaultSaveDataBlockSize; + tempCreationInfo.OwnerId = ownerId; + tempCreationInfo.Flags = flags; + tempCreationInfo.SpaceId = spaceId; + tempCreationInfo.Field25 = false; + + if (!SaveDataTypesValidity.IsValid(in tempCreationInfo)) + return ResultFs.InvalidArgument.Log(); + + creationInfo = tempCreationInfo; + return Result.Success; + } + } + [StructLayout(LayoutKind.Explicit, Size = 0x48)] public struct SaveDataFilter { @@ -230,18 +264,6 @@ namespace LibHac.Fs [FieldOffset(4)] public SaveDataMetaType Type; } - [StructLayout(LayoutKind.Explicit, Size = 0x40)] - public struct SaveDataCreationInfo - { - [FieldOffset(0x00)] public long Size; - [FieldOffset(0x08)] public long JournalSize; - [FieldOffset(0x10)] public long BlockSize; - [FieldOffset(0x18)] public ulong OwnerId; - [FieldOffset(0x20)] public SaveDataFlags Flags; - [FieldOffset(0x24)] public SaveDataSpaceId SpaceId; - [FieldOffset(0x25)] public bool Field25; - } - [StructLayout(LayoutKind.Explicit, Size = 0x60)] public struct SaveDataInfo { diff --git a/src/LibHac/Fs/Shim/Application.cs b/src/LibHac/Fs/Shim/Application.cs index 59352b5e..a3271acd 100644 --- a/src/LibHac/Fs/Shim/Application.cs +++ b/src/LibHac/Fs/Shim/Application.cs @@ -1,59 +1,72 @@ -using LibHac.Common; +using System; +using System.Runtime.CompilerServices; +using LibHac.Common; using LibHac.Fs.Fsa; using LibHac.Fs.Impl; using LibHac.FsSrv.Sf; +using LibHac.Os; +using static LibHac.Fs.Impl.AccessLogStrings; using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem; namespace LibHac.Fs.Shim { + [SkipLocalsInit] public static class Application { public static Result MountApplicationPackage(this FileSystemClient fs, U8Span mountName, U8Span path) { Result rc; - if (fs.IsEnabledAccessLog(AccessLogTarget.System)) + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) { - System.TimeSpan startTime = fs.Time.GetCurrent(); - rc = Run(fs, mountName, path); - System.TimeSpan endTime = fs.Time.GetCurrent(); + Tick start = fs.Hos.Os.GetSystemTick(); + rc = Mount(fs, mountName, path); + Tick end = fs.Hos.Os.GetSystemTick(); - fs.OutputAccessLog(rc, startTime, endTime, ""); + Span logBuffer = stackalloc byte[0x300]; + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogName).Append(mountName).Append(LogQuote) + .Append(LogPath).Append(path).Append(LogQuote); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); } else { - rc = Run(fs, mountName, path); + rc = Mount(fs, mountName, path); } - + fs.Impl.AbortIfNeeded(rc); if (rc.IsFailure()) return rc; - if (fs.IsEnabledAccessLog(AccessLogTarget.System)) - { - fs.EnableFileSystemAccessorAccessLog(mountName); - } + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) + fs.Impl.EnableFileSystemAccessorAccessLog(mountName); return Result.Success; - static Result Run(FileSystemClient fs, U8Span mountName, U8Span path) + static Result Mount(FileSystemClient fs, U8Span mountName, U8Span path) { - // ReSharper disable once VariableHidesOuterVariable - Result rc = MountHelpers.CheckMountName(mountName); + Result rc = fs.Impl.CheckMountName(mountName); if (rc.IsFailure()) return rc; - FspPath.FromSpan(out FspPath sfPath, path); + rc = FspPath.FromSpan(out FspPath sfPath, path); + if (rc.IsFailure()) return rc; using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - rc = fsProxy.Target.OpenFileSystemWithId(out ReferenceCountedDisposable fileSystem, - in sfPath, default, FileSystemProxyType.Package); - if (rc.IsFailure()) return rc; - - using (fileSystem) + ReferenceCountedDisposable fileSystem = null; + try { - var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem); + rc = fsProxy.Target.OpenFileSystemWithId(out fileSystem, in sfPath, Ncm.ProgramId.InvalidId.Value, + FileSystemProxyType.Package); + if (rc.IsFailure()) return rc; + var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem); return fs.Register(mountName, fileSystemAdapter); } + finally + { + fileSystem?.Dispose(); + } } } } diff --git a/src/LibHac/Fs/Shim/BcatSaveData.cs b/src/LibHac/Fs/Shim/BcatSaveData.cs index 81ce6010..1ca45cd0 100644 --- a/src/LibHac/Fs/Shim/BcatSaveData.cs +++ b/src/LibHac/Fs/Shim/BcatSaveData.cs @@ -1,65 +1,73 @@ -using LibHac.Common; +using System; +using System.Runtime.CompilerServices; +using LibHac.Common; using LibHac.Fs.Fsa; using LibHac.Fs.Impl; using LibHac.FsSrv.Sf; +using LibHac.Os; +using static LibHac.Fs.Impl.AccessLogStrings; using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem; namespace LibHac.Fs.Shim { + [SkipLocalsInit] public static class BcatSaveData { - public static Result MountBcatSaveData(this FileSystemClient fs, U8Span mountName, Ncm.ApplicationId applicationId) + public static Result MountBcatSaveData(this FileSystemClient fs, U8Span mountName, + Ncm.ApplicationId applicationId) { Result rc; - if (fs.IsEnabledAccessLog(AccessLogTarget.System)) + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) { - System.TimeSpan startTime = fs.Time.GetCurrent(); - rc = MountBcatSaveDataImpl(fs, mountName, applicationId); - System.TimeSpan endTime = fs.Time.GetCurrent(); + Tick start = fs.Hos.Os.GetSystemTick(); + rc = Mount(fs, mountName, applicationId); + Tick end = fs.Hos.Os.GetSystemTick(); - string logMessage = $", name: \"{mountName.ToString()}\", applicationid: 0x{applicationId}\""; + Span logBuffer = stackalloc byte[0x50]; + var sb = new U8StringBuilder(logBuffer, true); - fs.OutputAccessLog(rc, startTime, endTime, logMessage); + sb.Append(LogName).Append(mountName).Append(LogQuote) + .Append(LogApplicationId).AppendFormat(applicationId.Value, 'X'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); } else { - rc = MountBcatSaveDataImpl(fs, mountName, applicationId); + rc = Mount(fs, mountName, applicationId); } - + fs.Impl.AbortIfNeeded(rc); if (rc.IsFailure()) return rc; - if (fs.IsEnabledAccessLog(AccessLogTarget.System)) - { - fs.EnableFileSystemAccessorAccessLog(mountName); - } + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) + fs.Impl.EnableFileSystemAccessorAccessLog(mountName); return Result.Success; - } - private static Result MountBcatSaveDataImpl(FileSystemClient fs, U8Span mountName, Ncm.ApplicationId applicationId) - { - Result rc = MountHelpers.CheckMountName(mountName); - if (rc.IsFailure()) return rc; - - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - - var attribute = new SaveDataAttribute(applicationId, SaveDataType.Bcat, UserId.InvalidId, 0); - - ReferenceCountedDisposable saveFs = null; - - try + static Result Mount(FileSystemClient fs, U8Span mountName, Ncm.ApplicationId applicationId) { - rc = fsProxy.Target.OpenSaveDataFileSystem(out saveFs, SaveDataSpaceId.User, in attribute); + Result rc = fs.Impl.CheckMountName(mountName); if (rc.IsFailure()) return rc; - var fileSystemAdapter = new FileSystemServiceObjectAdapter(saveFs); + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - return fs.Register(mountName, fileSystemAdapter); - } - finally - { - saveFs?.Dispose(); + rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Bcat, + Fs.SaveData.InvalidUserId, 0); + if (rc.IsFailure()) return rc; + + ReferenceCountedDisposable fileSystem = null; + try + { + rc = fsProxy.Target.OpenSaveDataFileSystem(out fileSystem, SaveDataSpaceId.User, in attribute); + if (rc.IsFailure()) return rc; + + var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem); + return fs.Register(mountName, fileSystemAdapter); + } + finally + { + fileSystem?.Dispose(); + } } } } diff --git a/src/LibHac/Fs/Shim/Bis.cs b/src/LibHac/Fs/Shim/Bis.cs index c65df51e..e62cefdf 100644 --- a/src/LibHac/Fs/Shim/Bis.cs +++ b/src/LibHac/Fs/Shim/Bis.cs @@ -2,17 +2,21 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using LibHac.Common; +using LibHac.Diag; using LibHac.Fs.Fsa; using LibHac.Fs.Impl; using LibHac.FsSrv.Sf; using LibHac.FsSystem; +using LibHac.Os; using LibHac.Util; -using static LibHac.Fs.CommonPaths; +using static LibHac.Fs.Impl.CommonMountNames; +using static LibHac.Fs.Impl.AccessLogStrings; using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem; using IStorageSf = LibHac.FsSrv.Sf.IStorage; namespace LibHac.Fs.Shim { + [SkipLocalsInit] public static class Bis { private class BisCommonMountNameGenerator : ICommonMountNameGenerator @@ -28,7 +32,7 @@ namespace LibHac.Fs.Shim public Result GenerateCommonMountName(Span nameBuffer) { - U8Span mountName = GetBisMountName(PartitionId); + ReadOnlySpan mountName = GetBisMountName(PartitionId); // Add 2 for the mount name separator and null terminator // ReSharper disable once RedundantAssignment @@ -45,72 +49,77 @@ namespace LibHac.Fs.Shim } } - public static Result MountBis(this FileSystemClient fs, U8Span mountName, BisPartitionId partitionId) - { - return MountBis(fs, mountName, partitionId, default); - } - - public static Result MountBis(this FileSystemClient fs, BisPartitionId partitionId, U8Span rootPath) - { - return MountBis(fs, GetBisMountName(partitionId), partitionId, rootPath); - } - - // nn::fs::detail::MountBis - private static Result MountBis(FileSystemClient fs, U8Span mountName, BisPartitionId partitionId, U8Span rootPath) + private static Result MountBis(this FileSystemClientImpl fs, U8Span mountName, BisPartitionId partitionId, U8Span rootPath) { Result rc; if (fs.IsEnabledAccessLog(AccessLogTarget.System)) { - System.TimeSpan startTime = fs.Time.GetCurrent(); - rc = MountBisImpl(fs, mountName, partitionId, rootPath); - System.TimeSpan endTime = fs.Time.GetCurrent(); + Tick start = fs.Hos.Os.GetSystemTick(); + rc = Mount(fs, mountName, partitionId); + Tick end = fs.Hos.Os.GetSystemTick(); - string logMessage = $", name: \"{mountName.ToString()}\", bispartitionid: {partitionId}, path: \"{rootPath.ToString()}\""; + Span logBuffer = stackalloc byte[0x300]; + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); - fs.OutputAccessLog(rc, startTime, endTime, logMessage); + sb.Append(LogName).Append(mountName).Append(LogQuote) + .Append(LogBisPartitionId).Append(idString.ToString(partitionId)) + .Append(LogPath).Append(rootPath).Append(LogQuote); + + fs.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); } else { - rc = MountBisImpl(fs, mountName, partitionId, rootPath); + rc = Mount(fs, mountName, partitionId); } - + fs.AbortIfNeeded(rc); if (rc.IsFailure()) return rc; if (fs.IsEnabledAccessLog(AccessLogTarget.System)) - { fs.EnableFileSystemAccessorAccessLog(mountName); - } return Result.Success; - } - // ReSharper disable once UnusedParameter.Local - private static Result MountBisImpl(FileSystemClient fs, U8Span mountName, BisPartitionId partitionId, - U8Span rootPath) - { - Result rc = MountHelpers.CheckMountNameAcceptingReservedMountName(mountName); - if (rc.IsFailure()) return rc; - - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - - // Nintendo doesn't use the provided rootPath - FspPath.CreateEmpty(out FspPath sfPath); - - rc = fsProxy.Target.OpenBisFileSystem(out ReferenceCountedDisposable fileSystem, in sfPath, - partitionId); - if (rc.IsFailure()) return rc; - - using (fileSystem) + static Result Mount(FileSystemClientImpl fs, U8Span mountName, BisPartitionId partitionId) { - var nameGenerator = new BisCommonMountNameGenerator(partitionId); - var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem); + Result rc = fs.CheckMountNameAcceptingReservedMountName(mountName); + if (rc.IsFailure()) return rc; - return fs.Register(mountName, fileSystemAdapter, nameGenerator); + using ReferenceCountedDisposable fsProxy = fs.GetFileSystemProxyServiceObject(); + + // Nintendo doesn't use the provided rootPath + FspPath.CreateEmpty(out FspPath sfPath); + + ReferenceCountedDisposable fileSystem = null; + try + { + rc = fsProxy.Target.OpenBisFileSystem(out fileSystem, in sfPath, partitionId); + if (rc.IsFailure()) return rc; + + var nameGenerator = new BisCommonMountNameGenerator(partitionId); + var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem); + + return fs.Fs.Register(mountName, fileSystemAdapter, nameGenerator); + } + finally + { + fileSystem?.Dispose(); + } } } - public static U8Span GetBisMountName(BisPartitionId partitionId) + public static Result MountBis(this FileSystemClient fs, U8Span mountName, BisPartitionId partitionId) + { + return MountBis(fs.Impl, mountName, partitionId, default); + } + + public static Result MountBis(this FileSystemClient fs, BisPartitionId partitionId, U8Span rootPath) + { + return MountBis(fs.Impl, new U8Span(GetBisMountName(partitionId)), partitionId, rootPath); + } + + public static ReadOnlySpan GetBisMountName(BisPartitionId partitionId) { switch (partitionId) { @@ -124,7 +133,8 @@ namespace LibHac.Fs.Shim case BisPartitionId.BootConfigAndPackage2Part5: case BisPartitionId.BootConfigAndPackage2Part6: case BisPartitionId.CalibrationBinary: - throw new HorizonResultException(default, "The partition specified is not mountable."); + Abort.DoAbort("The partition specified is not mountable."); + break; case BisPartitionId.CalibrationFile: return BisCalibrationFilePartitionMountName; @@ -136,18 +146,24 @@ namespace LibHac.Fs.Shim return BisSystemPartitionMountName; default: - throw new ArgumentOutOfRangeException(nameof(partitionId), partitionId, null); + Abort.UnexpectedDefault(); + break; } + + return ReadOnlySpan.Empty; } - // 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) { Unsafe.SkipInit(out FsPath path); + Result rc; int pathLen = StringUtils.GetLength(rootPath, PathTools.MaxPathLength + 1); if (pathLen > PathTools.MaxPathLength) + { + fs.Impl.LogErrorMessage(ResultFs.TooLongPath.Value); return ResultFs.TooLongPath.Log(); + } if (pathLen > 0) { @@ -156,7 +172,7 @@ namespace LibHac.Fs.Shim : StringTraits.DirectorySeparator; var sb = new U8StringBuilder(path.Str); - Result rc = sb.Append(rootPath).Append(endingSeparator).ToSfPath(); + rc = sb.Append(rootPath).Append(endingSeparator).ToSfPath(); if (rc.IsFailure()) return rc; } else @@ -168,7 +184,9 @@ namespace LibHac.Fs.Shim using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - return fsProxy.Target.SetBisRootForHost(partitionId, in sfPath); + rc = fsProxy.Target.SetBisRootForHost(partitionId, in sfPath); + fs.Impl.LogErrorMessage(rc); + return rc; } public static Result OpenBisPartition(this FileSystemClient fs, out IStorage partitionStorage, @@ -176,23 +194,31 @@ namespace LibHac.Fs.Shim { partitionStorage = default; - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - Result rc = fsProxy.Target.OpenBisStorage(out ReferenceCountedDisposable storage, partitionId); - if (rc.IsFailure()) return rc; - - using (storage) + ReferenceCountedDisposable storage = null; + try { + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + Result rc = fsProxy.Target.OpenBisStorage(out storage, partitionId); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; + var storageAdapter = new StorageServiceObjectAdapter(storage); partitionStorage = storageAdapter; return Result.Success; } + finally + { + storage?.Dispose(); + } } public static Result InvalidateBisCache(this FileSystemClient fs) { using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - return fsProxy.Target.InvalidateBisCache(); + Result rc = fsProxy.Target.InvalidateBisCache(); + fs.Impl.AbortIfNeeded(rc); + return rc; } } } diff --git a/src/LibHac/Fs/Shim/Code.cs b/src/LibHac/Fs/Shim/Code.cs index 401579fb..2502f1d9 100644 --- a/src/LibHac/Fs/Shim/Code.cs +++ b/src/LibHac/Fs/Shim/Code.cs @@ -1,65 +1,80 @@ -using System.Runtime.CompilerServices; +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 LibHac.Os; +using static LibHac.Fs.Impl.AccessLogStrings; using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem; namespace LibHac.Fs.Shim { + [SkipLocalsInit] public static class Code { public static Result MountCode(this FileSystemClient fs, out CodeVerificationData verificationData, U8Span mountName, U8Span path, ProgramId programId) { Result rc; - - if (fs.IsEnabledAccessLog(AccessLogTarget.System)) + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) { - System.TimeSpan startTime = fs.Time.GetCurrent(); - rc = MountCodeImpl(fs, out verificationData, mountName, path, programId); - System.TimeSpan endTime = fs.Time.GetCurrent(); + Tick start = fs.Hos.Os.GetSystemTick(); + rc = Mount(fs, out verificationData, mountName, path, programId); + Tick end = fs.Hos.Os.GetSystemTick(); - fs.OutputAccessLog(rc, startTime, endTime, - $", name: \"{mountName.ToString()}\", name: \"{path.ToString()}\", programid: 0x{programId}"); + Span logBuffer = stackalloc byte[0x300]; + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogName).Append(mountName).Append(LogQuote) + .Append(LogPath).Append(path).Append(LogQuote) + .Append(LogProgramId).AppendFormat(programId.Value, 'X'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); } else { - rc = MountCodeImpl(fs, out verificationData, mountName, path, programId); + rc = Mount(fs, out verificationData, mountName, path, programId); } + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; - if (rc.IsSuccess() && fs.IsEnabledAccessLog(AccessLogTarget.System)) + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) + fs.Impl.EnableFileSystemAccessorAccessLog(mountName); + + return Result.Success; + + static Result Mount(FileSystemClient fs, out CodeVerificationData verificationData, + U8Span mountName, U8Span path, ProgramId programId) { - fs.EnableFileSystemAccessorAccessLog(mountName); - } + Unsafe.SkipInit(out verificationData); - return rc; - } + Result rc = fs.Impl.CheckMountName(mountName); + if (rc.IsFailure()) return rc; - private static Result MountCodeImpl(this FileSystemClient fs, out CodeVerificationData verificationData, - U8Span mountName, U8Span path, ProgramId programId) - { - Unsafe.SkipInit(out verificationData); + if (path.IsNull()) + return ResultFs.NullptrArgument.Log(); - Result rc = MountHelpers.CheckMountName(mountName); - if (rc.IsFailure()) return rc; + rc = FspPath.FromSpan(out FspPath fsPath, path); + if (rc.IsFailure()) return rc; - rc = FspPath.FromSpan(out FspPath fsPath, path); - if (rc.IsFailure()) return rc; + using ReferenceCountedDisposable fsProxy = + fs.Impl.GetFileSystemProxyForLoaderServiceObject(); - using ReferenceCountedDisposable fsProxy = - fs.Impl.GetFileSystemProxyForLoaderServiceObject(); + ReferenceCountedDisposable fileSystem = null; + try + { + rc = fsProxy.Target.OpenCodeFileSystem(out fileSystem, out verificationData, in fsPath, programId); + if (rc.IsFailure()) return rc; - rc = fsProxy.Target.OpenCodeFileSystem(out ReferenceCountedDisposable codeFs, - out verificationData, in fsPath, programId); - if (rc.IsFailure()) return rc; - - using (codeFs) - { - var fileSystemAdapter = new FileSystemServiceObjectAdapter(codeFs); - - return fs.Register(mountName, fileSystemAdapter); + var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem); + return fs.Register(mountName, fileSystemAdapter); + } + finally + { + fileSystem?.Dispose(); + } } } } diff --git a/src/LibHac/Fs/Shim/Content.cs b/src/LibHac/Fs/Shim/Content.cs index 46ccf4ba..457f38a6 100644 --- a/src/LibHac/Fs/Shim/Content.cs +++ b/src/LibHac/Fs/Shim/Content.cs @@ -1,90 +1,260 @@ using System; +using System.Runtime.CompilerServices; using LibHac.Common; +using LibHac.Diag; using LibHac.Fs.Fsa; using LibHac.Fs.Impl; using LibHac.FsSrv.Sf; using LibHac.Ncm; +using LibHac.Os; +using static LibHac.Fs.Impl.AccessLogStrings; using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem; namespace LibHac.Fs.Shim { + [SkipLocalsInit] public static class Content { - // todo: add logging - public static Result MountContent(this FileSystemClient fs, U8Span mountName, U8Span path, ContentType type) + private static FileSystemProxyType ConvertToFileSystemProxyType(ContentType type) { - if (type == ContentType.Meta) - return ResultFs.InvalidArgument.Log(); - - return MountContent(fs, mountName, path, ProgramId.InvalidId, type); - } - - public static Result MountContent(this FileSystemClient fs, U8Span mountName, ProgramId programId, ContentType type) - { - Result rc = MountHelpers.CheckMountNameAcceptingReservedMountName(mountName); - if (rc.IsFailure()) return rc; - - FileSystemProxyType fspType = ConvertToFileSystemProxyType(type); - - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - - rc = fsProxy.Target.OpenFileSystemWithPatch(out ReferenceCountedDisposable fileSystem, - programId, fspType); - if (rc.IsFailure()) return rc; - - using (fileSystem) + switch (type) { - var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem); - - return fs.Register(mountName, fileSystemAdapter); + case ContentType.Meta: return FileSystemProxyType.Meta; + case ContentType.Control: return FileSystemProxyType.Control; + case ContentType.Manual: return FileSystemProxyType.Manual; + case ContentType.Data: return FileSystemProxyType.Data; + default: + Abort.UnexpectedDefault(); + return default; } } - public static Result MountContent(this FileSystemClient fs, U8Span mountName, U8Span path, ProgramId programId, ContentType type) + private static Result MountContentImpl(FileSystemClient fs, U8Span mountName, U8Span path, ulong id, + ContentType contentType) { - Result rc = MountHelpers.CheckMountNameAcceptingReservedMountName(mountName); + Result rc = fs.Impl.CheckMountNameAcceptingReservedMountName(mountName); if (rc.IsFailure()) return rc; - FileSystemProxyType fspType = ConvertToFileSystemProxyType(type); + FileSystemProxyType fsType = ConvertToFileSystemProxyType(contentType); - return MountContentImpl(fs, mountName, path, programId.Value, fspType); - } + if (path.IsNull()) + return ResultFs.NullptrArgument.Log(); - public static Result MountContent(this FileSystemClient fs, U8Span mountName, U8Span path, DataId dataId, ContentType type) - { - Result rc = MountHelpers.CheckMountNameAcceptingReservedMountName(mountName); + rc = FspPath.FromSpan(out FspPath fsPath, path); if (rc.IsFailure()) return rc; - FileSystemProxyType fspType = ConvertToFileSystemProxyType(type); - - return MountContentImpl(fs, mountName, path, dataId.Value, fspType); - } - - private static Result MountContentImpl(FileSystemClient fs, U8Span mountName, U8Span path, ulong id, FileSystemProxyType type) - { - FspPath.FromSpan(out FspPath fsPath, path); - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - Result rc = fsProxy.Target.OpenFileSystemWithId(out ReferenceCountedDisposable fileSystem, - in fsPath, id, type); - if (rc.IsFailure()) return rc; - - using (fileSystem) + ReferenceCountedDisposable fileSystem = null; + try { - var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem); + rc = fsProxy.Target.OpenFileSystemWithId(out fileSystem, in fsPath, id, fsType); + if (rc.IsFailure()) return rc; + var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem); return fs.Register(mountName, fileSystemAdapter); } + finally + { + fileSystem?.Dispose(); + } } - private static FileSystemProxyType ConvertToFileSystemProxyType(ContentType type) => type switch + public static Result MountContent(this FileSystemClient fs, U8Span mountName, U8Span path, + ContentType contentType) { - ContentType.Meta => FileSystemProxyType.Meta, - ContentType.Control => FileSystemProxyType.Control, - ContentType.Manual => FileSystemProxyType.Manual, - ContentType.Data => FileSystemProxyType.Data, - _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) - }; + Result rc; + Span logBuffer = stackalloc byte[0x300]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = PreMount(contentType); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogName).Append(mountName).Append(LogQuote) + .Append(LogPath).Append(path).Append(LogQuote) + .Append(LogContentType).Append(idString.ToString(contentType)); + + fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = PreMount(contentType); + } + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; + + const ulong programId = 0; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = MountContentImpl(fs, mountName, path, programId, contentType); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogName).Append(mountName).Append(LogQuote) + .Append(LogPath).Append(path).Append(LogQuote) + .Append(LogProgramId).AppendFormat(programId, 'X') + .Append(LogContentType).Append(idString.ToString(contentType)); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = MountContentImpl(fs, mountName, path, 0, contentType); + } + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) + fs.Impl.EnableFileSystemAccessorAccessLog(mountName); + + return Result.Success; + + static Result PreMount(ContentType contentType) + { + if (contentType == ContentType.Meta) + return ResultFs.InvalidArgument.Log(); + + return Result.Success; + } + } + + public static Result MountContent(this FileSystemClient fs, U8Span mountName, U8Span path, ProgramId programId, + ContentType contentType) + { + Result rc; + Span logBuffer = stackalloc byte[0x300]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = MountContentImpl(fs, mountName, path, programId.Value, contentType); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogName).Append(mountName).Append(LogQuote) + .Append(LogPath).Append(path).Append(LogQuote) + .Append(LogProgramId).AppendFormat(programId.Value, 'X') + .Append(LogContentType).Append(idString.ToString(contentType)); + + logBuffer = sb.Buffer; + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer)); + } + else + { + rc = MountContentImpl(fs, mountName, path, programId.Value, contentType); + } + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) + fs.Impl.EnableFileSystemAccessorAccessLog(mountName); + + return Result.Success; + } + + public static Result MountContent(this FileSystemClient fs, U8Span mountName, U8Span path, DataId dataId, + ContentType contentType) + { + Result rc; + Span logBuffer = stackalloc byte[0x300]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = MountContentImpl(fs, mountName, path, dataId.Value, contentType); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogName).Append(mountName).Append(LogQuote) + .Append(LogPath).Append(path).Append(LogQuote) + .Append(LogDataId).AppendFormat(dataId.Value, 'X') + .Append(LogContentType).Append(idString.ToString(contentType)); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = MountContentImpl(fs, mountName, path, dataId.Value, contentType); + } + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) + fs.Impl.EnableFileSystemAccessorAccessLog(mountName); + + return Result.Success; + } + + public static Result MountContent(this FileSystemClient fs, U8Span mountName, ProgramId programId, + ContentType contentType) + { + Result rc; + Span logBuffer = stackalloc byte[0x300]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = Mount(fs, mountName, programId, contentType); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogName).Append(mountName).Append(LogQuote) + .Append(LogProgramId).AppendFormat(programId.Value, 'X') + .Append(LogContentType).Append(idString.ToString(contentType)); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = Mount(fs, mountName, programId, contentType); + } + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) + fs.Impl.EnableFileSystemAccessorAccessLog(mountName); + + return Result.Success; + + static Result Mount(FileSystemClient fs, U8Span mountName, ProgramId programId, ContentType contentType) + { + Result rc = fs.Impl.CheckMountNameAcceptingReservedMountName(mountName); + if (rc.IsFailure()) return rc; + + FileSystemProxyType fsType = ConvertToFileSystemProxyType(contentType); + + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + ReferenceCountedDisposable fileSystem = null; + try + { + rc = fsProxy.Target.OpenFileSystemWithPatch(out fileSystem, programId, fsType); + if (rc.IsFailure()) return rc; + + var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem); + return fs.Register(mountName, fileSystemAdapter); + } + finally + { + fileSystem?.Dispose(); + } + } + } } } diff --git a/src/LibHac/Fs/Shim/ContentStorage.cs b/src/LibHac/Fs/Shim/ContentStorage.cs index 786ee8ff..9651c76d 100644 --- a/src/LibHac/Fs/Shim/ContentStorage.cs +++ b/src/LibHac/Fs/Shim/ContentStorage.cs @@ -1,56 +1,20 @@ using System; +using System.Runtime.CompilerServices; using LibHac.Common; +using LibHac.Diag; using LibHac.Fs.Fsa; using LibHac.Fs.Impl; using LibHac.FsSrv.Sf; +using LibHac.Os; using LibHac.Util; +using static LibHac.Fs.Impl.AccessLogStrings; using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem; namespace LibHac.Fs.Shim { + [SkipLocalsInit] public static class ContentStorage { - public static Result MountContentStorage(this FileSystemClient fs, ContentStorageId storageId) - { - return MountContentStorage(fs, GetContentStorageMountName(storageId), storageId); - } - - public static Result MountContentStorage(this FileSystemClient fs, U8Span mountName, ContentStorageId storageId) - { - Result rc = MountHelpers.CheckMountNameAcceptingReservedMountName(mountName); - if (rc.IsFailure()) return rc; - - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - - rc = fsProxy.Target.OpenContentStorageFileSystem(out ReferenceCountedDisposable contentFs, - storageId); - if (rc.IsFailure()) return rc; - - using (contentFs) - { - var mountNameGenerator = new ContentStorageCommonMountNameGenerator(storageId); - - var fileSystemAdapter = new FileSystemServiceObjectAdapter(contentFs); - - return fs.Register(mountName, fileSystemAdapter, mountNameGenerator); - } - } - - public static U8String GetContentStorageMountName(ContentStorageId storageId) - { - switch (storageId) - { - case ContentStorageId.System: - return CommonPaths.ContentStorageSystemMountName; - case ContentStorageId.User: - return CommonPaths.ContentStorageUserMountName; - case ContentStorageId.SdCard: - return CommonPaths.ContentStorageSdCardMountName; - default: - throw new ArgumentOutOfRangeException(nameof(storageId), storageId, null); - } - } - private class ContentStorageCommonMountNameGenerator : ICommonMountNameGenerator { private ContentStorageId StorageId { get; } @@ -64,14 +28,115 @@ namespace LibHac.Fs.Shim public Result GenerateCommonMountName(Span nameBuffer) { - U8String mountName = GetContentStorageMountName(StorageId); + // Determine how much space we need. + int neededSize = + StringUtils.GetLength(GetContentStorageMountName(StorageId), PathTool.MountNameLengthMax) + 2; - int length = StringUtils.Copy(nameBuffer, mountName); - nameBuffer[length] = (byte)':'; - nameBuffer[length + 1] = 0; + Assert.True(nameBuffer.Length >= neededSize); + + // Generate the name. + var sb = new U8StringBuilder(nameBuffer); + sb.Append(GetContentStorageMountName(StorageId)) + .Append(StringTraits.DriveSeparator); + + Assert.True(sb.Length == neededSize - 1); return Result.Success; } } + + public static Result MountContentStorage(this FileSystemClient fs, ContentStorageId storageId) + { + return MountContentStorage(fs, new U8Span(GetContentStorageMountName(storageId)), storageId); + } + + public static Result MountContentStorage(this FileSystemClient fs, U8Span mountName, ContentStorageId storageId) + { + Result rc; + Span logBuffer = stackalloc byte[0x40]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = Mount(fs, mountName, storageId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogName).Append(mountName).Append(LogQuote) + .Append(LogContentStorageId).Append(idString.ToString(storageId)); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = Mount(fs, mountName, storageId); + } + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) + fs.Impl.EnableFileSystemAccessorAccessLog(mountName); + + return Result.Success; + + static Result Mount(FileSystemClient fs, U8Span mountName, ContentStorageId storageId) + { + // It can take some time for the system partition to be ready (if it's on the SD card). + // Thus, we will retry up to 10 times, waiting one second each time. + const int maxRetries = 10; + const int retryInterval = 1000; + + Result rc = fs.Impl.CheckMountNameAcceptingReservedMountName(mountName); + if (rc.IsFailure()) return rc; + + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + ReferenceCountedDisposable fileSystem = null; + try + { + for (int i = 0; i < maxRetries; i++) + { + rc = fsProxy.Target.OpenContentStorageFileSystem(out fileSystem, storageId); + + if (rc.IsSuccess()) + break; + + if (!ResultFs.SystemPartitionNotReady.Includes(rc)) + return rc; + + if (i == maxRetries - 1) + return rc; + + fs.Hos.Os.SleepThread(TimeSpan.FromMilliSeconds(retryInterval)); + } + + var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem); + var mountNameGenerator = new ContentStorageCommonMountNameGenerator(storageId); + return fs.Register(mountName, fileSystemAdapter, mountNameGenerator); + } + finally + { + fileSystem?.Dispose(); + } + } + } + + public static ReadOnlySpan GetContentStorageMountName(ContentStorageId storageId) + { + switch (storageId) + { + case ContentStorageId.System: + return CommonMountNames.ContentStorageSystemMountName; + case ContentStorageId.User: + return CommonMountNames.ContentStorageUserMountName; + case ContentStorageId.SdCard: + return CommonMountNames.ContentStorageSdCardMountName; + default: + Abort.UnexpectedDefault(); + return default; + } + } } } diff --git a/src/LibHac/Fs/Shim/CustomStorage.cs b/src/LibHac/Fs/Shim/CustomStorage.cs index c2a06b13..bdca46fe 100644 --- a/src/LibHac/Fs/Shim/CustomStorage.cs +++ b/src/LibHac/Fs/Shim/CustomStorage.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; using LibHac.Common; using LibHac.Diag; using LibHac.Fs.Fsa; @@ -8,31 +9,9 @@ using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem; namespace LibHac.Fs.Shim { + [SkipLocalsInit] public static class CustomStorage { - public static Result MountCustomStorage(this FileSystemClient fs, U8Span mountName, CustomStorageId storageId) - { - Result rc = MountHelpers.CheckMountName(mountName); - if (rc.IsFailure()) return rc; - - ReferenceCountedDisposable customFs = null; - try - { - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - - rc = fsProxy.Target.OpenCustomStorageFileSystem(out customFs, storageId); - if (rc.IsFailure()) return rc; - - var adapter = new FileSystemServiceObjectAdapter(customFs); - - return fs.Register(mountName, adapter); - } - finally - { - customFs?.Dispose(); - } - } - public static U8Span GetCustomStorageDirectoryName(CustomStorageId storageId) { switch (storageId) @@ -46,11 +25,33 @@ namespace LibHac.Fs.Shim } } - private static ReadOnlySpan CustomStorageDirectoryName => // CustomStorage0 + public static Result MountCustomStorage(this FileSystemClient fs, U8Span mountName, CustomStorageId storageId) + { + Result rc = fs.Impl.CheckMountName(mountName); + if (rc.IsFailure()) return rc; + + ReferenceCountedDisposable fileSystem = null; + try + { + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + rc = fsProxy.Target.OpenCustomStorageFileSystem(out fileSystem, storageId); + if (rc.IsFailure()) return rc; + + var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem); + return fs.Register(mountName, fileSystemAdapter); + } + finally + { + fileSystem?.Dispose(); + } + } + + private static ReadOnlySpan CustomStorageDirectoryName => // "CustomStorage0" new[] { - (byte) 'C', (byte) 'u', (byte) 's', (byte) 't', (byte) 'o', (byte) 'm', (byte) 'S', (byte) 't', - (byte) 'o', (byte) 'r', (byte) 'a', (byte) 'g', (byte) 'e', (byte) '0' + (byte)'C', (byte)'u', (byte)'s', (byte)'t', (byte)'o', (byte)'m', (byte)'S', (byte)'t', + (byte)'o', (byte)'r', (byte)'a', (byte)'g', (byte)'e', (byte)'0' }; } } diff --git a/src/LibHac/Fs/Shim/FileSystemProxyServiceObject.cs b/src/LibHac/Fs/Shim/FileSystemProxyServiceObject.cs index 1c498c7d..17c7ec63 100644 --- a/src/LibHac/Fs/Shim/FileSystemProxyServiceObject.cs +++ b/src/LibHac/Fs/Shim/FileSystemProxyServiceObject.cs @@ -1,5 +1,4 @@ -using System; -using LibHac.Common; +using LibHac.Common; using LibHac.FsSrv.Sf; namespace LibHac.Fs.Shim @@ -20,11 +19,6 @@ namespace LibHac.Fs.Shim public static class FileSystemProxyServiceObject { - private static bool HasFileSystemServer(FileSystemClientImpl fs) - { - return fs.Hos is not null; - } - public static ReferenceCountedDisposable GetFileSystemProxyServiceObject( this FileSystemClientImpl fs) { @@ -49,11 +43,6 @@ namespace LibHac.Fs.Shim if (dfcServiceObject is not null) return dfcServiceObject.AddReference(); - if (!HasFileSystemServer(fs)) - { - throw new InvalidOperationException("Client was not initialized with a server object."); - } - Result rc = fs.Hos.Sm.GetService(out ReferenceCountedDisposable fsProxy, "fsp-srv"); if (rc.IsFailure()) @@ -83,11 +72,6 @@ namespace LibHac.Fs.Shim private static ReferenceCountedDisposable GetFileSystemProxyForLoaderServiceObjectImpl(FileSystemClientImpl fs) { - if (!HasFileSystemServer(fs)) - { - throw new InvalidOperationException("Client was not initialized with a server object."); - } - Result rc = fs.Hos.Sm.GetService(out ReferenceCountedDisposable fsProxy, "fsp-ldr"); @@ -118,11 +102,6 @@ namespace LibHac.Fs.Shim private static ReferenceCountedDisposable GetProgramRegistryServiceObjectImpl( FileSystemClientImpl fs) { - if (!HasFileSystemServer(fs)) - { - throw new InvalidOperationException("Client was not initialized with a server object."); - } - Result rc = fs.Hos.Sm.GetService(out ReferenceCountedDisposable registry, "fsp-pr"); if (rc.IsFailure()) diff --git a/src/LibHac/Fs/Shim/GameCard.cs b/src/LibHac/Fs/Shim/GameCard.cs index ba8b18a0..87b5f78a 100644 --- a/src/LibHac/Fs/Shim/GameCard.cs +++ b/src/LibHac/Fs/Shim/GameCard.cs @@ -1,97 +1,32 @@ using System; +using System.Runtime.CompilerServices; using LibHac.Common; +using LibHac.Diag; using LibHac.Fs.Fsa; using LibHac.Fs.Impl; using LibHac.FsSrv; using LibHac.FsSrv.Sf; +using LibHac.Os; +using LibHac.Util; +using static LibHac.Fs.Impl.AccessLogStrings; using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem; using IStorageSf = LibHac.FsSrv.Sf.IStorage; namespace LibHac.Fs.Shim { + [SkipLocalsInit] public static class GameCard { - public static Result GetGameCardHandle(this FileSystemClient fs, out GameCardHandle handle) + private static ReadOnlySpan GetGameCardMountNameSuffix(GameCardPartition partition) { - handle = default; - - ReferenceCountedDisposable deviceOperator = null; - try + switch (partition) { - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - - Result rc = fsProxy.Target.OpenDeviceOperator(out deviceOperator); - if (rc.IsFailure()) return rc; - - return deviceOperator.Target.GetGameCardHandle(out handle); - } - finally - { - deviceOperator?.Dispose(); - } - } - - public static bool IsGameCardInserted(this FileSystemClient fs) - { - ReferenceCountedDisposable deviceOperator = null; - try - { - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - - Result rc = fsProxy.Target.OpenDeviceOperator(out deviceOperator); - if (rc.IsFailure()) throw new LibHacException("Abort"); - - rc = deviceOperator.Target.IsGameCardInserted(out bool isInserted); - if (rc.IsFailure()) throw new LibHacException("Abort"); - - return isInserted; - } - finally - { - deviceOperator?.Dispose(); - } - } - - public static Result OpenGameCardPartition(this FileSystemClient fs, out IStorage storage, - GameCardHandle handle, GameCardPartitionRaw partitionType) - { - storage = default; - - ReferenceCountedDisposable sfStorage = null; - try - { - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - - Result rc = fsProxy.Target.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, - GameCardPartition partitionId) - { - Result rc = MountHelpers.CheckMountNameAcceptingReservedMountName(mountName); - if (rc.IsFailure()) return rc; - - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - - rc = fsProxy.Target.OpenGameCardFileSystem(out ReferenceCountedDisposable cardFs, handle, - partitionId); - if (rc.IsFailure()) return rc; - - using (cardFs) - { - var mountNameGenerator = new GameCardCommonMountNameGenerator(handle, partitionId); - var fileSystemAdapter = new FileSystemServiceObjectAdapter(cardFs); - - return fs.Register(mountName, fileSystemAdapter, mountNameGenerator); + case GameCardPartition.Update: return CommonMountNames.GameCardFileSystemMountNameUpdateSuffix; + case GameCardPartition.Normal: return CommonMountNames.GameCardFileSystemMountNameNormalSuffix; + case GameCardPartition.Secure: return CommonMountNames.GameCardFileSystemMountNameSecureSuffix; + default: + Abort.UnexpectedDefault(); + return default; } } @@ -110,24 +45,153 @@ namespace LibHac.Fs.Shim public Result GenerateCommonMountName(Span nameBuffer) { - char letter = GetGameCardMountNameSuffix(PartitionId); + int handleDigitCount = Unsafe.SizeOf() * 2; - string mountName = $"{CommonPaths.GameCardFileSystemMountName}{letter}{Handle.Value:x8}"; - new U8Span(mountName).Value.CopyTo(nameBuffer); + // Determine how much space we need. + int neededSize = + StringUtils.GetLength(CommonMountNames.GameCardFileSystemMountName, PathTool.MountNameLengthMax) + + StringUtils.GetLength(GetGameCardMountNameSuffix(PartitionId), PathTool.MountNameLengthMax) + + handleDigitCount + 2; + + Assert.True(nameBuffer.Length >= neededSize); + + // Generate the name. + var sb = new U8StringBuilder(nameBuffer); + sb.Append(CommonMountNames.GameCardFileSystemMountName) + .Append(GetGameCardMountNameSuffix(PartitionId)) + .AppendFormat(Handle.Value, 'x', (byte)handleDigitCount) + .Append(StringTraits.DriveSeparator); + + Assert.True(sb.Length == neededSize - 1); return Result.Success; } + } - private static char GetGameCardMountNameSuffix(GameCardPartition partition) + public static Result GetGameCardHandle(this FileSystemClient fs, out GameCardHandle handle) + { + Unsafe.SkipInit(out handle); + + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + ReferenceCountedDisposable deviceOperator = null; + try { - switch (partition) + Result rc = fsProxy.Target.OpenDeviceOperator(out deviceOperator); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; + + rc = deviceOperator.Target.GetGameCardHandle(out handle); + fs.Impl.AbortIfNeeded(rc); + return rc; + } + finally + { + deviceOperator?.Dispose(); + } + } + + public static Result MountGameCardPartition(this FileSystemClient fs, U8Span mountName, GameCardHandle handle, + GameCardPartition partitionId) + { + Result rc; + Span logBuffer = stackalloc byte[0x60]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = Mount(fs, mountName, handle, partitionId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogName).Append(mountName).Append(LogQuote) + .Append(LogGameCardHandle).AppendFormat(handle.Value) + .Append(LogGameCardPartition).Append(idString.ToString(partitionId)); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = Mount(fs, mountName, handle, partitionId); + } + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) + fs.Impl.EnableFileSystemAccessorAccessLog(mountName); + + return Result.Success; + + static Result Mount(FileSystemClient fs, U8Span mountName, GameCardHandle handle, + GameCardPartition partitionId) + { + Result rc = fs.Impl.CheckMountNameAcceptingReservedMountName(mountName); + if (rc.IsFailure()) return rc; + + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + ReferenceCountedDisposable fileSystem = null; + try { - case GameCardPartition.Update: return 'U'; - case GameCardPartition.Normal: return 'N'; - case GameCardPartition.Secure: return 'S'; - default: - throw new ArgumentOutOfRangeException(nameof(partition), partition, null); + rc = fsProxy.Target.OpenGameCardFileSystem(out fileSystem, handle, partitionId); + if (rc.IsFailure()) return rc; + + var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem); + var mountNameGenerator = new GameCardCommonMountNameGenerator(handle, partitionId); + return fs.Register(mountName, fileSystemAdapter, mountNameGenerator); } + finally + { + fileSystem?.Dispose(); + } + } + } + + public static bool IsGameCardInserted(this FileSystemClient fs) + { + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + ReferenceCountedDisposable deviceOperator = null; + try + { + Result rc = fsProxy.Target.OpenDeviceOperator(out deviceOperator); + fs.Impl.LogErrorMessage(rc); + Abort.DoAbortUnless(rc.IsSuccess()); + + rc = deviceOperator.Target.IsGameCardInserted(out bool isInserted); + fs.Impl.LogErrorMessage(rc); + Abort.DoAbortUnless(rc.IsSuccess()); + + return isInserted; + } + finally + { + deviceOperator?.Dispose(); + } + } + + public static Result OpenGameCardPartition(this FileSystemClient fs, out IStorage storage, + GameCardHandle handle, GameCardPartitionRaw partitionType) + { + storage = default; + + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + ReferenceCountedDisposable sfStorage = null; + try + { + Result rc = fsProxy.Target.OpenGameCardStorage(out sfStorage, handle, partitionType); + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; + + storage = new StorageServiceObjectAdapter(sfStorage); + return Result.Success; + } + finally + { + sfStorage?.Dispose(); } } } diff --git a/src/LibHac/Fs/Shim/Host.cs b/src/LibHac/Fs/Shim/Host.cs index 30244401..0eb00010 100644 --- a/src/LibHac/Fs/Shim/Host.cs +++ b/src/LibHac/Fs/Shim/Host.cs @@ -1,13 +1,15 @@ using System; -using System.Diagnostics; using System.Runtime.CompilerServices; using LibHac.Common; +using LibHac.Diag; using LibHac.Fs.Fsa; using LibHac.Fs.Impl; using LibHac.FsSrv.Sf; using LibHac.FsSystem; +using LibHac.Os; using LibHac.Util; -using static LibHac.Fs.CommonPaths; +using static LibHac.Fs.Impl.AccessLogStrings; +using static LibHac.Fs.Impl.CommonMountNames; using IFileSystem = LibHac.Fs.Fsa.IFileSystem; using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem; @@ -16,14 +18,53 @@ namespace LibHac.Fs.Shim /// /// Contains functions for mounting file systems from a host computer. /// - /// Based on nnSdk 9.3.0 + /// Based on nnSdk 11.4.0 + [SkipLocalsInit] public static class Host { - private static ReadOnlySpan HostRootFileSystemPath => new[] - {(byte) '@', (byte) 'H', (byte) 'o', (byte) 's', (byte) 't', (byte) ':', (byte) '/'}; + private static ReadOnlySpan HostRootFileSystemPath => // "@Host:/" + new[] { (byte)'@', (byte)'H', (byte)'o', (byte)'s', (byte)'t', (byte)':', (byte)'/' }; private const int HostRootFileSystemPathLength = 8; + /// + /// Opens a host file system via . + /// + /// The to use. + /// If successful, the opened host file system. + /// The path on the host computer to open. e.g. /C:\Windows\System32/ + /// Options for opening the host file system. + /// The of the operation. + private static Result OpenHostFileSystemImpl(FileSystemClient fs, out IFileSystem fileSystem, in FspPath path, + MountHostOption option) + { + fileSystem = default; + + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + ReferenceCountedDisposable hostFs = null; + try + { + if (option.Flags != MountHostOptionFlag.None) + { + Result rc = fsProxy.Target.OpenHostFileSystemWithOption(out hostFs, in path, option); + if (rc.IsFailure()) return rc; + } + else + { + Result rc = fsProxy.Target.OpenHostFileSystem(out hostFs, in path); + if (rc.IsFailure()) return rc; + } + + fileSystem = new FileSystemServiceObjectAdapter(hostFs); + return Result.Success; + } + finally + { + hostFs?.Dispose(); + } + } + private class HostCommonMountNameGenerator : ICommonMountNameGenerator { private FsPath _path; @@ -43,7 +84,8 @@ namespace LibHac.Fs.Shim public Result GenerateCommonMountName(Span nameBuffer) { - int requiredNameBufferSize = StringUtils.GetLength(_path.Str, FsPath.MaxLength) + HostRootFileSystemPathLength; + int requiredNameBufferSize = + StringUtils.GetLength(_path.Str, FsPath.MaxLength) + HostRootFileSystemPathLength; if (nameBuffer.Length < requiredNameBufferSize) return ResultFs.TooLongPath.Log(); @@ -51,7 +93,7 @@ namespace LibHac.Fs.Shim var sb = new U8StringBuilder(nameBuffer); sb.Append(HostRootFileSystemPath).Append(_path.Str); - Debug.Assert(sb.Length == requiredNameBufferSize - 1); + Assert.True(sb.Length == requiredNameBufferSize - 1); return Result.Success; } @@ -65,226 +107,17 @@ namespace LibHac.Fs.Shim { const int requiredNameBufferSize = HostRootFileSystemPathLength; - Debug.Assert(nameBuffer.Length >= requiredNameBufferSize); + Assert.True(nameBuffer.Length >= requiredNameBufferSize); - // ReSharper disable once RedundantAssignment - int size = StringUtils.Copy(nameBuffer, HostRootFileSystemPath); - Debug.Assert(size == requiredNameBufferSize - 1); + var sb = new U8StringBuilder(nameBuffer); + sb.Append(HostRootFileSystemPath); + + Assert.True(sb.Length == requiredNameBufferSize - 1); return Result.Success; } } - /// - /// Mounts the C:\ drive of a host Windows computer at @Host:/ - /// - /// The to use. - /// The of the operation. - public static Result MountHostRoot(this FileSystemClient fs) - { - IFileSystem hostFileSystem = default; - FspPath.CreateEmpty(out FspPath path); - - static string LogMessageGenerator() => $", name: \"{HostRootFileSystemMountName.ToString()}\""; - - Result OpenHostFs() => OpenHostFileSystemImpl(fs, out hostFileSystem, in path, MountHostOption.None); - - Result MountHostFs() => fs.Register(HostRootFileSystemMountName, hostFileSystem, - new HostRootCommonMountNameGenerator()); - - // Open the host file system - Result result = - fs.RunOperationWithAccessLogOnFailure(AccessLogTarget.Application, OpenHostFs, LogMessageGenerator); - if (result.IsFailure()) return result; - - // Mount the host file system - result = fs.RunOperationWithAccessLog(AccessLogTarget.Application, MountHostFs, LogMessageGenerator); - if (result.IsFailure()) return result; - - if (fs.IsEnabledAccessLog(AccessLogTarget.Application)) - fs.EnableFileSystemAccessorAccessLog(HostRootFileSystemMountName); - - return Result.Success; - } - - /// - /// Mounts the C:\ drive of a host Windows computer at @Host:/ - /// - /// The to use. - /// Options for mounting the host file system. - /// The of the operation. - public static Result MountHostRoot(this FileSystemClient fs, MountHostOption option) - { - IFileSystem hostFileSystem = default; - FspPath.CreateEmpty(out FspPath path); - - string LogMessageGenerator() => - $", name: \"{HostRootFileSystemMountName.ToString()}, mount_host_option: {option}\""; - - Result OpenHostFs() => OpenHostFileSystemImpl(fs, out hostFileSystem, in path, option); - - Result MountHostFs() => fs.Register(HostRootFileSystemMountName, hostFileSystem, - new HostRootCommonMountNameGenerator()); - - // Open the host file system - Result result = - fs.RunOperationWithAccessLogOnFailure(AccessLogTarget.Application, OpenHostFs, LogMessageGenerator); - if (result.IsFailure()) return result; - - // Mount the host file system - result = fs.RunOperationWithAccessLog(AccessLogTarget.Application, MountHostFs, LogMessageGenerator); - if (result.IsFailure()) return result; - - if (fs.IsEnabledAccessLog(AccessLogTarget.Application)) - fs.EnableFileSystemAccessorAccessLog(HostRootFileSystemMountName); - - return Result.Success; - } - - /// - /// Unmounts the file system at @Host:/ - /// - /// The to use. - public static void UnmountHostRoot(this FileSystemClient fs) - { - fs.Unmount(HostRootFileSystemMountName); - } - - /// - /// Mounts a directory on a host Windows computer at the specified mount point. - /// - /// The to use. - /// The mount name at which the file system will be mounted. - /// The path on the host computer to mount. e.g. C:\Windows\System32 - /// The of the operation. - public static Result MountHost(this FileSystemClient fs, U8Span mountName, U8Span path) - { - return MountHostImpl(fs, mountName, path, null); - } - - /// - /// Mounts a directory on a host Windows computer at the specified mount point. - /// - /// The to use. - /// The mount name at which the file system will be mounted. - /// The path on the host computer to mount. e.g. C:\Windows\System32 - /// Options for mounting the host file system. - /// The of the operation. - public static Result MountHost(this FileSystemClient fs, U8Span mountName, U8Span path, MountHostOption option) - { - return MountHostImpl(fs, mountName, path, option); - } - - /// - /// Mounts a directory on a host Windows computer at the specified mount point. - /// - /// The to use. - /// The mount name at which the file system will be mounted. - /// The path on the host computer to mount. e.g. C:\Windows\System32 - /// Options for mounting the host file system. Specifying this parameter is optional. - /// The caller of this function. - /// The of the operation. - private static Result MountHostImpl(this FileSystemClient fs, U8Span mountName, U8Span path, - MountHostOption? optionalOption, [CallerMemberName] string caller = "") - { - Result rc; - ICommonMountNameGenerator nameGenerator; - - string logMessage = null; - var option = MountHostOption.None; - - // Set the mount option if it was specified - if (optionalOption.HasValue) - { - option = optionalOption.Value; - } - - if (fs.IsEnabledAccessLog(AccessLogTarget.Application)) - { - if (optionalOption.HasValue) - { - logMessage = $", name: \"{mountName.ToString()}\", mount_host_option: {option}"; - } - else - { - logMessage = $", name: \"{mountName.ToString()}\""; - } - - System.TimeSpan startTime = fs.Time.GetCurrent(); - rc = PreMountHost(out nameGenerator, mountName, path); - System.TimeSpan endTime = fs.Time.GetCurrent(); - - fs.OutputAccessLogUnlessResultSuccess(rc, startTime, endTime, logMessage, caller); - } - else - { - rc = PreMountHost(out nameGenerator, mountName, path); - } - - if (rc.IsFailure()) return rc; - - IFileSystem hostFileSystem; - - if (fs.IsEnabledAccessLog(AccessLogTarget.Application)) - { - System.TimeSpan startTime = fs.Time.GetCurrent(); - rc = OpenHostFileSystem(fs, out hostFileSystem, mountName, path, option); - System.TimeSpan endTime = fs.Time.GetCurrent(); - - fs.OutputAccessLogUnlessResultSuccess(rc, startTime, endTime, logMessage, caller); - } - else - { - rc = OpenHostFileSystem(fs, out hostFileSystem, mountName, path, option); - } - - if (rc.IsFailure()) return rc; - - if (fs.IsEnabledAccessLog(AccessLogTarget.Application)) - { - System.TimeSpan startTime = fs.Time.GetCurrent(); - rc = fs.Register(mountName, hostFileSystem, nameGenerator); - System.TimeSpan endTime = fs.Time.GetCurrent(); - - fs.OutputAccessLog(rc, startTime, endTime, logMessage, caller); - } - else - { - rc = fs.Register(mountName, hostFileSystem, nameGenerator); - } - - if (rc.IsFailure()) return rc; - - if (fs.IsEnabledAccessLog(AccessLogTarget.Application)) - { - fs.EnableFileSystemAccessorAccessLog(mountName); - } - - return Result.Success; - } - - /// - /// Creates an based on the and - /// , and verifies the . - /// - /// If successful, the created . - /// The mount name at which the file system will be mounted. - /// The path that will be opened on the host computer. e.g. C:\Windows\System32 - /// The of the operation. - private static Result PreMountHost(out ICommonMountNameGenerator nameGenerator, U8Span mountName, U8Span path) - { - nameGenerator = default; - - Result rc = MountHelpers.CheckMountName(mountName); - if (rc.IsFailure()) return rc; - - if (path.IsNull()) - return ResultFs.NullptrArgument.Log(); - - nameGenerator = new HostCommonMountNameGenerator(path); - return Result.Success; - } - /// /// Verifies parameters and opens a host file system. /// @@ -308,7 +141,7 @@ namespace LibHac.Fs.Shim if (PathUtility.IsWindowsDrive(mountName)) return ResultFs.InvalidMountName.Log(); - if (MountHelpers.IsReservedMountName(mountName)) + if (fs.Impl.IsUsedReservedMountName(mountName)) return ResultFs.InvalidMountName.Log(); bool needsTrailingSeparator = false; @@ -345,47 +178,337 @@ namespace LibHac.Fs.Shim } } - FspPath.FromSpan(out FspPath sfPath, fullPath.Str); + Result rc = FspPath.FromSpan(out FspPath sfPath, fullPath.Str); + if (rc.IsFailure()) return rc; return OpenHostFileSystemImpl(fs, out fileSystem, in sfPath, option); } /// - /// Opens a host file system via . + /// Creates a based on the and + /// , and verifies the . /// /// The to use. - /// If successful, the opened host file system. - /// The path on the host computer to open. e.g. /C:\Windows\System32/ - /// Options for opening the host file system. + /// If successful, the created . + /// The mount name at which the file system will be mounted. + /// The path that will be opened on the host computer. e.g. C:\Windows\System32 /// The of the operation. - private static Result OpenHostFileSystemImpl(FileSystemClient fs, out IFileSystem fileSystem, in FspPath path, - MountHostOption option) + private static Result PreMountHost(FileSystemClient fs, out HostCommonMountNameGenerator nameGenerator, + U8Span mountName, U8Span path) { - fileSystem = default; + nameGenerator = default; - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - ReferenceCountedDisposable hostFs = null; + Result rc = fs.Impl.CheckMountName(mountName); + if (rc.IsFailure()) return rc; - try + if (path.IsNull()) + return ResultFs.NullptrArgument.Log(); + + nameGenerator = new HostCommonMountNameGenerator(path); + return Result.Success; + } + + /// + /// Mounts a directory on a host Windows computer at the specified mount point. + /// + /// The to use. + /// The mount name at which the file system will be mounted. + /// The path on the host computer to mount. e.g. C:\Windows\System32 + /// The of the operation. + public static Result MountHost(this FileSystemClient fs, U8Span mountName, U8Span path) + { + Result rc; + Span logBuffer = stackalloc byte[0x300]; + + HostCommonMountNameGenerator mountNameGenerator; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) { - if (option == MountHostOption.None) - { - Result rc = fsProxy.Target.OpenHostFileSystem(out hostFs, in path); - if (rc.IsFailure()) return rc; - } - else - { - Result rc = fsProxy.Target.OpenHostFileSystemWithOption(out hostFs, in path, option); - if (rc.IsFailure()) return rc; - } + Tick start = fs.Hos.Os.GetSystemTick(); + rc = PreMountHost(fs, out mountNameGenerator, mountName, path); + Tick end = fs.Hos.Os.GetSystemTick(); - fileSystem = new FileSystemServiceObjectAdapter(hostFs); - return Result.Success; + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogName).Append(mountName).Append(LogQuote) + .Append(LogRootPath).Append(path).Append(LogQuote); + + logBuffer = sb.Buffer; + + fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer)); } - finally + else { - hostFs?.Dispose(); + rc = PreMountHost(fs, out mountNameGenerator, mountName, path); } + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; + + IFileSystem fileSystem; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = OpenHostFileSystem(fs, out fileSystem, mountName, path, MountHostOption.None); + Tick end = fs.Hos.Os.GetSystemTick(); + + fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer)); + } + else + { + rc = OpenHostFileSystem(fs, out fileSystem, mountName, path, MountHostOption.None); + } + // No AbortIfNeeded here + if (rc.IsFailure()) return rc; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Register(mountName, fileSystem, mountNameGenerator); + Tick end = fs.Hos.Os.GetSystemTick(); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer)); + } + else + { + rc = fs.Register(mountName, fileSystem, mountNameGenerator); + } + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) + fs.Impl.EnableFileSystemAccessorAccessLog(mountName); + + return Result.Success; + } + + /// + /// Mounts a directory on a host Windows computer at the specified mount point. + /// + /// The to use. + /// The mount name at which the file system will be mounted. + /// The path on the host computer to mount. e.g. C:\Windows\System32 + /// Options for mounting the host file system. + /// The of the operation. + public static Result MountHost(this FileSystemClient fs, U8Span mountName, U8Span path, MountHostOption option) + { + Result rc; + Span logBuffer = stackalloc byte[0x300]; + + HostCommonMountNameGenerator mountNameGenerator; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = PreMountHost(fs, out mountNameGenerator, mountName, path); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogName).Append(mountName).Append(LogQuote) + .Append(LogRootPath).Append(path).Append(LogQuote) + .Append(LogMountHostOption).Append(idString.ToString(option)); + + logBuffer = sb.Buffer; + + fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer)); + } + else + { + rc = PreMountHost(fs, out mountNameGenerator, mountName, path); + } + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; + + IFileSystem fileSystem; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = OpenHostFileSystem(fs, out fileSystem, mountName, path, option); + Tick end = fs.Hos.Os.GetSystemTick(); + + fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer)); + } + else + { + rc = OpenHostFileSystem(fs, out fileSystem, mountName, path, MountHostOption.None); + } + // No AbortIfNeeded here + if (rc.IsFailure()) return rc; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Register(mountName, fileSystem, mountNameGenerator); + Tick end = fs.Hos.Os.GetSystemTick(); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer)); + } + else + { + rc = fs.Register(mountName, fileSystem, mountNameGenerator); + } + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) + fs.Impl.EnableFileSystemAccessorAccessLog(mountName); + + return Result.Success; + } + + /// + /// Mounts the C:\ drive of a host Windows computer at @Host:/ + /// + /// The to use. + /// The of the operation. + public static Result MountHostRoot(this FileSystemClient fs) + { + Result rc; + Span logBuffer = stackalloc byte[0x30]; + + IFileSystem fileSystem; + FspPath.CreateEmpty(out FspPath sfPath); + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = OpenHostFileSystemImpl(fs, out fileSystem, in sfPath, MountHostOption.None); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogName).Append(HostRootFileSystemMountName).Append(LogQuote); + logBuffer = sb.Buffer; + + fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer)); + } + else + { + rc = OpenHostFileSystemImpl(fs, out fileSystem, in sfPath, MountHostOption.None); + } + // No AbortIfNeeded here + if (rc.IsFailure()) return rc; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = MountHostFs(fs, fileSystem); + Tick end = fs.Hos.Os.GetSystemTick(); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer)); + } + else + { + rc = MountHostFs(fs, fileSystem); + } + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) + fs.Impl.EnableFileSystemAccessorAccessLog(new U8Span(HostRootFileSystemMountName)); + + return Result.Success; + + static Result MountHostFs(FileSystemClient fs, IFileSystem fileSystem) + { + return fs.Register(new U8Span(HostRootFileSystemMountName), fileSystem, + new HostRootCommonMountNameGenerator()); + } + } + + /// + /// Mounts the C:\ drive of a host Windows computer at @Host:/ + /// + /// The to use. + /// Options for mounting the host file system. + /// The of the operation. + public static Result MountHostRoot(this FileSystemClient fs, MountHostOption option) + { + Result rc; + Span logBuffer = stackalloc byte[0x60]; + + IFileSystem fileSystem; + FspPath.CreateEmpty(out FspPath sfPath); + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = OpenHostFileSystemImpl(fs, out fileSystem, in sfPath, option); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogName).Append(HostRootFileSystemMountName).Append(LogQuote) + .Append(LogMountHostOption).Append(idString.ToString(option)); + + logBuffer = sb.Buffer; + + fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer)); + } + else + { + rc = OpenHostFileSystemImpl(fs, out fileSystem, in sfPath, option); + } + // No AbortIfNeeded here + if (rc.IsFailure()) return rc; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = MountHostFs(fs, fileSystem); + Tick end = fs.Hos.Os.GetSystemTick(); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer)); + } + else + { + rc = MountHostFs(fs, fileSystem); + } + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) + fs.Impl.EnableFileSystemAccessorAccessLog(new U8Span(HostRootFileSystemMountName)); + + return Result.Success; + + static Result MountHostFs(FileSystemClient fs, IFileSystem fileSystem) + { + return fs.Register(new U8Span(HostRootFileSystemMountName), fileSystem, + new HostRootCommonMountNameGenerator()); + } + } + + /// + /// Unmounts the file system at @Host:/ + /// + /// The to use. + public static void UnmountHostRoot(this FileSystemClient fs) + { + Result rc; + Span logBuffer = stackalloc byte[0x30]; + + var mountName = new U8Span(HostRootFileSystemMountName); + + if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledFileSystemAccessorAccessLog(mountName)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.Unmount(mountName); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogName).Append(mountName).Append((byte)'"'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.Unmount(mountName); + } + fs.Impl.LogErrorMessage(rc); + Abort.DoAbortUnless(rc.IsSuccess()); } } } diff --git a/src/LibHac/Fs/Shim/LoaderApi.cs b/src/LibHac/Fs/Shim/LoaderApi.cs index 83303b8d..1f215dad 100644 --- a/src/LibHac/Fs/Shim/LoaderApi.cs +++ b/src/LibHac/Fs/Shim/LoaderApi.cs @@ -10,7 +10,9 @@ namespace LibHac.Fs.Shim using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyForLoaderServiceObject(); - return fsProxy.Target.IsArchivedProgram(out isArchived, processId.Value); + Result rc = fsProxy.Target.IsArchivedProgram(out isArchived, processId.Value); + fs.Impl.AbortIfNeeded(rc); + return rc; } } } diff --git a/src/LibHac/Fs/Shim/ProgramIndexMapInfo.cs b/src/LibHac/Fs/Shim/ProgramIndexMapInfo.cs index a174b8b2..60ce51a2 100644 --- a/src/LibHac/Fs/Shim/ProgramIndexMapInfo.cs +++ b/src/LibHac/Fs/Shim/ProgramIndexMapInfo.cs @@ -24,7 +24,9 @@ namespace LibHac.Fs.Shim var mapInfoBuffer = new InBuffer(MemoryMarshal.Cast(mapInfo)); - return fsProxy.Target.RegisterProgramIndexMapInfo(mapInfoBuffer, mapInfo.Length); + Result rc = fsProxy.Target.RegisterProgramIndexMapInfo(mapInfoBuffer, mapInfo.Length); + fs.Impl.AbortIfNeeded(rc); + return rc; } } } diff --git a/src/LibHac/Fs/Shim/ProgramRegistry.cs b/src/LibHac/Fs/Shim/ProgramRegistry.cs index 3b73c0c9..8e0aa94f 100644 --- a/src/LibHac/Fs/Shim/ProgramRegistry.cs +++ b/src/LibHac/Fs/Shim/ProgramRegistry.cs @@ -14,11 +14,15 @@ namespace LibHac.Fs.Shim { using ReferenceCountedDisposable registry = fs.Impl.GetProgramRegistryServiceObject(); - Result rc = registry.Target.SetCurrentProcess(fs.Hos.ProcessId.Value); + Result rc = registry.Target.SetCurrentProcess(fs.Hos.Os.GetCurrentProcessId().Value); + fs.Impl.AbortIfNeeded(rc); if (rc.IsFailure()) return rc; - return registry.Target.RegisterProgram(processId, programId, storageId, new InBuffer(accessControlData), + rc = registry.Target.RegisterProgram(processId, programId, storageId, new InBuffer(accessControlData), new InBuffer(accessControlDescriptor)); + + fs.Impl.AbortIfNeeded(rc); + return rc; } /// @@ -26,7 +30,7 @@ namespace LibHac.Fs.Shim { using ReferenceCountedDisposable registry = fs.Impl.GetProgramRegistryServiceObject(); - Result rc = registry.Target.SetCurrentProcess(fs.Hos.ProcessId.Value); + Result rc = registry.Target.SetCurrentProcess(fs.Hos.Os.GetCurrentProcessId().Value); if (rc.IsFailure()) return rc; return registry.Target.UnregisterProgram(processId); diff --git a/src/LibHac/Fs/Shim/SaveData.cs b/src/LibHac/Fs/Shim/SaveData.cs index 6dda8048..df474242 100644 --- a/src/LibHac/Fs/Shim/SaveData.cs +++ b/src/LibHac/Fs/Shim/SaveData.cs @@ -1,60 +1,120 @@ -using LibHac.Common; +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 LibHac.Os; +using static LibHac.Fs.Impl.AccessLogStrings; using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem; namespace LibHac.Fs.Shim { + [SkipLocalsInit] public static class SaveData { - public static Result MountSaveData(this FileSystemClient fs, U8Span mountName, Ncm.ApplicationId applicationId, UserId userId) + private static Result MountSaveDataImpl(this FileSystemClientImpl fs, U8Span mountName, SaveDataSpaceId spaceId, + ProgramId programId, UserId userId, SaveDataType type, bool openReadOnly, ushort index) + { + Result rc = fs.CheckMountName(mountName); + if (rc.IsFailure()) return rc; + + using ReferenceCountedDisposable fsProxy = fs.GetFileSystemProxyServiceObject(); + + rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, programId, type, userId, 0, index); + if (rc.IsFailure()) return rc; + + ReferenceCountedDisposable fileSystem = null; + try + { + if (openReadOnly) + { + rc = fsProxy.Target.OpenReadOnlySaveDataFileSystem(out fileSystem, spaceId, in attribute); + if (rc.IsFailure()) return rc; + } + else + { + rc = fsProxy.Target.OpenSaveDataFileSystem(out fileSystem, spaceId, in attribute); + if (rc.IsFailure()) return rc; + } + + var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem); + + return fs.Fs.Register(mountName, fileSystemAdapter, fileSystemAdapter, null, false, true); + } + finally + { + fileSystem?.Dispose(); + } + } + + public static Result MountSaveData(this FileSystemClient fs, U8Span mountName, Ncm.ApplicationId applicationId, + UserId userId) { Result rc; + Span logBuffer = stackalloc byte[0x90]; - if (fs.IsEnabledAccessLog(AccessLogTarget.Application)) + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) { - System.TimeSpan startTime = fs.Time.GetCurrent(); - rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, applicationId, userId, SaveDataType.Account, false, 0); - System.TimeSpan endTime = fs.Time.GetCurrent(); + Tick start = fs.Hos.Os.GetSystemTick(); + rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, applicationId, userId, + SaveDataType.Account, openReadOnly: false, index: 0); + Tick end = fs.Hos.Os.GetSystemTick(); - fs.OutputAccessLog(rc, startTime, endTime, $", name: \"{mountName.ToString()}\", applicationid: 0x{applicationId}, userid: 0x{userId}"); + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogName).Append(mountName).Append(LogQuote) + .Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') + .Append(LogUserId).AppendFormat(userId.Id.High, 'X', 16).AppendFormat(userId.Id.Low, 'X', 16); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); } else { - rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, applicationId, userId, SaveDataType.Account, false, 0); + rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, applicationId, userId, + SaveDataType.Account, openReadOnly: false, index: 0); } - if (rc.IsSuccess() && fs.IsEnabledAccessLog(AccessLogTarget.Application)) - { - fs.EnableFileSystemAccessorAccessLog(mountName); - } + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) + fs.Impl.EnableFileSystemAccessorAccessLog(mountName); return rc; } - public static Result MountSaveDataReadOnly(this FileSystemClient fs, U8Span mountName, Ncm.ApplicationId applicationId, UserId userId) + public static Result MountSaveDataReadOnly(this FileSystemClient fs, U8Span mountName, + Ncm.ApplicationId applicationId, UserId userId) { Result rc; + Span logBuffer = stackalloc byte[0x90]; - if (fs.IsEnabledAccessLog(AccessLogTarget.Application)) + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) { - System.TimeSpan startTime = fs.Time.GetCurrent(); - rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, applicationId, userId, SaveDataType.Account, true, 0); - System.TimeSpan endTime = fs.Time.GetCurrent(); + Tick start = fs.Hos.Os.GetSystemTick(); + rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, applicationId, userId, + SaveDataType.Account, openReadOnly: true, index: 0); + Tick end = fs.Hos.Os.GetSystemTick(); - fs.OutputAccessLog(rc, startTime, endTime, $", name: \"{mountName.ToString()}\", applicationid: 0x{applicationId}, userid: 0x{userId}"); + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogName).Append(mountName).Append(LogQuote) + .Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') + .Append(LogUserId).AppendFormat(userId.Id.High, 'X', 16).AppendFormat(userId.Id.Low, 'X', 16); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); } else { - rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, applicationId, userId, SaveDataType.Account, false, 0); + rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, applicationId, userId, + SaveDataType.Account, openReadOnly: true, index: 0); } - if (rc.IsSuccess() && fs.IsEnabledAccessLog(AccessLogTarget.Application)) - { - fs.EnableFileSystemAccessorAccessLog(mountName); - } + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) + fs.Impl.EnableFileSystemAccessorAccessLog(mountName); return rc; } @@ -62,24 +122,31 @@ namespace LibHac.Fs.Shim public static Result MountTemporaryStorage(this FileSystemClient fs, U8Span mountName) { Result rc; + Span logBuffer = stackalloc byte[0x30]; - if (fs.IsEnabledAccessLog(AccessLogTarget.Application)) + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) { - System.TimeSpan startTime = fs.Time.GetCurrent(); - rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.Temporary, default, default, SaveDataType.Temporary, false, 0); - System.TimeSpan endTime = fs.Time.GetCurrent(); + Tick start = fs.Hos.Os.GetSystemTick(); + rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.Temporary, Fs.SaveData.InvalidProgramId, + Fs.SaveData.InvalidUserId, SaveDataType.Temporary, openReadOnly: false, index: 0); + Tick end = fs.Hos.Os.GetSystemTick(); - fs.OutputAccessLog(rc, startTime, endTime, $", name: \"{mountName.ToString()}\""); + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogName).Append(mountName).Append(LogQuote); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); } else { - rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.Temporary, default, default, SaveDataType.Temporary, false, 0); + rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.Temporary, Fs.SaveData.InvalidProgramId, + Fs.SaveData.InvalidUserId, SaveDataType.Temporary, openReadOnly: false, index: 0); } - if (rc.IsSuccess() && fs.IsEnabledAccessLog(AccessLogTarget.Application)) - { - fs.EnableFileSystemAccessorAccessLog(mountName); - } + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) + fs.Impl.EnableFileSystemAccessorAccessLog(mountName); return rc; } @@ -87,24 +154,31 @@ namespace LibHac.Fs.Shim public static Result MountCacheStorage(this FileSystemClient fs, U8Span mountName) { Result rc; + Span logBuffer = stackalloc byte[0x30]; - if (fs.IsEnabledAccessLog(AccessLogTarget.Application)) + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) { - System.TimeSpan startTime = fs.Time.GetCurrent(); - rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, default, default, SaveDataType.Cache, false, 0); - System.TimeSpan endTime = fs.Time.GetCurrent(); + Tick start = fs.Hos.Os.GetSystemTick(); + rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, Fs.SaveData.InvalidProgramId, + Fs.SaveData.InvalidUserId, SaveDataType.Cache, openReadOnly: false, index: 0); + Tick end = fs.Hos.Os.GetSystemTick(); - fs.OutputAccessLog(rc, startTime, endTime, $", name: \"{mountName.ToString()}\""); + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogName).Append(mountName).Append(LogQuote); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); } else { - rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, default, default, SaveDataType.Cache, false, 0); + rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, Fs.SaveData.InvalidProgramId, + Fs.SaveData.InvalidUserId, SaveDataType.Cache, openReadOnly: false, index: 0); } - if (rc.IsSuccess() && fs.IsEnabledAccessLog(AccessLogTarget.Application)) - { - fs.EnableFileSystemAccessorAccessLog(mountName); - } + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) + fs.Impl.EnableFileSystemAccessorAccessLog(mountName); return rc; } @@ -112,111 +186,103 @@ namespace LibHac.Fs.Shim public static Result MountCacheStorage(this FileSystemClient fs, U8Span mountName, int index) { Result rc; + Span logBuffer = stackalloc byte[0x40]; - if (fs.IsEnabledAccessLog(AccessLogTarget.Application)) + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) { - System.TimeSpan startTime = fs.Time.GetCurrent(); - rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, default, default, SaveDataType.Cache, false, (ushort)index); - System.TimeSpan endTime = fs.Time.GetCurrent(); + Tick start = fs.Hos.Os.GetSystemTick(); + rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, Fs.SaveData.InvalidProgramId, + Fs.SaveData.InvalidUserId, SaveDataType.Cache, openReadOnly: false, (ushort)index); + Tick end = fs.Hos.Os.GetSystemTick(); - fs.OutputAccessLog(rc, startTime, endTime, $", name: \"{mountName.ToString()}\", index: {index}"); + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogName).Append(mountName).Append(LogQuote) + .Append(LogIndex).AppendFormat(index); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); } else { - rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, default, default, SaveDataType.Cache, false, (ushort)index); + rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, Fs.SaveData.InvalidProgramId, + Fs.SaveData.InvalidUserId, SaveDataType.Cache, openReadOnly: false, (ushort)index); } - if (rc.IsSuccess() && fs.IsEnabledAccessLog(AccessLogTarget.Application)) - { - fs.EnableFileSystemAccessorAccessLog(mountName); - } - - return rc; - } - - public static Result MountCacheStorage(this FileSystemClient fs, U8Span mountName, Ncm.ApplicationId applicationId) - { - Result rc; - - if (fs.IsEnabledAccessLog(AccessLogTarget.System)) - { - System.TimeSpan startTime = fs.Time.GetCurrent(); - rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, applicationId, default, SaveDataType.Cache, false, 0); - System.TimeSpan endTime = fs.Time.GetCurrent(); - - fs.OutputAccessLog(rc, startTime, endTime, $", name: \"{mountName.ToString()}\", applicationid: 0x{applicationId}"); - } - else - { - rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, applicationId, default, SaveDataType.Cache, false, 0); - } - - if (rc.IsSuccess() && fs.IsEnabledAccessLog(AccessLogTarget.System)) - { - fs.EnableFileSystemAccessorAccessLog(mountName); - } - - return rc; - } - - public static Result MountCacheStorage(this FileSystemClient fs, U8Span mountName, Ncm.ApplicationId applicationId, int index) - { - Result rc; - - if (fs.IsEnabledAccessLog(AccessLogTarget.System)) - { - System.TimeSpan startTime = fs.Time.GetCurrent(); - rc = MountSaveDataImpl(fs, mountName, SaveDataSpaceId.User, applicationId, default, SaveDataType.Cache, false, (ushort)index); - System.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, (ushort)index); - } - - if (rc.IsSuccess() && fs.IsEnabledAccessLog(AccessLogTarget.System)) - { - fs.EnableFileSystemAccessorAccessLog(mountName); - } - - return rc; - } - - private static Result MountSaveDataImpl(this FileSystemClient fs, U8Span mountName, SaveDataSpaceId spaceId, - ProgramId programId, UserId userId, SaveDataType type, bool openReadOnly, ushort index) - { - Result rc = MountHelpers.CheckMountName(mountName); + fs.Impl.AbortIfNeeded(rc); if (rc.IsFailure()) return rc; - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.Application)) + fs.Impl.EnableFileSystemAccessorAccessLog(mountName); - var attribute = new SaveDataAttribute(programId, type, userId, 0, index); + return rc; + } - ReferenceCountedDisposable saveFs = null; + public static Result MountCacheStorage(this FileSystemClient fs, U8Span mountName, + Ncm.ApplicationId applicationId) + { + Result rc; + Span logBuffer = stackalloc byte[0x50]; - try + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) { - if (openReadOnly) - { - rc = fsProxy.Target.OpenReadOnlySaveDataFileSystem(out saveFs, spaceId, in attribute); - } - else - { - rc = fsProxy.Target.OpenSaveDataFileSystem(out saveFs, spaceId, in attribute); - } + Tick start = fs.Hos.Os.GetSystemTick(); + rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, applicationId, + Fs.SaveData.InvalidUserId, SaveDataType.Cache, openReadOnly: false, index: 0); + Tick end = fs.Hos.Os.GetSystemTick(); - if (rc.IsFailure()) return rc; + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogName).Append(mountName).Append(LogQuote) + .Append(LogApplicationId).AppendFormat(applicationId.Value, 'X'); - var fileSystemAdapter = new FileSystemServiceObjectAdapter(saveFs); - - return fs.Register(mountName, fileSystemAdapter, fileSystemAdapter, null, false, false); + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); } - finally + else { - saveFs?.Dispose(); + rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, applicationId, + Fs.SaveData.InvalidUserId, SaveDataType.Cache, openReadOnly: false, index: 0); } + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) + fs.Impl.EnableFileSystemAccessorAccessLog(mountName); + + return rc; + } + + public static Result MountCacheStorage(this FileSystemClient fs, U8Span mountName, + Ncm.ApplicationId applicationId, int index) + { + Result rc; + Span logBuffer = stackalloc byte[0x60]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, applicationId, + Fs.SaveData.InvalidUserId, SaveDataType.Cache, openReadOnly: false, (ushort)index); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogName).Append(mountName).Append(LogQuote) + .Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') + .Append(LogIndex).AppendFormat(index); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = MountSaveDataImpl(fs.Impl, mountName, SaveDataSpaceId.User, applicationId, + Fs.SaveData.InvalidUserId, SaveDataType.Cache, openReadOnly: false, (ushort)index); + } + + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) + fs.Impl.EnableFileSystemAccessorAccessLog(mountName); + + return rc; } } } diff --git a/src/LibHac/Fs/Shim/SaveDataManagement.cs b/src/LibHac/Fs/Shim/SaveDataManagement.cs index 78406c90..d7b27149 100644 --- a/src/LibHac/Fs/Shim/SaveDataManagement.cs +++ b/src/LibHac/Fs/Shim/SaveDataManagement.cs @@ -1,15 +1,71 @@ using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using LibHac.Common; using LibHac.Diag; using LibHac.Fs.Impl; +using LibHac.FsSrv.Impl; using LibHac.FsSrv.Sf; using LibHac.Ncm; +using LibHac.Os; using LibHac.Sf; +using static LibHac.Fs.Impl.AccessLogStrings; namespace LibHac.Fs.Shim { + public readonly struct SaveDataIterator : IDisposable + { + private FileSystemClient FsClient { get; } + private ReferenceCountedDisposable Reader { get; } + + internal SaveDataIterator(FileSystemClient fsClient, ref ReferenceCountedDisposable reader) + { + FsClient = fsClient; + Reader = Shared.Move(ref reader); + } + + private Result ReadSaveDataInfoImpl(out long readCount, Span buffer) + { + var outBuffer = new OutBuffer(MemoryMarshal.Cast(buffer)); + return Reader.Target.Read(out readCount, outBuffer); + } + + public Result ReadSaveDataInfo(out long readCount, Span buffer) + { + Result rc; + FileSystemClient fs = FsClient; + Span logBuffer = stackalloc byte[0x50]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = ReadSaveDataInfoImpl(out readCount, buffer); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogSize).AppendFormat(buffer.Length); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = ReadSaveDataInfoImpl(out readCount, buffer); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + } + + public void Dispose() + { + Reader?.Dispose(); + } + } + + [SkipLocalsInit] public static class SaveDataManagement { + internal static Result ReadSaveDataFileSystemExtraData(this FileSystemClientImpl fs, out SaveDataExtraData extraData, ulong saveDataId) { @@ -56,297 +112,521 @@ namespace LibHac.Fs.Shim internal static Result FindSaveDataWithFilter(this FileSystemClientImpl fs, out SaveDataInfo saveInfo, SaveDataSpaceId spaceId, in SaveDataFilter filter) { - throw new NotImplementedException(); + saveInfo = default; + + using ReferenceCountedDisposable fsProxy = fs.GetFileSystemProxyServiceObject(); + + Unsafe.SkipInit(out SaveDataInfo tempInfo); + OutBuffer saveInfoBuffer = OutBuffer.FromStruct(ref tempInfo); + + Result rc = fsProxy.Target.FindSaveDataWithFilter(out long count, saveInfoBuffer, spaceId, in filter); + if (rc.IsFailure()) return rc; + + if (count == 0) + return ResultFs.TargetNotFound.Log(); + + saveInfo = tempInfo; + return Result.Success; } - public static Result CreateSaveData(this FileSystemClient fs, Ncm.ApplicationId applicationId, UserId userId, + public static Result CreateSaveData(this FileSystemClientImpl fs, Ncm.ApplicationId applicationId, UserId userId, ulong ownerId, long size, long journalSize, SaveDataFlags flags) { - return fs.RunOperationWithAccessLog(AccessLogTarget.System, - () => - { - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + using ReferenceCountedDisposable fsProxy = fs.GetFileSystemProxyServiceObject(); - var attribute = new SaveDataAttribute(applicationId, SaveDataType.Account, userId, 0); + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Account, + userId, 0); + if (rc.IsFailure()) return rc; - var createInfo = new SaveDataCreationInfo - { - Size = size, - JournalSize = journalSize, - BlockSize = 0x4000, - OwnerId = ownerId, - Flags = flags, - SpaceId = SaveDataSpaceId.User - }; + rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags, + SaveDataSpaceId.User); + if (rc.IsFailure()) return rc; - var metaInfo = new SaveDataMetaInfo - { - Type = SaveDataMetaType.Thumbnail, - Size = 0x40060 - }; + var metaPolicy = new SaveDataMetaPolicy(SaveDataType.Account); + metaPolicy.GenerateMetaInfo(out SaveDataMetaInfo metaInfo); - return fsProxy.Target.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}"); + return fsProxy.Target.CreateSaveDataFileSystem(in attribute, in creationInfo, in metaInfo); } - public static Result CreateBcatSaveData(this FileSystemClient fs, Ncm.ApplicationId applicationId, long size) + public static Result CreateBcatSaveData(this FileSystemClientImpl fs, Ncm.ApplicationId applicationId, long size) { - return fs.RunOperationWithAccessLog(AccessLogTarget.System, - () => - { - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + using ReferenceCountedDisposable fsProxy = fs.GetFileSystemProxyServiceObject(); - var attribute = new SaveDataAttribute(applicationId, SaveDataType.Bcat, UserId.InvalidId, 0); + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Bcat, + Fs.SaveData.InvalidUserId, 0); + if (rc.IsFailure()) return rc; - var createInfo = new SaveDataCreationInfo - { - Size = size, - JournalSize = 0x200000, - BlockSize = 0x4000, - OwnerId = SystemProgramId.Bcat.Value, - Flags = 0, - SpaceId = SaveDataSpaceId.User - }; + rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, + SaveDataProperties.BcatSaveDataJournalSize, SystemProgramId.Bcat.Value, SaveDataFlags.None, + SaveDataSpaceId.User); + if (rc.IsFailure()) return rc; - var metaInfo = new SaveDataMetaInfo(); + var metaInfo = new SaveDataMetaInfo + { + Type = SaveDataMetaType.None, + Size = 0 + }; - return fsProxy.Target.CreateSaveDataFileSystem(in attribute, in createInfo, in metaInfo); - }, - () => $", applicationid: 0x{applicationId.Value:X}, save_data_size: {size}"); + return fsProxy.Target.CreateSaveDataFileSystem(in attribute, in creationInfo, in metaInfo); } - public static Result CreateDeviceSaveData(this FileSystemClient fs, Ncm.ApplicationId applicationId, ulong ownerId, - long size, long journalSize, SaveDataFlags flags) + public static Result CreateDeviceSaveData(this FileSystemClientImpl fs, Ncm.ApplicationId applicationId, + ulong ownerId, long size, long journalSize, SaveDataFlags flags) { - return fs.RunOperationWithAccessLog(AccessLogTarget.System, - () => - { - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + using ReferenceCountedDisposable fsProxy = fs.GetFileSystemProxyServiceObject(); - var attribute = new SaveDataAttribute(applicationId, SaveDataType.Device, UserId.InvalidId, 0); + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Device, + Fs.SaveData.InvalidUserId, 0); + if (rc.IsFailure()) return rc; - var createInfo = new SaveDataCreationInfo - { - Size = size, - JournalSize = journalSize, - BlockSize = 0x4000, - OwnerId = ownerId, - Flags = flags, - SpaceId = SaveDataSpaceId.User - }; + rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags, + SaveDataSpaceId.User); + if (rc.IsFailure()) return rc; - var metaInfo = new SaveDataMetaInfo(); + var metaPolicy = new SaveDataMetaPolicy(SaveDataType.Device); + metaPolicy.GenerateMetaInfo(out SaveDataMetaInfo metaInfo); - return fsProxy.Target.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}"); + return fsProxy.Target.CreateSaveDataFileSystem(in attribute, in creationInfo, in metaInfo); + } + + public static Result CreateCacheStorage(this FileSystemClientImpl fs, Ncm.ApplicationId applicationId, + SaveDataSpaceId spaceId, ulong ownerId, ushort index, long size, long journalSize, SaveDataFlags flags) + { + using ReferenceCountedDisposable fsProxy = fs.GetFileSystemProxyServiceObject(); + + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Cache, + Fs.SaveData.InvalidUserId, 0, index); + if (rc.IsFailure()) return rc; + + rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags, + spaceId); + if (rc.IsFailure()) return rc; + + var metaInfo = new SaveDataMetaInfo + { + Type = SaveDataMetaType.None, + Size = 0 + }; + + return fsProxy.Target.CreateSaveDataFileSystem(in attribute, in creationInfo, in metaInfo); + } + + public static Result CreateSaveData(this FileSystemClientImpl fs, in SaveDataAttribute attribute, + in SaveDataCreationInfo creationInfo, in SaveDataMetaInfo metaInfo, in HashSalt hashSalt) + { + using ReferenceCountedDisposable fsProxy = fs.GetFileSystemProxyServiceObject(); + return fsProxy.Target.CreateSaveDataFileSystemWithHashSalt(in attribute, in creationInfo, + in metaInfo, in hashSalt); + } + + public static Result DeleteSaveData(this FileSystemClientImpl fs, ulong saveDataId) + { + using ReferenceCountedDisposable fsProxy = fs.GetFileSystemProxyServiceObject(); + return fsProxy.Target.DeleteSaveDataFileSystem(saveDataId); + } + + public static Result DeleteSaveData(this FileSystemClientImpl fs, SaveDataSpaceId spaceId, ulong saveDataId) + { + using ReferenceCountedDisposable fsProxy = fs.GetFileSystemProxyServiceObject(); + return fsProxy.Target.DeleteSaveDataFileSystemBySaveDataSpaceId(spaceId, saveDataId); } public static Result DeleteSaveData(this FileSystemClient fs, ulong saveDataId) { - return fs.RunOperationWithAccessLog(AccessLogTarget.System, - () => - { - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - return fsProxy.Target.DeleteSaveDataFileSystem(saveDataId); - }, - () => $", savedataid: 0x{saveDataId:X}"); + Result rc; + Span logBuffer = stackalloc byte[0x30]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.DeleteSaveData(saveDataId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.DeleteSaveData(saveDataId); + } + fs.Impl.AbortIfNeeded(rc); + return rc; } public static Result DeleteSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId) { - return fs.RunOperationWithAccessLog(AccessLogTarget.System, - () => - { - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - return fsProxy.Target.DeleteSaveDataFileSystemBySaveDataSpaceId(spaceId, saveDataId); - }, - () => $", savedataspaceid: {spaceId}, savedataid: 0x{saveDataId:X}"); + Result rc; + Span logBuffer = stackalloc byte[0x50]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.DeleteSaveData(spaceId, saveDataId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataId).AppendFormat(saveDataId, 'X'); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.DeleteSaveData(spaceId, saveDataId); + } + fs.Impl.AbortIfNeeded(rc); + return rc; } - public static Result OpenSaveDataIterator(this FileSystemClient fs, out SaveDataIterator iterator, SaveDataSpaceId spaceId) + public static Result OpenSaveDataIterator(this FileSystemClientImpl fs, out SaveDataIterator iterator, + SaveDataSpaceId spaceId) { - var tempIterator = new SaveDataIterator(); + iterator = default; - try - { - Result result = fs.RunOperationWithAccessLog(AccessLogTarget.System, - () => - { - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + using ReferenceCountedDisposable fsProxy = fs.GetFileSystemProxyServiceObject(); - Result rc = fsProxy.Target.OpenSaveDataInfoReaderBySaveDataSpaceId( - out ReferenceCountedDisposable reader, spaceId); - if (rc.IsFailure()) return rc; - - tempIterator = new SaveDataIterator(fs, reader); - - return Result.Success; - }, - () => $", savedataspaceid: {spaceId}"); - - iterator = result.IsSuccess() ? tempIterator : default; - tempIterator = default; - - return result; - } - finally - { - tempIterator.Dispose(); - } - } - - public static Result OpenSaveDataIterator(this FileSystemClient fs, out SaveDataIterator iterator, SaveDataSpaceId spaceId, in SaveDataFilter filter) - { ReferenceCountedDisposable reader = null; - var tempIterator = new SaveDataIterator(); - SaveDataFilter tempFilter = filter; - try { - Result result = fs.RunOperationWithAccessLog(AccessLogTarget.System, - () => - { - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + Result rc = fsProxy.Target.OpenSaveDataInfoReaderBySaveDataSpaceId(out reader, spaceId); + if (rc.IsFailure()) return rc; - Result rc = fsProxy.Target.OpenSaveDataInfoReaderWithFilter(out reader, spaceId, in tempFilter); - if (rc.IsFailure()) return rc; - - tempIterator = new SaveDataIterator(fs, reader); - - return Result.Success; - }, - () => $", savedataspaceid: {spaceId}"); - - iterator = result.IsSuccess() ? tempIterator : default; - - return result; + iterator = new SaveDataIterator(fs.Fs, ref reader); } finally { reader?.Dispose(); } + + return Result.Success; } - public static Result FindSaveDataWithFilter(this FileSystemClient fs, out SaveDataInfo info, SaveDataSpaceId spaceId, - in SaveDataFilter filter) + public static Result OpenSaveDataIterator(this FileSystemClientImpl fs, out SaveDataIterator iterator, + SaveDataSpaceId spaceId, in SaveDataFilter filter) { - info = default; + iterator = default; - SaveDataFilter tempFilter = filter; - var tempInfo = new SaveDataInfo(); + using ReferenceCountedDisposable fsProxy = fs.GetFileSystemProxyServiceObject(); - Result result = fs.RunOperationWithAccessLog(AccessLogTarget.System, - () => - { - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - - tempInfo = new SaveDataInfo(); - - Result rc = fsProxy.Target.FindSaveDataWithFilter(out long count, - OutBuffer.FromStruct(ref tempInfo), spaceId, in tempFilter); - if (rc.IsFailure()) return rc; - - if (count == 0) - return ResultFs.TargetNotFound.Log(); - - return Result.Success; - }, - () => $", savedataspaceid: {spaceId}"); - - if (result.IsSuccess()) + ReferenceCountedDisposable reader = null; + try { - info = tempInfo; + Result rc = fsProxy.Target.OpenSaveDataInfoReaderWithFilter(out reader, spaceId, in filter); + if (rc.IsFailure()) return rc; + + iterator = new SaveDataIterator(fs.Fs, ref reader); + } + finally + { + reader?.Dispose(); } - return result; + return Result.Success; + } + + public static Result OpenSaveDataIterator(this FileSystemClient fs, out SaveDataIterator iterator, + SaveDataSpaceId spaceId) + { + Result rc; + Span logBuffer = stackalloc byte[0x50]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.OpenSaveDataIterator(out iterator, spaceId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.OpenSaveDataIterator(out iterator, spaceId); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + } + + public static Result OpenSaveDataIterator(this FileSystemClient fs, out SaveDataIterator iterator, + SaveDataSpaceId spaceId, in SaveDataFilter filter) + { + Result rc; + Span logBuffer = stackalloc byte[0x50]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.OpenSaveDataIterator(out iterator, spaceId, in filter); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.OpenSaveDataIterator(out iterator, spaceId, in filter); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + } + + public static Result FindSaveDataWithFilter(this FileSystemClient fs, out SaveDataInfo info, + SaveDataSpaceId spaceId, in SaveDataFilter filter) + { + Result rc; + Span logBuffer = stackalloc byte[0x50]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.FindSaveDataWithFilter(out info, spaceId, in filter); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.FindSaveDataWithFilter(out info, spaceId, in filter); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; } public static Result CreateSaveData(this FileSystemClient fs, Ncm.ApplicationId applicationId, UserId userId, - ulong ownerId, long size, long journalSize, HashSalt hashSalt, SaveDataFlags flags) + ulong ownerId, long size, long journalSize, SaveDataFlags flags) { - return fs.RunOperationWithAccessLog(AccessLogTarget.System, - () => - { - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + Result rc; + Span logBuffer = stackalloc byte[0x100]; - var attribute = new SaveDataAttribute(applicationId, SaveDataType.Account, userId, 0); + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.CreateSaveData(applicationId, userId, ownerId, size, journalSize, flags); + Tick end = fs.Hos.Os.GetSystemTick(); - var createInfo = new SaveDataCreationInfo - { - Size = size, - JournalSize = journalSize, - BlockSize = 0x4000, - OwnerId = ownerId, - Flags = flags, - SpaceId = SaveDataSpaceId.User - }; + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') + .Append(LogUserId).AppendFormat(userId.Id.High, 'X', 16).AppendFormat(userId.Id.Low, 'X', 16) + .Append(LogSaveDataOwnerId).AppendFormat(ownerId, 'X') + .Append(LogSaveDataSize).AppendFormat(size) + .Append(LogSaveDataJournalSize).AppendFormat(journalSize) + .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); - var metaInfo = new SaveDataMetaInfo - { - Type = SaveDataMetaType.Thumbnail, - Size = 0x40060 - }; + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.CreateSaveData(applicationId, userId, ownerId, size, journalSize, flags); + } - return fsProxy.Target.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}"); + fs.Impl.AbortIfNeeded(rc); + return rc; } - public static Result CreateTemporaryStorage(this FileSystemClient fs, Ncm.ApplicationId applicationId, ulong ownerId, long size, SaveDataFlags flags) + public static Result CreateSaveData(this FileSystemClient fs, Ncm.ApplicationId applicationId, UserId userId, + ulong ownerId, long size, long journalSize, in HashSalt hashSalt, SaveDataFlags flags) { - return fs.RunOperationWithAccessLog(AccessLogTarget.System, - () => - { - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + Result rc; + Span logBuffer = stackalloc byte[0x100]; - var attribute = new SaveDataAttribute(applicationId, SaveDataType.Temporary, UserId.InvalidId, 0); + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = CreateSave(fs, applicationId, userId, ownerId, size, journalSize, in hashSalt, flags); + Tick end = fs.Hos.Os.GetSystemTick(); - var createInfo = new SaveDataCreationInfo - { - Size = size, - BlockSize = 0x4000, - OwnerId = ownerId, - Flags = flags, - SpaceId = SaveDataSpaceId.Temporary - }; + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') + .Append(LogUserId).AppendFormat(userId.Id.High, 'X', 16).AppendFormat(userId.Id.Low, 'X', 16) + .Append(LogSaveDataOwnerId).AppendFormat(ownerId, 'X') + .Append(LogSaveDataSize).AppendFormat(size) + .Append(LogSaveDataJournalSize).AppendFormat(journalSize) + .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); - var metaInfo = new SaveDataMetaInfo(); + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = CreateSave(fs, applicationId, userId, ownerId, size, journalSize, in hashSalt, flags); + } + fs.Impl.AbortIfNeeded(rc); + return rc; - return fsProxy.Target.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}"); + static Result CreateSave(FileSystemClient fs, Ncm.ApplicationId applicationId, UserId userId, + ulong ownerId, long size, long journalSize, in HashSalt hashSalt, SaveDataFlags flags) + { + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Account, + userId, 0); + if (rc.IsFailure()) return rc; + + rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags, + SaveDataSpaceId.User); + if (rc.IsFailure()) return rc; + + var metaPolicy = new SaveDataMetaPolicy(SaveDataType.Account); + metaPolicy.GenerateMetaInfo(out SaveDataMetaInfo metaInfo); + + return fsProxy.Target.CreateSaveDataFileSystemWithHashSalt(in attribute, in creationInfo, in metaInfo, + in hashSalt); + } + } + + public static Result CreateBcatSaveData(this FileSystemClient fs, Ncm.ApplicationId applicationId, long size) + { + Result rc; + Span logBuffer = stackalloc byte[0x50]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.CreateBcatSaveData(applicationId, size); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') + .Append(LogSaveDataSize).AppendFormat(size); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.CreateBcatSaveData(applicationId, size); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + } + + public static Result CreateDeviceSaveData(this FileSystemClient fs, Ncm.ApplicationId applicationId, + ulong ownerId, long size, long journalSize, SaveDataFlags flags) + { + Result rc; + Span logBuffer = stackalloc byte[0x100]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.CreateDeviceSaveData(applicationId, ownerId, size, journalSize, flags); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') + .Append(LogSaveDataOwnerId).AppendFormat(ownerId, 'X') + .Append(LogSaveDataSize).AppendFormat(size) + .Append(LogSaveDataJournalSize).AppendFormat(journalSize) + .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.CreateDeviceSaveData(applicationId, ownerId, size, journalSize, flags); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + } + + public static Result CreateTemporaryStorage(this FileSystemClientImpl fs, Ncm.ApplicationId applicationId, + ulong ownerId, long size, SaveDataFlags flags) + { + using ReferenceCountedDisposable fsProxy = fs.GetFileSystemProxyServiceObject(); + + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, applicationId, SaveDataType.Temporary, + Fs.SaveData.InvalidUserId, 0); + if (rc.IsFailure()) return rc; + + rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, 0, ownerId, flags, + SaveDataSpaceId.Temporary); + if (rc.IsFailure()) return rc; + + var metaInfo = new SaveDataMetaInfo + { + Type = SaveDataMetaType.None, + Size = 0 + }; + + return fsProxy.Target.CreateSaveDataFileSystem(in attribute, in creationInfo, in metaInfo); + } + + public static Result CreateTemporaryStorage(this FileSystemClient fs, Ncm.ApplicationId applicationId, + ulong ownerId, long size, SaveDataFlags flags) + { + Result rc; + Span logBuffer = stackalloc byte[0x100]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.CreateTemporaryStorage(applicationId, ownerId, size, flags); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') + .Append(LogSaveDataOwnerId).AppendFormat(ownerId, 'X') + .Append(LogSaveDataSize).AppendFormat(size) + .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.CreateTemporaryStorage(applicationId, ownerId, size, flags); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; } public static Result CreateCacheStorage(this FileSystemClient fs, Ncm.ApplicationId applicationId, SaveDataSpaceId spaceId, ulong ownerId, ushort index, long size, long journalSize, SaveDataFlags flags) { - return fs.RunOperationWithAccessLog(AccessLogTarget.System, - () => - { - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + Result rc; + Span logBuffer = stackalloc byte[0x100]; - var attribute = new SaveDataAttribute(applicationId, SaveDataType.Cache, UserId.InvalidId, 0, index); + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.CreateCacheStorage(applicationId, spaceId, ownerId, index, size, journalSize, flags); + Tick end = fs.Hos.Os.GetSystemTick(); - var creationInfo = new SaveDataCreationInfo - { - Size = size, - JournalSize = journalSize, - BlockSize = 0x4000, - OwnerId = ownerId, - Flags = flags, - SpaceId = spaceId - }; + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogApplicationId).AppendFormat(applicationId.Value, 'X') + .Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataOwnerId).AppendFormat(ownerId, 'X') + .Append(LogSaveDataSize).AppendFormat(size) + .Append(LogSaveDataJournalSize).AppendFormat(journalSize) + .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); - var metaInfo = new SaveDataMetaInfo(); + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.CreateCacheStorage(applicationId, spaceId, ownerId, index, size, journalSize, flags); + } - return fsProxy.Target.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}"); + fs.Impl.AbortIfNeeded(rc); + return rc; } public static Result CreateCacheStorage(this FileSystemClient fs, Ncm.ApplicationId applicationId, @@ -358,136 +638,139 @@ namespace LibHac.Fs.Shim public static Result CreateCacheStorage(this FileSystemClient fs, Ncm.ApplicationId applicationId, ulong ownerId, long size, long journalSize, SaveDataFlags flags) { - return CreateCacheStorage(fs, applicationId, SaveDataSpaceId.User, ownerId, 0, size, journalSize, flags); + return CreateCacheStorage(fs, applicationId, SaveDataSpaceId.User, ownerId, size, journalSize, flags); } public static Result CreateSystemSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, UserId userId, ulong ownerId, long size, long journalSize, SaveDataFlags flags) { - return fs.RunOperationWithAccessLog(AccessLogTarget.System, - () => - { - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + Result rc; + Span logBuffer = stackalloc byte[0x100]; - var attribute = new SaveDataAttribute(ProgramId.InvalidId, SaveDataType.System, userId, saveDataId); + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = CreateSave(fs, spaceId, saveDataId, userId, ownerId, size, journalSize, flags); + Tick end = fs.Hos.Os.GetSystemTick(); - var createInfo = new SaveDataCreationInfo - { - Size = size, - JournalSize = journalSize, - BlockSize = 0x4000, - OwnerId = ownerId, - Flags = flags, - SpaceId = spaceId - }; + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataId).AppendFormat(saveDataId, 'X') + .Append(LogUserId).AppendFormat(userId.Id.High, 'X', 16).AppendFormat(userId.Id.Low, 'X', 16) + .Append(LogSaveDataOwnerId).AppendFormat(ownerId, 'X') + .Append(LogSaveDataSize).AppendFormat(size) + .Append(LogSaveDataJournalSize).AppendFormat(journalSize) + .Append(LogSaveDataFlags).AppendFormat((int)flags, 'X', 8); - return fsProxy.Target.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}"); + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = CreateSave(fs, spaceId, saveDataId, userId, ownerId, size, journalSize, flags); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result CreateSave(FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId, UserId userId, + ulong ownerId, long size, long journalSize, SaveDataFlags flags) + { + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + Result rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, Fs.SaveData.InvalidProgramId, + SaveDataType.System, userId, saveDataId); + if (rc.IsFailure()) return rc; + + rc = SaveDataCreationInfo.Make(out SaveDataCreationInfo creationInfo, size, journalSize, ownerId, flags, + spaceId); + if (rc.IsFailure()) return rc; + + return fsProxy.Target.CreateSaveDataFileSystemBySystemSaveDataId(in attribute, in creationInfo); + } } public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, UserId userId, ulong ownerId, long size, long journalSize, SaveDataFlags flags) { - return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, userId, ownerId, size, journalSize, flags); + return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, userId, ownerId, size, journalSize, + flags); } public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, UserId userId, long size, long journalSize, SaveDataFlags flags) { - return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, userId, 0, size, journalSize, flags); + return CreateSystemSaveData(fs, saveDataId, userId, 0, size, journalSize, flags); } public static Result CreateSystemSaveData(this FileSystemClient fs, ulong saveDataId, ulong ownerId, long size, long journalSize, SaveDataFlags flags) { - return CreateSystemSaveData(fs, SaveDataSpaceId.System, saveDataId, UserId.InvalidId, ownerId, size, journalSize, flags); + return CreateSystemSaveData(fs, saveDataId, Fs.SaveData.InvalidUserId, 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.InvalidId, 0, size, journalSize, flags); + return CreateSystemSaveData(fs, saveDataId, Fs.SaveData.InvalidUserId, 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.InvalidId, ownerId, size, journalSize, flags); + return CreateSystemSaveData(fs, spaceId, saveDataId, Fs.SaveData.InvalidUserId, ownerId, size, journalSize, + flags); } - public static Result QuerySaveDataTotalSize(this FileSystemClient fs, out long totalSize, long size, long journalSize) + public static Result QuerySaveDataTotalSize(this FileSystemClientImpl fs, out long totalSize, long size, + long journalSize) { - totalSize = default; + Unsafe.SkipInit(out totalSize); - long totalSizeTemp = 0; + using ReferenceCountedDisposable fsProxy = fs.GetFileSystemProxyServiceObject(); - Result result = fs.RunOperationWithAccessLog(AccessLogTarget.System, - () => - { - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - - return fsProxy.Target.QuerySaveDataTotalSize(out totalSizeTemp, size, journalSize); - }, - () => $", save_data_size: {size}, save_data_journal_size: {journalSize}"); - - if (result.IsSuccess()) - { - totalSize = totalSizeTemp; - } - - return result; - } - - public static void DisableAutoSaveDataCreation(this FileSystemClient fsClient) - { - using ReferenceCountedDisposable fsProxy = fsClient.Impl.GetFileSystemProxyServiceObject(); - - Result rc = fsProxy.Target.DisableAutoSaveDataCreation(); - - if (rc.IsFailure()) - { - Abort.DoAbort(rc); - } - } - } - - public struct SaveDataIterator : IDisposable - { - private FileSystemClient FsClient { get; } - private ReferenceCountedDisposable Reader { get; } - - internal SaveDataIterator(FileSystemClient fsClient, ReferenceCountedDisposable reader) - { - FsClient = fsClient; - Reader = reader.AddReference(); - } - - public Result ReadSaveDataInfo(out long readCount, Span buffer) - { - Result rc; - - var byteBuffer = new OutBuffer(MemoryMarshal.Cast(buffer)); - - if (FsClient.IsEnabledAccessLog(AccessLogTarget.System)) - { - System.TimeSpan startTime = FsClient.Time.GetCurrent(); - rc = Reader.Target.Read(out readCount, byteBuffer); - System.TimeSpan endTime = FsClient.Time.GetCurrent(); - - FsClient.OutputAccessLog(rc, startTime, endTime, $", size: {buffer.Length}"); - } - else - { - rc = Reader.Target.Read(out readCount, byteBuffer); - } + Result rc = fsProxy.Target.QuerySaveDataTotalSize(out long tempTotalSize, size, journalSize); + if (rc.IsSuccess()) + totalSize = tempTotalSize; return rc; } - public void Dispose() + public static Result QuerySaveDataTotalSize(this FileSystemClient fs, out long totalSize, long size, + long journalSize) { - Reader?.Dispose(); + Result rc; + Span logBuffer = stackalloc byte[0x50]; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System) && fs.Impl.IsEnabledHandleAccessLog(null)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.QuerySaveDataTotalSize(out totalSize, size, journalSize); + Tick end = fs.Hos.Os.GetSystemTick(); + + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogSaveDataSize).AppendFormat(size) + .Append(LogSaveDataJournalSize).AppendFormat(journalSize); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = fs.Impl.QuerySaveDataTotalSize(out totalSize, size, journalSize); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + } + + public static void DisableAutoSaveDataCreation(this FileSystemClient fs) + { + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + Result rc = fsProxy.Target.DisableAutoSaveDataCreation(); + + fs.Impl.LogErrorMessage(rc); + Abort.DoAbortUnless(rc.IsSuccess()); } } } diff --git a/src/LibHac/Fs/Shim/SdCard.cs b/src/LibHac/Fs/Shim/SdCard.cs index 59be8162..94c198f4 100644 --- a/src/LibHac/Fs/Shim/SdCard.cs +++ b/src/LibHac/Fs/Shim/SdCard.cs @@ -1,71 +1,147 @@ -using LibHac.Common; +using System; +using System.Runtime.CompilerServices; +using LibHac.Common; +using LibHac.Diag; using LibHac.Fs.Fsa; using LibHac.Fs.Impl; using LibHac.FsSrv.Sf; +using LibHac.Os; +using static LibHac.Fs.Impl.AccessLogStrings; using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem; namespace LibHac.Fs.Shim { + [SkipLocalsInit] public static class SdCard { + private static Result OpenSdCardFileSystem(FileSystemClient fs, + out ReferenceCountedDisposable fileSystem) + { + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + return OpenFileSystem(fs, fsProxy, out fileSystem); + + static Result OpenFileSystem(FileSystemClient fs, ReferenceCountedDisposable fsProxy, + out ReferenceCountedDisposable fileSystem) + { + fileSystem = default; + + // Retry a few times if the storage device isn't ready yet + const int maxRetries = 10; + const int retryInterval = 1000; + + for (int i = 0; i < maxRetries; i++) + { + Result rc = fsProxy.Target.OpenSdCardFileSystem(out fileSystem); + + if (rc.IsSuccess()) + break; + + if (!ResultFs.StorageDeviceNotReady.Includes(rc)) + return rc; + + if (i == maxRetries - 1) + return rc; + + fs.Hos.Os.SleepThread(TimeSpan.FromMilliSeconds(retryInterval)); + } + + return Result.Success; + } + } + + private static Result RegisterFileSystem(FileSystemClient fs, U8Span mountName, + ReferenceCountedDisposable fileSystem) + { + var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem); + return fs.Register(mountName, fileSystemAdapter); + } + public static Result MountSdCard(this FileSystemClient fs, U8Span mountName) { Result rc; + Span logBuffer = stackalloc byte[0x30]; - if (fs.IsEnabledAccessLog(AccessLogTarget.Application)) + // Check if the mount name is valid + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) { - System.TimeSpan startTime = fs.Time.GetCurrent(); - rc = Run(fs, mountName); - System.TimeSpan endTime = fs.Time.GetCurrent(); + Tick start = fs.Hos.Os.GetSystemTick(); + rc = fs.Impl.CheckMountName(mountName); + Tick end = fs.Hos.Os.GetSystemTick(); - fs.OutputAccessLog(rc, startTime, endTime, ""); + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogName).Append(mountName); + logBuffer = sb.Buffer; + + fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer)); } else { - rc = Run(fs, mountName); + rc = fs.Impl.CheckMountName(mountName); } - + fs.Impl.AbortIfNeeded(rc); if (rc.IsFailure()) return rc; - if (fs.IsEnabledAccessLog(AccessLogTarget.Application)) + // Open the SD card file system + ReferenceCountedDisposable fileSystem = null; + try { - fs.EnableFileSystemAccessorAccessLog(mountName); - } - - return Result.Success; - - static Result Run(FileSystemClient fs, U8Span mountName) - { - // ReSharper disable once VariableHidesOuterVariable - Result rc = MountHelpers.CheckMountName(mountName); - if (rc.IsFailure()) return rc; - - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - - rc = fsProxy.Target.OpenSdCardFileSystem(out ReferenceCountedDisposable fileSystem); - if (rc.IsFailure()) return rc; - - using (fileSystem) + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) { - var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem); + Tick start = fs.Hos.Os.GetSystemTick(); + rc = OpenSdCardFileSystem(fs, out fileSystem); + Tick end = fs.Hos.Os.GetSystemTick(); - return fs.Register(mountName, fileSystemAdapter); + fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer)); } + else + { + rc = OpenSdCardFileSystem(fs, out fileSystem); + } + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; + + // Mount the file system + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) + { + Tick start = fs.Hos.Os.GetSystemTick(); + rc = RegisterFileSystem(fs, mountName, fileSystem); + Tick end = fs.Hos.Os.GetSystemTick(); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer)); + } + else + { + rc = RegisterFileSystem(fs, mountName, fileSystem); + } + fs.Impl.AbortIfNeeded(rc); + if (rc.IsFailure()) return rc; + + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) + fs.Impl.EnableFileSystemAccessorAccessLog(mountName); + + return Result.Success; + } + finally + { + fileSystem?.Dispose(); } } public static bool IsSdCardInserted(this FileSystemClient fs) { + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + ReferenceCountedDisposable deviceOperator = null; try { - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - Result rc = fsProxy.Target.OpenDeviceOperator(out deviceOperator); - if (rc.IsFailure()) throw new HorizonResultException(rc, "Abort"); + fs.Impl.LogErrorMessage(rc); + Abort.DoAbortUnless(rc.IsSuccess()); - rc = deviceOperator.Target.IsSdCardInserted(out bool isInserted); - if (rc.IsFailure()) throw new HorizonResultException(rc, "Abort"); + rc = CheckIfInserted(fs, deviceOperator, out bool isInserted); + fs.Impl.LogErrorMessage(rc); + Abort.DoAbortUnless(rc.IsSuccess()); return isInserted; } @@ -73,6 +149,34 @@ namespace LibHac.Fs.Shim { deviceOperator?.Dispose(); } + + static Result CheckIfInserted(FileSystemClient fs, + ReferenceCountedDisposable deviceOperator, out bool isInserted) + { + Unsafe.SkipInit(out isInserted); + + // Retry a few times if the storage device isn't ready yet + const int maxRetries = 10; + const int retryInterval = 1000; + + for (int i = 0; i < maxRetries; i++) + { + Result rc = deviceOperator.Target.IsSdCardInserted(out isInserted); + + if (rc.IsSuccess()) + break; + + if (!ResultFs.StorageDeviceNotReady.Includes(rc)) + return rc; + + if (i == maxRetries - 1) + return rc; + + fs.Hos.Os.SleepThread(TimeSpan.FromMilliSeconds(retryInterval)); + } + + return Result.Success; + } } public static Result SetSdCardEncryptionSeed(this FileSystemClient fs, in EncryptionSeed seed) @@ -80,17 +184,15 @@ namespace LibHac.Fs.Shim using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); Result rc = fsProxy.Target.SetSdCardEncryptionSeed(in seed); - if (rc.IsFailure()) throw new HorizonResultException(rc, "Abort"); - - return Result.Success; + fs.Impl.AbortIfNeeded(rc); + return rc; } public static void SetSdCardAccessibility(this FileSystemClient fs, bool isAccessible) { - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - - Result rc = fsProxy.Target.SetSdCardAccessibility(isAccessible); - if (rc.IsFailure()) throw new HorizonResultException(rc, "Abort"); + Result rc = fs.Impl.SetSdCardAccessibility(isAccessible); + fs.Impl.LogErrorMessage(rc); + Abort.DoAbortUnless(rc.IsSuccess()); } public static bool IsSdCardAccessible(this FileSystemClient fs) @@ -98,9 +200,19 @@ namespace LibHac.Fs.Shim using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); Result rc = fsProxy.Target.IsSdCardAccessible(out bool isAccessible); - if (rc.IsFailure()) throw new HorizonResultException(rc, "Abort"); + fs.Impl.LogErrorMessage(rc); + Abort.DoAbortUnless(rc.IsSuccess()); return isAccessible; } + + public static Result SetSdCardAccessibility(this FileSystemClientImpl fs, bool isAccessible) + { + using ReferenceCountedDisposable fsProxy = fs.GetFileSystemProxyServiceObject(); + + Result rc = fsProxy.Target.SetSdCardAccessibility(isAccessible); + fs.AbortIfNeeded(rc); + return rc; + } } } diff --git a/src/LibHac/Fs/Shim/SystemSaveData.cs b/src/LibHac/Fs/Shim/SystemSaveData.cs index 0839d518..3edd50b9 100644 --- a/src/LibHac/Fs/Shim/SystemSaveData.cs +++ b/src/LibHac/Fs/Shim/SystemSaveData.cs @@ -1,43 +1,97 @@ -using LibHac.Common; +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 LibHac.Os; +using static LibHac.Fs.Impl.AccessLogStrings; using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem; namespace LibHac.Fs.Shim { + [SkipLocalsInit] public static class SystemSaveData { + public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName, ulong saveDataId) + { + return fs.MountSystemSaveData(mountName, saveDataId, Fs.SaveData.InvalidUserId); + } + public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName, SaveDataSpaceId spaceId, ulong saveDataId) { - return MountSystemSaveData(fs, mountName, spaceId, saveDataId, UserId.InvalidId); + return fs.MountSystemSaveData(mountName, spaceId, saveDataId, Fs.SaveData.InvalidUserId); + } + + public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName, ulong saveDataId, + UserId userId) + { + return fs.MountSystemSaveData(mountName, SaveDataSpaceId.System, saveDataId, userId); } public static Result MountSystemSaveData(this FileSystemClient fs, U8Span mountName, SaveDataSpaceId spaceId, ulong saveDataId, UserId userId) { - Result rc = MountHelpers.CheckMountName(mountName); - if (rc.IsFailure()) return rc; + Result rc; + Span logBuffer = stackalloc byte[0x90]; - using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); - - var attribute = new SaveDataAttribute(ProgramId.InvalidId, SaveDataType.System, userId, saveDataId); - - ReferenceCountedDisposable saveFs = null; - - try + if (fs.Impl.IsEnabledAccessLog(AccessLogTarget.System)) { - rc = fsProxy.Target.OpenSaveDataFileSystemBySystemSaveDataId(out saveFs, spaceId, in attribute); + Tick start = fs.Hos.Os.GetSystemTick(); + rc = Mount(fs, mountName, spaceId, saveDataId, userId); + Tick end = fs.Hos.Os.GetSystemTick(); + + var idString = new IdString(); + var sb = new U8StringBuilder(logBuffer, true); + sb.Append(LogName).Append(mountName) + .Append(LogSaveDataSpaceId).Append(idString.ToString(spaceId)) + .Append(LogSaveDataId).AppendFormat(saveDataId, 'X') + .Append(LogUserId).AppendFormat(userId.Id.High, 'X', 16).AppendFormat(userId.Id.Low, 'X', 16); + + fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(sb.Buffer)); + } + else + { + rc = Mount(fs, mountName, spaceId, saveDataId, userId); + } + + fs.Impl.AbortIfNeeded(rc); + return rc; + + static Result Mount(FileSystemClient fs, U8Span mountName, SaveDataSpaceId spaceId, ulong saveDataId, + UserId userId) + { + Result rc = fs.Impl.CheckMountName(mountName); if (rc.IsFailure()) return rc; - var fileSystemAdapter = new FileSystemServiceObjectAdapter(saveFs); - return fs.Register(mountName, fileSystemAdapter); - } - finally - { - saveFs?.Dispose(); + using ReferenceCountedDisposable fsProxy = fs.Impl.GetFileSystemProxyServiceObject(); + + rc = SaveDataAttribute.Make(out SaveDataAttribute attribute, Fs.SaveData.InvalidProgramId, + SaveDataType.System, userId, saveDataId); + if (rc.IsFailure()) return rc; + + ReferenceCountedDisposable fileSystem = null; + try + { + rc = fsProxy.Target.OpenSaveDataFileSystemBySystemSaveDataId(out fileSystem, spaceId, in attribute); + if (rc.IsFailure()) return rc; + + var fileSystemAdapter = new FileSystemServiceObjectAdapter(fileSystem); + + if (spaceId == SaveDataSpaceId.System) + { + return fs.Register(mountName, fileSystemAdapter, fileSystemAdapter, null, false, false); + } + else + { + return fs.Register(mountName, fileSystemAdapter); + } + } + finally + { + fileSystem?.Dispose(); + } } } } diff --git a/src/LibHac/FsSrv/FileSystemProxyImpl.cs b/src/LibHac/FsSrv/FileSystemProxyImpl.cs index 2899b182..7d805f7e 100644 --- a/src/LibHac/FsSrv/FileSystemProxyImpl.cs +++ b/src/LibHac/FsSrv/FileSystemProxyImpl.cs @@ -530,7 +530,7 @@ namespace LibHac.FsSrv try { rc = FsProxyCore.OpenHostFileSystem(out hostFs, new U8Span(path.Str), - option.HasFlag(MountHostOption.PseudoCaseSensitive)); + option.Flags.HasFlag(MountHostOptionFlag.PseudoCaseSensitive)); if (rc.IsFailure()) return rc; bool isRootPath = path.Str[0] == 0; diff --git a/src/LibHac/FsSrv/FileSystemServer.cs b/src/LibHac/FsSrv/FileSystemServer.cs index c8b24177..d44ca125 100644 --- a/src/LibHac/FsSrv/FileSystemServer.cs +++ b/src/LibHac/FsSrv/FileSystemServer.cs @@ -17,8 +17,7 @@ namespace LibHac.FsSrv /// The that will be used by this server. public FileSystemServer(HorizonClient horizonClient) { - Globals.Hos = horizonClient; - Globals.InitMutex = new object(); + Globals.Initialize(horizonClient); } } @@ -31,6 +30,12 @@ namespace LibHac.FsSrv public DeviceEventSimulatorGlobals DeviceEventSimulator; public AccessControlGlobals AccessControl; public StorageDeviceManagerFactoryGlobals StorageDeviceManagerFactory; + + public void Initialize(HorizonClient horizonClient) + { + Hos = horizonClient; + InitMutex = new object(); + } } // Functions in the nn::fssrv::storage namespace use this struct. diff --git a/src/LibHac/FsSrv/GameCardHandle.cs b/src/LibHac/FsSrv/GameCardHandle.cs index 3b3cf7aa..0c77017b 100644 --- a/src/LibHac/FsSrv/GameCardHandle.cs +++ b/src/LibHac/FsSrv/GameCardHandle.cs @@ -2,7 +2,7 @@ namespace LibHac.FsSrv { - public struct GameCardHandle : IEquatable + public readonly struct GameCardHandle : IEquatable { public readonly int Value; diff --git a/src/LibHac/FsSrv/Impl/SaveDataProperties.cs b/src/LibHac/FsSrv/Impl/SaveDataProperties.cs index b75ba32b..528f05c2 100644 --- a/src/LibHac/FsSrv/Impl/SaveDataProperties.cs +++ b/src/LibHac/FsSrv/Impl/SaveDataProperties.cs @@ -5,6 +5,9 @@ namespace LibHac.FsSrv.Impl { public static class SaveDataProperties { + public const long DefaultSaveDataBlockSize = 0x4000; + public const long BcatSaveDataJournalSize = 0x200000; + public static bool IsJournalingSupported(SaveDataType type) { switch (type) diff --git a/src/LibHac/Horizon.cs b/src/LibHac/Horizon.cs index fd4e2f0d..b27742b6 100644 --- a/src/LibHac/Horizon.cs +++ b/src/LibHac/Horizon.cs @@ -1,5 +1,4 @@ -using System.Diagnostics; -using System.Threading; +using System.Threading; using LibHac.Common; using LibHac.Diag; using LibHac.Fs.Shim; @@ -13,21 +12,19 @@ namespace LibHac public class Horizon { private const int InitialProcessCountMax = 0x50; - internal long StartTick { get; } - internal ITimeSpanGenerator Time { get; } + + internal ITickGenerator TickGenerator { get; } internal ServiceManager ServiceManager { get; } private HorizonClient LoaderClient { get; } private ulong _currentInitialProcessId; private ulong _currentProcessId; - // Todo: Initialize with a configuration object - public Horizon(ITimeSpanGenerator timer) + public Horizon(HorizonConfiguration config) { _currentProcessId = InitialProcessCountMax; - Time = timer ?? new StopWatchTimeSpanGenerator(); - StartTick = Stopwatch.GetTimestamp(); + TickGenerator = config.TickGenerator; ServiceManager = new ServiceManager(); LoaderClient = CreatePrivilegedHorizonClient(); diff --git a/src/LibHac/HorizonClient.cs b/src/LibHac/HorizonClient.cs index c828f7cb..6b0f6d3a 100644 --- a/src/LibHac/HorizonClient.cs +++ b/src/LibHac/HorizonClient.cs @@ -21,16 +21,14 @@ namespace LibHac public LrClient Lr { get; } public ArpClient Arp => ArpLazy.Value; - public ITimeSpanGenerator Time => Horizon.Time; - internal HorizonClient(Horizon horizon, ProcessId processId) { Horizon = horizon; ProcessId = processId; Fs = new FileSystemClient(this); - Sm = new ServiceManagerClient(horizon.ServiceManager); - Os = new OsState(this, horizon.StartTick); + Sm = new ServiceManagerClient(Horizon.ServiceManager); + Os = new OsState(this, horizon.TickGenerator); Lr = new LrClient(this); ArpLazy = new Lazy(InitArpClient, true); diff --git a/src/LibHac/HorizonConfiguration.cs b/src/LibHac/HorizonConfiguration.cs new file mode 100644 index 00000000..6e665ea3 --- /dev/null +++ b/src/LibHac/HorizonConfiguration.cs @@ -0,0 +1,22 @@ +#nullable enable +using LibHac.Os; + +namespace LibHac +{ + /// + /// Contains configuration options for instantiating a object. + /// + public class HorizonConfiguration + { + /// + /// Used when getting the current system . + /// If , a default is used. + /// + public ITickGenerator? TickGenerator { get; set; } + } + + public interface ITickGenerator + { + Tick GetCurrentTick(); + } +} diff --git a/src/LibHac/HorizonFactory.cs b/src/LibHac/HorizonFactory.cs index 9e465714..f7a5f3bb 100644 --- a/src/LibHac/HorizonFactory.cs +++ b/src/LibHac/HorizonFactory.cs @@ -1,13 +1,15 @@ using LibHac.Bcat; +using LibHac.Common.Keys; +using LibHac.Fs.Fsa; using LibHac.FsSrv; namespace LibHac { public static class HorizonFactory { - public static Horizon CreateWithFsConfig(ITimeSpanGenerator timer, FileSystemServerConfig fsServerConfig) + public static Horizon CreateWithFsConfig(HorizonConfiguration config, FileSystemServerConfig fsServerConfig) { - var horizon = new Horizon(timer); + var horizon = new Horizon(config); HorizonClient fsServerClient = horizon.CreatePrivilegedHorizonClient(); var fsServer = new FileSystemServer(fsServerClient); @@ -19,5 +21,29 @@ namespace LibHac return horizon; } + + public static Horizon CreateWithDefaultFsConfig(HorizonConfiguration config, IFileSystem rootFileSystem, KeySet keySet) + { + var horizon = new Horizon(config); + + var defaultObjects = DefaultFsServerObjects.GetDefaultEmulatedCreators(rootFileSystem, keySet); + + HorizonClient fsServerClient = horizon.CreatePrivilegedHorizonClient(); + var fsServer = new FileSystemServer(fsServerClient); + + var fsServerConfig = new FileSystemServerConfig + { + DeviceOperator = defaultObjects.DeviceOperator, + ExternalKeySet = keySet.ExternalKeySet, + FsCreators = defaultObjects.FsCreators + }; + + FileSystemServerInitializer.InitializeWithConfig(fsServerClient, fsServer, fsServerConfig); + + HorizonClient bcatServerClient = horizon.CreateHorizonClient(); + _ = new BcatServer(bcatServerClient); + + return horizon; + } } } diff --git a/src/LibHac/Os/Impl/OsResourceManager.cs b/src/LibHac/Os/Impl/OsResourceManager.cs index 46b2abdc..a80a6912 100644 --- a/src/LibHac/Os/Impl/OsResourceManager.cs +++ b/src/LibHac/Os/Impl/OsResourceManager.cs @@ -7,9 +7,9 @@ namespace LibHac.Os.Impl public TickManager TickManager { get; } // Todo: Use configuration object if/when more options are added - public OsResourceManager(long startTick) + public OsResourceManager(ITickGenerator tickGenerator) { - TickManager = new TickManager(startTick); + TickManager = new TickManager(tickGenerator); } public void Dispose() diff --git a/src/LibHac/Os/Impl/TickManager-os.net.cs b/src/LibHac/Os/Impl/TickManager-os.net.cs index 3918b089..a588b12c 100644 --- a/src/LibHac/Os/Impl/TickManager-os.net.cs +++ b/src/LibHac/Os/Impl/TickManager-os.net.cs @@ -7,11 +7,11 @@ namespace LibHac.Os.Impl internal struct TickManagerImpl : IDisposable { private long _tickFrequency; - private long _startTick; + private ITickGenerator _tickGenerator; private TimeSpan _maxTimeSpan; private long _maxTick; - public TickManagerImpl(long startTick) + public TickManagerImpl(ITickGenerator tickGenerator) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { @@ -19,7 +19,7 @@ namespace LibHac.Os.Impl } _tickFrequency = Stopwatch.Frequency; - _startTick = startTick; + _tickGenerator = tickGenerator; long nanoSecondsPerSecond = TimeSpan.FromSeconds(1).GetNanoSeconds(); @@ -43,8 +43,8 @@ namespace LibHac.Os.Impl } } - public Tick GetTick() => new Tick(Stopwatch.GetTimestamp() - _startTick); - public Tick GetSystemTickOrdered() => new Tick(Stopwatch.GetTimestamp() - _startTick); + public Tick GetTick() => _tickGenerator.GetCurrentTick(); + public Tick GetSystemTickOrdered() => _tickGenerator.GetCurrentTick(); public long GetTickFrequency() => _tickFrequency; public long GetMaxTick() => _maxTick; public long GetMaxTimeSpanNs() => _maxTimeSpan.GetNanoSeconds(); diff --git a/src/LibHac/Os/Impl/TickManager.cs b/src/LibHac/Os/Impl/TickManager.cs index 34a5f397..3d868c7e 100644 --- a/src/LibHac/Os/Impl/TickManager.cs +++ b/src/LibHac/Os/Impl/TickManager.cs @@ -16,7 +16,7 @@ namespace LibHac.Os.Impl private TickManagerImpl _impl; - public TickManager(long startTick) => _impl = new TickManagerImpl(startTick); + public TickManager(ITickGenerator tickGenerator) => _impl = new TickManagerImpl(tickGenerator); ~TickManager() { diff --git a/src/LibHac/Os/OsState.cs b/src/LibHac/Os/OsState.cs index 4a2d80da..3b7466c9 100644 --- a/src/LibHac/Os/OsState.cs +++ b/src/LibHac/Os/OsState.cs @@ -9,10 +9,10 @@ namespace LibHac.Os internal OsResourceManager ResourceManager { get; } // Todo: Use configuration object if/when more options are added - internal OsState(HorizonClient horizonClient, long startTick) + internal OsState(HorizonClient horizonClient, ITickGenerator tickGenerator) { Hos = horizonClient; - ResourceManager = new OsResourceManager(startTick); + ResourceManager = new OsResourceManager(tickGenerator); } public ProcessId GetCurrentProcessId() diff --git a/src/hactoolnet/Program.cs b/src/hactoolnet/Program.cs index 0043a9fb..fa174dac 100644 --- a/src/hactoolnet/Program.cs +++ b/src/hactoolnet/Program.cs @@ -67,17 +67,18 @@ namespace hactoolnet using (var logger = new ProgressBar()) { ctx.Logger = logger; - ctx.FsClient = new FileSystemClient(new StopWatchTimeSpanGenerator()); + OpenKeySet(ctx); + + Horizon horizon = HorizonFactory.CreateWithDefaultFsConfig(new HorizonConfiguration(), + new InMemoryFileSystem(), ctx.KeySet); + ctx.FsClient = horizon.CreatePrivilegedHorizonClient().Fs; if (ctx.Options.AccessLog != null) { logWriter = new StreamWriter(ctx.Options.AccessLog); - var accessLog = new TextWriterAccessLog(logWriter); ctx.FsClient.SetLocalSystemAccessLogForDebug(true); ctx.FsClient.SetGlobalAccessLogMode(GlobalAccessLogMode.Log); - - ctx.FsClient.SetAccessLogObject(accessLog); } if (ctx.Options.ResultLog != null) @@ -88,8 +89,6 @@ namespace hactoolnet Result.SetLogger(resultLogger); } - OpenKeySet(ctx); - if (ctx.Options.RunCustom) { CustomTask(ctx); diff --git a/tests/LibHac.Tests/Fs/FileSystemClientTests/FileSystemServerFactory.cs b/tests/LibHac.Tests/Fs/FileSystemClientTests/FileSystemServerFactory.cs index abcba92d..0042d795 100644 --- a/tests/LibHac.Tests/Fs/FileSystemClientTests/FileSystemServerFactory.cs +++ b/tests/LibHac.Tests/Fs/FileSystemClientTests/FileSystemServerFactory.cs @@ -20,7 +20,7 @@ namespace LibHac.Tests.Fs.FileSystemClientTests config.DeviceOperator = defaultObjects.DeviceOperator; config.ExternalKeySet = new ExternalKeySet(); - Horizon horizon = LibHac.HorizonFactory.CreateWithFsConfig(new StopWatchTimeSpanGenerator(), config); + Horizon horizon = LibHac.HorizonFactory.CreateWithFsConfig(new HorizonConfiguration(), config); HorizonClient horizonClient = horizon.CreatePrivilegedHorizonClient(); diff --git a/tests/LibHac.Tests/HorizonFactory.cs b/tests/LibHac.Tests/HorizonFactory.cs index 85aca29e..a0ccc3ab 100644 --- a/tests/LibHac.Tests/HorizonFactory.cs +++ b/tests/LibHac.Tests/HorizonFactory.cs @@ -18,7 +18,7 @@ namespace LibHac.Tests config.DeviceOperator = defaultObjects.DeviceOperator; config.ExternalKeySet = new ExternalKeySet(); - Horizon horizon = LibHac.HorizonFactory.CreateWithFsConfig(new StopWatchTimeSpanGenerator(), config); + Horizon horizon = LibHac.HorizonFactory.CreateWithFsConfig(new HorizonConfiguration(), config); return horizon; }