From d1110392b49ee722f798db9236234c68a6c79e67 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Sat, 21 Mar 2020 21:13:59 -0700 Subject: [PATCH 1/6] Add a function to specify that a returned Result is unused --- src/LibHac/FsService/FileSystemProxy.cs | 12 ++++++------ src/LibHac/FsService/FileSystemProxyCore.cs | 5 +++-- src/LibHac/FsService/FileSystemServer.cs | 2 +- src/LibHac/Result.cs | 6 ++++++ 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/LibHac/FsService/FileSystemProxy.cs b/src/LibHac/FsService/FileSystemProxy.cs index 266be3ad..77f1ae68 100644 --- a/src/LibHac/FsService/FileSystemProxy.cs +++ b/src/LibHac/FsService/FileSystemProxy.cs @@ -160,10 +160,10 @@ namespace LibHac.FsService // Check if permissions allow deleting save data } - reader.Indexer.SetState(saveDataId, SaveDataState.Creating); + rc = reader.Indexer.SetState(saveDataId, SaveDataState.Creating); if (rc.IsFailure()) return rc; - reader.Indexer.Commit(); + rc = reader.Indexer.Commit(); if (rc.IsFailure()) return rc; } @@ -406,7 +406,7 @@ namespace LibHac.FsService // Revert changes if an error happened in the middle of creation if (isDeleteNeeded) { - DeleteSaveDataFileSystemImpl2(creationInfo.SpaceId, saveDataId); + DeleteSaveDataFileSystemImpl2(creationInfo.SpaceId, saveDataId).IgnoreResult(); if (reader.IsInitialized && saveDataId != FileSystemServer.SaveIndexerId) { @@ -414,8 +414,8 @@ namespace LibHac.FsService if (rc.IsSuccess() && value.SpaceId == creationInfo.SpaceId) { - reader.Indexer.Delete(saveDataId); - reader.Indexer.Commit(); + reader.Indexer.Delete(saveDataId).IgnoreResult(); + reader.Indexer.Commit().IgnoreResult(); } } } @@ -1151,7 +1151,7 @@ namespace LibHac.FsService rc = FsServer.SaveDataIndexerManager.GetSaveDataIndexer(out reader, SaveDataSpaceId.Temporary); if (rc.IsFailure()) return rc; - reader.Indexer.Reset(); + reader.Indexer.Reset().IgnoreResult(); return Result.Success; } diff --git a/src/LibHac/FsService/FileSystemProxyCore.cs b/src/LibHac/FsService/FileSystemProxyCore.cs index 3531c222..0feb8a81 100644 --- a/src/LibHac/FsService/FileSystemProxyCore.cs +++ b/src/LibHac/FsService/FileSystemProxyCore.cs @@ -677,7 +677,8 @@ namespace LibHac.FsService if (rc.IsFailure()) return rc; - baseFileSystem.EnsureDirectoryExists(contentDirPath.ToString()); + rc = baseFileSystem.EnsureDirectoryExists(contentDirPath.ToString()); + if (rc.IsFailure()) return rc; rc = FsCreators.SubDirectoryFileSystemCreator.Create(out IFileSystem subDirFileSystem, baseFileSystem, contentDirPath); @@ -879,7 +880,7 @@ namespace LibHac.FsService if (cacheExtraData) { - // Missing extra data caching + // todo: Missing extra data caching } fileSystem = openReadOnly ? new ReadOnlyFileSystem(saveFs) : saveFs; diff --git a/src/LibHac/FsService/FileSystemServer.cs b/src/LibHac/FsService/FileSystemServer.cs index ae55b265..84c16f05 100644 --- a/src/LibHac/FsService/FileSystemServer.cs +++ b/src/LibHac/FsService/FileSystemServer.cs @@ -42,7 +42,7 @@ namespace LibHac.FsService SaveDataIndexerManager = new SaveDataIndexerManager(FsClient, SaveIndexerId); - fsProxy.CleanUpTemporaryStorage(); + fsProxy.CleanUpTemporaryStorage().IgnoreResult(); } /// diff --git a/src/LibHac/Result.cs b/src/LibHac/Result.cs index 3a8fd232..660e4be1 100644 --- a/src/LibHac/Result.cs +++ b/src/LibHac/Result.cs @@ -70,6 +70,11 @@ namespace LibHac public bool IsSuccess() => _value == SuccessValue; public bool IsFailure() => !IsSuccess(); + /// + /// Specifies that the from a returned function is explicitly ignored. + /// + public void IgnoreResult() { } + public void ThrowIfFailure() { if (IsFailure()) @@ -212,6 +217,7 @@ namespace LibHac /// new Result.Base(ModuleFs, 2000, 2499). The property will need to have the aggressive inlining flag set like so: /// public static Result.Base SdCardAccessFailed { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 2000, 2499); } /// + [DebuggerDisplay("{" + nameof(ToStringWithName) + "(),nq}")] public struct Base { private const int DescriptionEndBitsOffset = ReservedBitsOffset; From ce54ae111cb0a64677ab458add72ef3923343df6 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Sat, 21 Mar 2020 22:44:38 -0700 Subject: [PATCH 2/6] Add MultiCommitManager - Recovering from an interrupted commit isn't implemented yet --- build/CodeGen/results.csv | 7 + src/LibHac/Fs/FileSystemBase.cs | 36 +++ src/LibHac/Fs/IFileSystem.cs | 6 + src/LibHac/Fs/ResultFs.cs | 11 + src/LibHac/Fs/SaveDataStructs.cs | 1 + src/LibHac/FsService/FileSystemProxy.cs | 19 +- src/LibHac/FsService/IMultiCommitManager.cs | 10 + .../FsService/Impl/MultiCommitManager.cs | 262 ++++++++++++++++++ 8 files changed, 351 insertions(+), 1 deletion(-) create mode 100644 src/LibHac/FsService/IMultiCommitManager.cs create mode 100644 src/LibHac/FsService/Impl/MultiCommitManager.cs diff --git a/build/CodeGen/results.csv b/build/CodeGen/results.csv index 992e05a0..b788bb4b 100644 --- a/build/CodeGen/results.csv +++ b/build/CodeGen/results.csv @@ -127,6 +127,10 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary 2,4771,4779,SignedSystemPartitionDataCorrupted, 2,4781,,GameCardLogoDataCorrupted, +2,4791,4799,MultiCommitContextCorrupted, +2,4791,,InvalidMultiCommitContextVersion, +2,4792,,InvalidMultiCommitContextState, + # The range name is a guess. 4812 is currently the only result in it 2,4811,4819,ZeroBitmapFileCorrupted, 2,4812,,IncompleteBlockInZeroBitmapHashStorageFile, @@ -197,14 +201,17 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary 2,6454,,WriteStateUnflushed, 2,6457,,WriteModeFileNotClosed, 2,6461,,AllocatorAlignmentViolation, +2,6463,,MultiCommitFileSystemAlreadyAdded, 2,6465,,UserNotExist, 2,6600,6699,EntryNotFound, 2,6606,,TargetProgramIndexNotFound,Specified program index is not found + 2,6700,6799,OutOfResource, 2,6706,,MappingTableFull, 2,6707,,AllocationTableInsufficientFreeBlocks, 2,6709,,OpenCountLimit, +2,6710,,MultiCommitFileSystemLimit, 2,6800,6899,MappingFailed, 2,6811,,RemapStorageMapFull, diff --git a/src/LibHac/Fs/FileSystemBase.cs b/src/LibHac/Fs/FileSystemBase.cs index 5303b688..5e8fdc08 100644 --- a/src/LibHac/Fs/FileSystemBase.cs +++ b/src/LibHac/Fs/FileSystemBase.cs @@ -35,6 +35,21 @@ namespace LibHac.Fs return ResultFs.NotImplemented.Log(); } + protected virtual Result CommitProvisionallyImpl(long commitCount) + { + return ResultFs.NotImplemented.Log(); + } + + protected virtual Result RollbackImpl() + { + return ResultFs.NotImplemented.Log(); + } + + protected virtual Result FlushImpl() + { + return ResultFs.NotImplemented.Log(); + } + protected virtual Result GetFileTimeStampRawImpl(out FileTimeStampRaw timeStamp, U8Span path) { timeStamp = default; @@ -200,6 +215,27 @@ namespace LibHac.Fs return CommitImpl(); } + public Result CommitProvisionally(long commitCount) + { + if (IsDisposed) return ResultFs.PreconditionViolation.Log(); + + return CommitProvisionallyImpl(commitCount); + } + + public Result Rollback() + { + if (IsDisposed) return ResultFs.PreconditionViolation.Log(); + + return RollbackImpl(); + } + + public Result Flush() + { + if (IsDisposed) return ResultFs.PreconditionViolation.Log(); + + return FlushImpl(); + } + public Result QueryEntry(Span outBuffer, ReadOnlySpan inBuffer, QueryId queryId, U8Span path) { if (IsDisposed) return ResultFs.PreconditionViolation.Log(); diff --git a/src/LibHac/Fs/IFileSystem.cs b/src/LibHac/Fs/IFileSystem.cs index 7192378e..99415b6b 100644 --- a/src/LibHac/Fs/IFileSystem.cs +++ b/src/LibHac/Fs/IFileSystem.cs @@ -183,6 +183,12 @@ namespace LibHac.Fs /// The of the requested operation. Result Commit(); + Result CommitProvisionally(long commitCount); + + Result Rollback(); + + Result Flush(); + /// /// Gets the creation, last accessed, and last modified timestamps of a file or directory. /// diff --git a/src/LibHac/Fs/ResultFs.cs b/src/LibHac/Fs/ResultFs.cs index cb4f4bd0..11003473 100644 --- a/src/LibHac/Fs/ResultFs.cs +++ b/src/LibHac/Fs/ResultFs.cs @@ -256,6 +256,13 @@ namespace LibHac.Fs /// Error code: 2002-4781; Inner value: 0x255a02 public static Result.Base GameCardLogoDataCorrupted => new Result.Base(ModuleFs, 4781); + /// Error code: 2002-4791; Range: 4791-4799; Inner value: 0x256e02 + public static Result.Base MultiCommitContextCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 4791, 4799); } + /// Error code: 2002-4791; Inner value: 0x256e02 + public static Result.Base InvalidMultiCommitContextVersion => new Result.Base(ModuleFs, 4791); + /// Error code: 2002-4792; Inner value: 0x257002 + public static Result.Base InvalidMultiCommitContextState => new Result.Base(ModuleFs, 4792); + /// Error code: 2002-4811; Range: 4811-4819; Inner value: 0x259602 public static Result.Base ZeroBitmapFileCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 4811, 4819); } /// Error code: 2002-4812; Inner value: 0x259802 @@ -385,6 +392,8 @@ namespace LibHac.Fs public static Result.Base WriteModeFileNotClosed => new Result.Base(ModuleFs, 6457); /// Error code: 2002-6461; Inner value: 0x327a02 public static Result.Base AllocatorAlignmentViolation => new Result.Base(ModuleFs, 6461); + /// Error code: 2002-6463; Inner value: 0x327e02 + public static Result.Base MultiCommitFileSystemAlreadyAdded => new Result.Base(ModuleFs, 6463); /// Error code: 2002-6465; Inner value: 0x328202 public static Result.Base UserNotExist => new Result.Base(ModuleFs, 6465); @@ -401,6 +410,8 @@ namespace LibHac.Fs public static Result.Base AllocationTableInsufficientFreeBlocks => new Result.Base(ModuleFs, 6707); /// Error code: 2002-6709; Inner value: 0x346a02 public static Result.Base OpenCountLimit => new Result.Base(ModuleFs, 6709); + /// Error code: 2002-6710; Inner value: 0x346c02 + public static Result.Base MultiCommitFileSystemLimit => new Result.Base(ModuleFs, 6710); /// Error code: 2002-6800; Range: 6800-6899; Inner value: 0x352002 public static Result.Base MappingFailed { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6800, 6899); } diff --git a/src/LibHac/Fs/SaveDataStructs.cs b/src/LibHac/Fs/SaveDataStructs.cs index a9e07435..f2c9b9a3 100644 --- a/src/LibHac/Fs/SaveDataStructs.cs +++ b/src/LibHac/Fs/SaveDataStructs.cs @@ -8,6 +8,7 @@ namespace LibHac.Fs [StructLayout(LayoutKind.Explicit, Size = 0x40)] public struct SaveDataAttribute : IEquatable, IComparable { + // todo: rename to ProgramId [FieldOffset(0x00)] public TitleId TitleId; [FieldOffset(0x08)] public UserId UserId; [FieldOffset(0x18)] public ulong SaveDataId; diff --git a/src/LibHac/FsService/FileSystemProxy.cs b/src/LibHac/FsService/FileSystemProxy.cs index 77f1ae68..64a688fc 100644 --- a/src/LibHac/FsService/FileSystemProxy.cs +++ b/src/LibHac/FsService/FileSystemProxy.cs @@ -2,6 +2,7 @@ using System.Runtime.CompilerServices; using LibHac.Common; using LibHac.Fs; +using LibHac.FsService.Impl; using LibHac.FsSystem; using LibHac.Kvdb; using LibHac.Ncm; @@ -12,7 +13,7 @@ namespace LibHac.FsService public class FileSystemProxy : IFileSystemProxy { private FileSystemProxyCore FsProxyCore { get; } - private FileSystemServer FsServer { get; } + internal FileSystemServer FsServer { get; } public long CurrentProcess { get; private set; } @@ -1175,6 +1176,22 @@ namespace LibHac.FsService return Result.Success; } + internal Result OpenMultiCommitContextSaveData(out IFileSystem fileSystem) + { + fileSystem = default; + + SaveDataAttribute attribute = default; + attribute.TitleId = new TitleId(MultiCommitManager.ProgramId); + attribute.SaveDataId = MultiCommitManager.SaveDataId; + + Result rc = OpenSaveDataFileSystemImpl(out IFileSystem saveFs, out _, SaveDataSpaceId.System, ref attribute, + false, true); + if (rc.IsFailure()) return rc; + + fileSystem = saveFs; + return Result.Success; + } + private static bool IsSystemSaveDataId(ulong id) { return (long)id < 0; diff --git a/src/LibHac/FsService/IMultiCommitManager.cs b/src/LibHac/FsService/IMultiCommitManager.cs new file mode 100644 index 00000000..029946d9 --- /dev/null +++ b/src/LibHac/FsService/IMultiCommitManager.cs @@ -0,0 +1,10 @@ +using LibHac.Fs; + +namespace LibHac.FsService +{ + public interface IMultiCommitManager + { + Result Add(IFileSystem fileSystem); + Result Commit(); + } +} diff --git a/src/LibHac/FsService/Impl/MultiCommitManager.cs b/src/LibHac/FsService/Impl/MultiCommitManager.cs new file mode 100644 index 00000000..db7bbf9c --- /dev/null +++ b/src/LibHac/FsService/Impl/MultiCommitManager.cs @@ -0,0 +1,262 @@ +using System.Collections.Generic; +using System.Runtime.InteropServices; +using LibHac.Common; +using LibHac.Fs; +using LibHac.Fs.Shim; + +namespace LibHac.FsService.Impl +{ + internal class MultiCommitManager : IMultiCommitManager + { + private const int MaxFileSystemCount = 10; + private const int CurrentContextVersion = 0x10000; + + public const ulong ProgramId = 0x100000000000000; + public const ulong SaveDataId = 0x8000000000000001; + private const long SaveDataSize = 0xC000; + private const long SaveJournalSize = 0xC000; + + private const long ContextFileSize = 0x200; + + // /commitinfo + private static U8Span ContextFileName => + new U8Span(new[] { (byte)'/', (byte)'c', (byte)'o', (byte)'m', (byte)'m', (byte)'i', (byte)'t', (byte)'i', (byte)'n', (byte)'f', (byte)'o' }); + + private static readonly object Locker = new object(); + + private FileSystemProxy FsProxy { get; } + private List FileSystems { get; } = new List(MaxFileSystemCount); + private long CommitCount { get; set; } + + public MultiCommitManager(FileSystemProxy fsProxy) + { + FsProxy = fsProxy; + } + + public Result Add(IFileSystem fileSystem) + { + if (FileSystems.Count >= MaxFileSystemCount) + return ResultFs.MultiCommitFileSystemLimit.Log(); + + // Check that the file system hasn't already been added + for (int i = 0; i < FileSystems.Count; i++) + { + if (ReferenceEquals(FileSystems[i], fileSystem)) + return ResultFs.MultiCommitFileSystemAlreadyAdded.Log(); + } + + FileSystems.Add(fileSystem); + return Result.Success; + } + + public Result Commit() + { + lock (Locker) + { + Result rc = CreateSave(); + if (rc.IsFailure()) return rc; + + rc = FsProxy.OpenMultiCommitContextSaveData(out IFileSystem contextFs); + if (rc.IsFailure()) return rc; + + return CommitImpl(contextFs); + } + } + + private Result CommitImpl(IFileSystem contextFileSystem) + { + var context = new CommitContextManager(contextFileSystem); + + try + { + CommitCount = 1; + + Result rc = context.Initialize(CommitCount, FileSystems.Count); + if (rc.IsFailure()) return rc; + + rc = CommitProvisionally(); + if (rc.IsFailure()) return rc; + + rc = context.SetCommittedProvisionally(); + if (rc.IsFailure()) return rc; + + foreach (IFileSystem fs in FileSystems) + { + rc = fs.Commit(); + if (rc.IsFailure()) return rc; + } + + rc = context.Close(); + if (rc.IsFailure()) return rc; + } + finally + { + context.Dispose(); + } + + return Result.Success; + } + + private Result CreateSave() + { + Result rc = FsProxy.OpenMultiCommitContextSaveData(out IFileSystem contextFs); + + if (rc.IsFailure()) + { + if (!ResultFs.TargetNotFound.Includes(rc)) + { + return rc; + } + + rc = FsProxy.FsServer.FsClient.CreateSystemSaveData(SaveDataId, SaveDataSize, SaveJournalSize, + SaveDataFlags.None); + if (rc.IsFailure()) return rc; + } + + contextFs?.Dispose(); + return Result.Success; + } + + private Result CommitProvisionally() + { + Result rc = Result.Success; + int i; + + for (i = 0; i < FileSystems.Count; i++) + { + rc = FileSystems[i].CommitProvisionally(CommitCount); + + if (rc.IsFailure()) + break; + } + + if (rc.IsFailure()) + { + // Rollback all provisional commits including the failed commit + for (int j = 0; j <= i; j++) + { + FileSystems[j].Rollback().IgnoreResult(); + } + } + + return rc; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x18)] + private struct CommitContext + { + public int Version; + public CommitState State; + public int FileSystemCount; + public long CommitCount; // I think? + } + + private enum CommitState + { + // ReSharper disable once UnusedMember.Local + None = 0, + NotCommitted = 1, + ProvisionallyCommitted = 2 + } + + private struct CommitContextManager + { + private IFileSystem _fileSystem; + private CommitContext _context; + + public CommitContextManager(IFileSystem contextFileSystem) + { + _fileSystem = contextFileSystem; + _context = default; + } + + public Result Initialize(long commitCount, int fileSystemCount) + { + IFile contextFile = null; + + try + { + // Open context file and create if it doesn't exist + Result rc = _fileSystem.OpenFile(out contextFile, ContextFileName, OpenMode.Read); + + if (rc.IsFailure()) + { + if (!ResultFs.PathNotFound.Includes(rc)) + return rc; + + rc = _fileSystem.CreateFile(ContextFileName, ContextFileSize, CreateFileOptions.None); + if (rc.IsFailure()) return rc; + + rc = _fileSystem.OpenFile(out contextFile, ContextFileName, OpenMode.Read); + if (rc.IsFailure()) return rc; + } + + _context.Version = CurrentContextVersion; + _context.State = CommitState.NotCommitted; + _context.FileSystemCount = fileSystemCount; + _context.CommitCount = commitCount; + + // Write the initial context to the file + rc = contextFile.Write(0, SpanHelpers.AsByteSpan(ref _context), WriteOption.None); + if (rc.IsFailure()) return rc; + + rc = contextFile.Flush(); + if (rc.IsFailure()) return rc; + } + finally + { + contextFile?.Dispose(); + } + + return _fileSystem.Commit(); + } + + public Result SetCommittedProvisionally() + { + IFile contextFile = null; + + try + { + Result rc = _fileSystem.OpenFile(out contextFile, ContextFileName, OpenMode.ReadWrite); + if (rc.IsFailure()) return rc; + + _context.State = CommitState.ProvisionallyCommitted; + + rc = contextFile.Write(0, SpanHelpers.AsByteSpan(ref _context), WriteOption.None); + if (rc.IsFailure()) return rc; + + rc = contextFile.Flush(); + if (rc.IsFailure()) return rc; + } + finally + { + contextFile?.Dispose(); + } + + return _fileSystem.Commit(); + } + + public Result Close() + { + Result rc = _fileSystem.DeleteFile(ContextFileName); + if (rc.IsFailure()) return rc; + + rc = _fileSystem.Commit(); + if (rc.IsFailure()) return rc; + + _fileSystem = null; + return Result.Success; + } + + public void Dispose() + { + if (_fileSystem is null) return; + + _fileSystem.DeleteFile(ContextFileName).IgnoreResult(); + _fileSystem.Commit().IgnoreResult(); + + _fileSystem = null; + } + } + } +} From 0ab96f67bf64d26a5b9a10f0a5e818c47a852630 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Sat, 21 Mar 2020 23:37:32 -0700 Subject: [PATCH 3/6] Add new functions to existing classes deriving from IFileSystem --- build/CodeGen/results.csv | 11 +++++++---- src/LibHac/Fs/ResultFs.cs | 14 ++++++++++---- src/LibHac/FsSystem/AesXtsFileSystem.cs | 10 ++++++++++ src/LibHac/FsSystem/ConcatenationFileSystem.cs | 10 ++++++++++ .../FsSystem/DirectorySaveDataFileSystem.cs | 17 +++++++++++++++++ src/LibHac/FsSystem/PartitionFileSystemCore.cs | 1 + src/LibHac/FsSystem/RomFs/RomFsFileSystem.cs | 1 + src/LibHac/FsSystem/SubdirectoryFileSystem.cs | 10 ++++++++++ 8 files changed, 66 insertions(+), 8 deletions(-) diff --git a/build/CodeGen/results.csv b/build/CodeGen/results.csv index b788bb4b..23cb7ec5 100644 --- a/build/CodeGen/results.csv +++ b/build/CodeGen/results.csv @@ -128,8 +128,8 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary 2,4781,,GameCardLogoDataCorrupted, 2,4791,4799,MultiCommitContextCorrupted, -2,4791,,InvalidMultiCommitContextVersion, -2,4792,,InvalidMultiCommitContextState, +2,4791,,InvalidMultiCommitContextVersion,The version of the multi-commit context file is to high for the current MultiCommitManager implementation. +2,4792,,InvalidMultiCommitContextState,The multi-commit has not been provisionally committed. # The range name is a guess. 4812 is currently the only result in it 2,4811,4819,ZeroBitmapFileCorrupted, @@ -186,14 +186,17 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary 2,6351,,UnsupportedOperationInRoGameCardStorageSetSize, 2,6359,,UnsupportedOperationInConcatFsQueryEntry, 2,6364,,UnsupportedOperationModifyRomFsFileSystem, +2,6365,,UnsupportedOperationInRomFsFileSystem,Called RomFsFileSystem::CommitProvisionally. 2,6366,,UnsupportedOperationRomFsFileSystemGetSpace, 2,6367,,UnsupportedOperationModifyRomFsFile, 2,6369,,UnsupportedOperationModifyReadOnlyFileSystem, 2,6371,,UnsupportedOperationReadOnlyFileSystemGetSpace, 2,6372,,UnsupportedOperationModifyReadOnlyFile, 2,6374,,UnsupportedOperationModifyPartitionFileSystem, +2,6375,,UnsupportedOperationInPartitionFileSystem,Called PartitionFileSystemCore::CommitProvisionally. 2,6376,,UnsupportedOperationInPartitionFileSetSize, 2,6377,,UnsupportedOperationIdInPartitionFileSystem, +2,6384,,UnsupportedOperationInDirectorySaveDataFileSystem,Called DirectorySaveDataFileSystem::CommitProvisionally on a non-user savedata. 2,6400,6449,PermissionDenied, @@ -201,7 +204,7 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary 2,6454,,WriteStateUnflushed, 2,6457,,WriteModeFileNotClosed, 2,6461,,AllocatorAlignmentViolation, -2,6463,,MultiCommitFileSystemAlreadyAdded, +2,6463,,MultiCommitFileSystemAlreadyAdded,The provided file system has already been added to the multi-commit manager. 2,6465,,UserNotExist, 2,6600,6699,EntryNotFound, @@ -211,7 +214,7 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary 2,6706,,MappingTableFull, 2,6707,,AllocationTableInsufficientFreeBlocks, 2,6709,,OpenCountLimit, -2,6710,,MultiCommitFileSystemLimit, +2,6710,,MultiCommitFileSystemLimit,The maximum number of file systems have been added to the multi-commit manager. 2,6800,6899,MappingFailed, 2,6811,,RemapStorageMapFull, diff --git a/src/LibHac/Fs/ResultFs.cs b/src/LibHac/Fs/ResultFs.cs index 11003473..7840bb4c 100644 --- a/src/LibHac/Fs/ResultFs.cs +++ b/src/LibHac/Fs/ResultFs.cs @@ -258,9 +258,9 @@ namespace LibHac.Fs /// Error code: 2002-4791; Range: 4791-4799; Inner value: 0x256e02 public static Result.Base MultiCommitContextCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 4791, 4799); } - /// Error code: 2002-4791; Inner value: 0x256e02 + /// The version of the multi-commit context file is to high for the current MultiCommitManager implementation.
Error code: 2002-4791; Inner value: 0x256e02
public static Result.Base InvalidMultiCommitContextVersion => new Result.Base(ModuleFs, 4791); - /// Error code: 2002-4792; Inner value: 0x257002 + /// The multi-commit has not been provisionally committed.
Error code: 2002-4792; Inner value: 0x257002
public static Result.Base InvalidMultiCommitContextState => new Result.Base(ModuleFs, 4792); /// Error code: 2002-4811; Range: 4811-4819; Inner value: 0x259602 @@ -364,6 +364,8 @@ namespace LibHac.Fs public static Result.Base UnsupportedOperationInConcatFsQueryEntry => new Result.Base(ModuleFs, 6359); /// Error code: 2002-6364; Inner value: 0x31b802 public static Result.Base UnsupportedOperationModifyRomFsFileSystem => new Result.Base(ModuleFs, 6364); + /// Called RomFsFileSystem::CommitProvisionally.
Error code: 2002-6365; Inner value: 0x31ba02
+ public static Result.Base UnsupportedOperationInRomFsFileSystem => new Result.Base(ModuleFs, 6365); /// Error code: 2002-6366; Inner value: 0x31bc02 public static Result.Base UnsupportedOperationRomFsFileSystemGetSpace => new Result.Base(ModuleFs, 6366); /// Error code: 2002-6367; Inner value: 0x31be02 @@ -376,10 +378,14 @@ namespace LibHac.Fs public static Result.Base UnsupportedOperationModifyReadOnlyFile => new Result.Base(ModuleFs, 6372); /// Error code: 2002-6374; Inner value: 0x31cc02 public static Result.Base UnsupportedOperationModifyPartitionFileSystem => new Result.Base(ModuleFs, 6374); + /// Called PartitionFileSystemCore::CommitProvisionally.
Error code: 2002-6375; Inner value: 0x31ce02
+ public static Result.Base UnsupportedOperationInPartitionFileSystem => new Result.Base(ModuleFs, 6375); /// Error code: 2002-6376; Inner value: 0x31d002 public static Result.Base UnsupportedOperationInPartitionFileSetSize => new Result.Base(ModuleFs, 6376); /// Error code: 2002-6377; Inner value: 0x31d202 public static Result.Base UnsupportedOperationIdInPartitionFileSystem => new Result.Base(ModuleFs, 6377); + /// Called DirectorySaveDataFileSystem::CommitProvisionally on a non-user savedata.
Error code: 2002-6384; Inner value: 0x31e002
+ public static Result.Base UnsupportedOperationInDirectorySaveDataFileSystem => new Result.Base(ModuleFs, 6384); /// Error code: 2002-6400; Range: 6400-6449; Inner value: 0x320002 public static Result.Base PermissionDenied { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6400, 6449); } @@ -392,7 +398,7 @@ namespace LibHac.Fs public static Result.Base WriteModeFileNotClosed => new Result.Base(ModuleFs, 6457); /// Error code: 2002-6461; Inner value: 0x327a02 public static Result.Base AllocatorAlignmentViolation => new Result.Base(ModuleFs, 6461); - /// Error code: 2002-6463; Inner value: 0x327e02 + /// The provided file system has already been added to the multi-commit manager.
Error code: 2002-6463; Inner value: 0x327e02
public static Result.Base MultiCommitFileSystemAlreadyAdded => new Result.Base(ModuleFs, 6463); /// Error code: 2002-6465; Inner value: 0x328202 public static Result.Base UserNotExist => new Result.Base(ModuleFs, 6465); @@ -410,7 +416,7 @@ namespace LibHac.Fs public static Result.Base AllocationTableInsufficientFreeBlocks => new Result.Base(ModuleFs, 6707); /// Error code: 2002-6709; Inner value: 0x346a02 public static Result.Base OpenCountLimit => new Result.Base(ModuleFs, 6709); - /// Error code: 2002-6710; Inner value: 0x346c02 + /// The maximum number of file systems have been added to the multi-commit manager.
Error code: 2002-6710; Inner value: 0x346c02
public static Result.Base MultiCommitFileSystemLimit => new Result.Base(ModuleFs, 6710); /// Error code: 2002-6800; Range: 6800-6899; Inner value: 0x352002 diff --git a/src/LibHac/FsSystem/AesXtsFileSystem.cs b/src/LibHac/FsSystem/AesXtsFileSystem.cs index 2e6abcf0..7335f93f 100644 --- a/src/LibHac/FsSystem/AesXtsFileSystem.cs +++ b/src/LibHac/FsSystem/AesXtsFileSystem.cs @@ -221,6 +221,16 @@ namespace LibHac.FsSystem return BaseFileSystem.Commit(); } + protected override Result CommitProvisionallyImpl(long commitCount) + { + return BaseFileSystem.CommitProvisionally(commitCount); + } + + protected override Result RollbackImpl() + { + return BaseFileSystem.Rollback(); + } + protected override Result QueryEntryImpl(Span outBuffer, ReadOnlySpan inBuffer, QueryId queryId, U8Span path) { diff --git a/src/LibHac/FsSystem/ConcatenationFileSystem.cs b/src/LibHac/FsSystem/ConcatenationFileSystem.cs index a572a2ec..38b9dca4 100644 --- a/src/LibHac/FsSystem/ConcatenationFileSystem.cs +++ b/src/LibHac/FsSystem/ConcatenationFileSystem.cs @@ -306,6 +306,16 @@ namespace LibHac.FsSystem return BaseFileSystem.Commit(); } + protected override Result CommitProvisionallyImpl(long commitCount) + { + return BaseFileSystem.CommitProvisionally(commitCount); + } + + protected override Result FlushImpl() + { + return BaseFileSystem.Flush(); + } + protected override Result QueryEntryImpl(Span outBuffer, ReadOnlySpan inBuffer, QueryId queryId, U8Span path) { diff --git a/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs b/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs index e775bc81..1075b7e2 100644 --- a/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs +++ b/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs @@ -299,6 +299,23 @@ namespace LibHac.FsSystem } } + protected override Result CommitProvisionallyImpl(long commitCount) + { + if (!IsUserSaveData) + return ResultFs.UnsupportedOperationIdInPartitionFileSystem.Log(); + + return Result.Success; + } + + protected override Result RollbackImpl() + { + // No old data is kept for temporary save data, so there's nothing to rollback to + if (!IsPersistentSaveData) + return Result.Success; + + return Initialize(IsPersistentSaveData, IsUserSaveData); + } + private Result ResolveFullPath(Span outPath, U8Span relativePath) { if (StringUtils.GetLength(relativePath, PathTools.MaxPathLength + 1) > PathTools.MaxPathLength) diff --git a/src/LibHac/FsSystem/PartitionFileSystemCore.cs b/src/LibHac/FsSystem/PartitionFileSystemCore.cs index 81a84b6b..f6bb15bb 100644 --- a/src/LibHac/FsSystem/PartitionFileSystemCore.cs +++ b/src/LibHac/FsSystem/PartitionFileSystemCore.cs @@ -108,6 +108,7 @@ namespace LibHac.FsSystem protected override Result DeleteFileImpl(U8Span path) => ResultFs.UnsupportedOperationModifyPartitionFileSystem.Log(); protected override Result RenameDirectoryImpl(U8Span oldPath, U8Span newPath) => ResultFs.UnsupportedOperationModifyPartitionFileSystem.Log(); protected override Result RenameFileImpl(U8Span oldPath, U8Span newPath) => ResultFs.UnsupportedOperationModifyPartitionFileSystem.Log(); + protected override Result CommitProvisionallyImpl(long commitCount) => ResultFs.UnsupportedOperationInPartitionFileSystem.Log(); private class PartitionFile : FileBase { diff --git a/src/LibHac/FsSystem/RomFs/RomFsFileSystem.cs b/src/LibHac/FsSystem/RomFs/RomFsFileSystem.cs index 764a60b5..fdc7399e 100644 --- a/src/LibHac/FsSystem/RomFs/RomFsFileSystem.cs +++ b/src/LibHac/FsSystem/RomFs/RomFsFileSystem.cs @@ -92,6 +92,7 @@ namespace LibHac.FsSystem.RomFs protected override Result DeleteFileImpl(U8Span path) => ResultFs.UnsupportedOperationModifyRomFsFileSystem.Log(); protected override Result RenameDirectoryImpl(U8Span oldPath, U8Span newPath) => ResultFs.UnsupportedOperationModifyRomFsFileSystem.Log(); protected override Result RenameFileImpl(U8Span oldPath, U8Span newPath) => ResultFs.UnsupportedOperationModifyRomFsFileSystem.Log(); + protected override Result CommitProvisionallyImpl(long commitCount) => ResultFs.UnsupportedOperationInRomFsFileSystem.Log(); protected override Result GetFreeSpaceSizeImpl(out long freeSpace, U8Span path) { diff --git a/src/LibHac/FsSystem/SubdirectoryFileSystem.cs b/src/LibHac/FsSystem/SubdirectoryFileSystem.cs index f32c92e2..d04e7071 100644 --- a/src/LibHac/FsSystem/SubdirectoryFileSystem.cs +++ b/src/LibHac/FsSystem/SubdirectoryFileSystem.cs @@ -194,6 +194,16 @@ namespace LibHac.FsSystem return BaseFileSystem.Commit(); } + protected override Result CommitProvisionallyImpl(long commitCount) + { + return BaseFileSystem.CommitProvisionally(commitCount); + } + + protected override Result RollbackImpl() + { + return BaseFileSystem.Rollback(); + } + protected override Result GetFreeSpaceSizeImpl(out long freeSpace, U8Span path) { freeSpace = default; From 8ead72ed84309ec08fbafd9d8402a5592ea56fe2 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Sat, 21 Mar 2020 23:55:23 -0700 Subject: [PATCH 4/6] Add OpenMultiCommitManager to IFileSystemProxy --- src/LibHac/FsService/FileSystemProxy.cs | 6 ++++++ src/LibHac/FsService/IFileSystemProxy.cs | 1 + src/LibHac/FsService/Impl/MultiCommitManager.cs | 10 +++++----- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/LibHac/FsService/FileSystemProxy.cs b/src/LibHac/FsService/FileSystemProxy.cs index 64a688fc..1725da97 100644 --- a/src/LibHac/FsService/FileSystemProxy.cs +++ b/src/LibHac/FsService/FileSystemProxy.cs @@ -1176,6 +1176,12 @@ namespace LibHac.FsService return Result.Success; } + public Result OpenMultiCommitManager(out IMultiCommitManager commitManager) + { + commitManager = new MultiCommitManager(this); + return Result.Success; + } + internal Result OpenMultiCommitContextSaveData(out IFileSystem fileSystem) { fileSystem = default; diff --git a/src/LibHac/FsService/IFileSystemProxy.cs b/src/LibHac/FsService/IFileSystemProxy.cs index e4b1c9ce..15711c93 100644 --- a/src/LibHac/FsService/IFileSystemProxy.cs +++ b/src/LibHac/FsService/IFileSystemProxy.cs @@ -100,5 +100,6 @@ namespace LibHac.FsService Result GetProgramIndexForAccessLog(out int programIndex, out int programCount); Result OverrideSaveDataTransferTokenSignVerificationKey(ReadOnlySpan key); Result CorruptSaveDataFileSystemByOffset(SaveDataSpaceId spaceId, ulong saveDataId, long offset); + Result OpenMultiCommitManager(out IMultiCommitManager commitManager); } } \ No newline at end of file diff --git a/src/LibHac/FsService/Impl/MultiCommitManager.cs b/src/LibHac/FsService/Impl/MultiCommitManager.cs index db7bbf9c..66361e15 100644 --- a/src/LibHac/FsService/Impl/MultiCommitManager.cs +++ b/src/LibHac/FsService/Impl/MultiCommitManager.cs @@ -142,13 +142,13 @@ namespace LibHac.FsService.Impl return rc; } - [StructLayout(LayoutKind.Sequential, Size = 0x18)] + [StructLayout(LayoutKind.Explicit, Size = 0x18)] private struct CommitContext { - public int Version; - public CommitState State; - public int FileSystemCount; - public long CommitCount; // I think? + [FieldOffset(0x00)] public int Version; + [FieldOffset(0x04)] public CommitState State; + [FieldOffset(0x08)] public int FileSystemCount; + [FieldOffset(0x10)] public long CommitCount; // I think? } private enum CommitState From 95ef1c9529c0e1f9a1554be2a61e76beb51aedbd Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Sun, 22 Mar 2020 17:52:27 -0700 Subject: [PATCH 5/6] Don't write to a read-only file --- src/LibHac/FsService/Impl/MultiCommitManager.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/LibHac/FsService/Impl/MultiCommitManager.cs b/src/LibHac/FsService/Impl/MultiCommitManager.cs index 66361e15..e8a0a31b 100644 --- a/src/LibHac/FsService/Impl/MultiCommitManager.cs +++ b/src/LibHac/FsService/Impl/MultiCommitManager.cs @@ -190,6 +190,16 @@ namespace LibHac.FsService.Impl rc = _fileSystem.OpenFile(out contextFile, ContextFileName, OpenMode.Read); if (rc.IsFailure()) return rc; } + } + finally + { + contextFile?.Dispose(); + } + + try + { + Result rc = _fileSystem.OpenFile(out contextFile, ContextFileName, OpenMode.ReadWrite); + if (rc.IsFailure()) return rc; _context.Version = CurrentContextVersion; _context.State = CommitState.NotCommitted; From f44b09ec1f5ccb33b0cdf05636a2d8d90c029d27 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Mon, 23 Mar 2020 09:10:55 -0700 Subject: [PATCH 6/6] Add some Results --- build/CodeGen/results.csv | 6 +++++- src/LibHac/Fs/ResultFs.cs | 10 +++++++++- src/LibHac/FsService/SaveDataIndexer.cs | 2 +- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/build/CodeGen/results.csv b/build/CodeGen/results.csv index 23cb7ec5..6e78b285 100644 --- a/build/CodeGen/results.csv +++ b/build/CodeGen/results.csv @@ -159,10 +159,14 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary 2,6061,,InvalidOffset, 2,6062,,InvalidSize, 2,6063,,NullptrArgument, +2,6064,,InvalidAlignment, 2,6065,,InvalidMountName, 2,6066,,ExtensionSizeTooLarge, 2,6067,,ExtensionSizeInvalid, -2,6068,,ReadOldSaveDataInfoReader, +2,6068,,InvalidSaveDataInfoReader, +2,6069,,InvalidCacheStorageSize, +2,6070,,InvalidCacheStorageIndex, +2,6071,,InvalidCommitNameCount,Up to 10 file systems can be committed at the same time. 2,6080,6099,InvalidEnumValue, 2,6081,,InvalidSaveDataState, diff --git a/src/LibHac/Fs/ResultFs.cs b/src/LibHac/Fs/ResultFs.cs index 7840bb4c..7725caf3 100644 --- a/src/LibHac/Fs/ResultFs.cs +++ b/src/LibHac/Fs/ResultFs.cs @@ -313,6 +313,8 @@ namespace LibHac.Fs public static Result.Base InvalidSize => new Result.Base(ModuleFs, 6062); /// Error code: 2002-6063; Inner value: 0x2f5e02 public static Result.Base NullptrArgument => new Result.Base(ModuleFs, 6063); + /// Error code: 2002-6064; Inner value: 0x2f6002 + public static Result.Base InvalidAlignment => new Result.Base(ModuleFs, 6064); /// Error code: 2002-6065; Inner value: 0x2f6202 public static Result.Base InvalidMountName => new Result.Base(ModuleFs, 6065); /// Error code: 2002-6066; Inner value: 0x2f6402 @@ -320,7 +322,13 @@ namespace LibHac.Fs /// Error code: 2002-6067; Inner value: 0x2f6602 public static Result.Base ExtensionSizeInvalid => new Result.Base(ModuleFs, 6067); /// Error code: 2002-6068; Inner value: 0x2f6802 - public static Result.Base ReadOldSaveDataInfoReader => new Result.Base(ModuleFs, 6068); + public static Result.Base InvalidSaveDataInfoReader => new Result.Base(ModuleFs, 6068); + /// Error code: 2002-6069; Inner value: 0x2f6a02 + public static Result.Base InvalidCacheStorageSize => new Result.Base(ModuleFs, 6069); + /// Error code: 2002-6070; Inner value: 0x2f6c02 + public static Result.Base InvalidCacheStorageIndex => new Result.Base(ModuleFs, 6070); + /// Up to 10 file systems can be committed at the same time.
Error code: 2002-6071; Inner value: 0x2f6e02
+ public static Result.Base InvalidCommitNameCount => new Result.Base(ModuleFs, 6071); /// Error code: 2002-6080; Range: 6080-6099; Inner value: 0x2f8002 public static Result.Base InvalidEnumValue { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6080, 6099); } diff --git a/src/LibHac/FsService/SaveDataIndexer.cs b/src/LibHac/FsService/SaveDataIndexer.cs index 42ac4f8b..3d8759b5 100644 --- a/src/LibHac/FsService/SaveDataIndexer.cs +++ b/src/LibHac/FsService/SaveDataIndexer.cs @@ -648,7 +648,7 @@ namespace LibHac.FsService // Indexer has been reloaded since this info reader was created if (Version != Indexer.Version) { - return ResultFs.ReadOldSaveDataInfoReader.Log(); + return ResultFs.InvalidSaveDataInfoReader.Log(); } // No more to iterate