mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Merge pull request #122 from Thealexbarney/multi-commit
Add IMultiCommitManager to FS server. Recovering an interrupted commit is currently not implemented.
This commit is contained in:
commit
1f4c904b04
19 changed files with 457 additions and 13 deletions
|
@ -127,6 +127,10 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
|
|||
2,4771,4779,SignedSystemPartitionDataCorrupted,
|
||||
2,4781,,GameCardLogoDataCorrupted,
|
||||
|
||||
2,4791,4799,MultiCommitContextCorrupted,
|
||||
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,
|
||||
2,4812,,IncompleteBlockInZeroBitmapHashStorageFile,
|
||||
|
@ -155,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,
|
||||
|
@ -182,14 +190,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,
|
||||
|
||||
|
@ -197,14 +208,17 @@ Module,DescriptionStart,DescriptionEnd,Name,Summary
|
|||
2,6454,,WriteStateUnflushed,
|
||||
2,6457,,WriteModeFileNotClosed,
|
||||
2,6461,,AllocatorAlignmentViolation,
|
||||
2,6463,,MultiCommitFileSystemAlreadyAdded,The provided file system has already been added to the multi-commit manager.
|
||||
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,The maximum number of file systems have been added to the multi-commit manager.
|
||||
|
||||
2,6800,6899,MappingFailed,
|
||||
2,6811,,RemapStorageMapFull,
|
||||
|
|
|
|
@ -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<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId, U8Span path)
|
||||
{
|
||||
if (IsDisposed) return ResultFs.PreconditionViolation.Log();
|
||||
|
|
|
@ -183,6 +183,12 @@ namespace LibHac.Fs
|
|||
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
|
||||
Result Commit();
|
||||
|
||||
Result CommitProvisionally(long commitCount);
|
||||
|
||||
Result Rollback();
|
||||
|
||||
Result Flush();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the creation, last accessed, and last modified timestamps of a file or directory.
|
||||
/// </summary>
|
||||
|
|
|
@ -256,6 +256,13 @@ namespace LibHac.Fs
|
|||
/// <summary>Error code: 2002-4781; Inner value: 0x255a02</summary>
|
||||
public static Result.Base GameCardLogoDataCorrupted => new Result.Base(ModuleFs, 4781);
|
||||
|
||||
/// <summary>Error code: 2002-4791; Range: 4791-4799; Inner value: 0x256e02</summary>
|
||||
public static Result.Base MultiCommitContextCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 4791, 4799); }
|
||||
/// <summary>The version of the multi-commit context file is to high for the current MultiCommitManager implementation.<br/>Error code: 2002-4791; Inner value: 0x256e02</summary>
|
||||
public static Result.Base InvalidMultiCommitContextVersion => new Result.Base(ModuleFs, 4791);
|
||||
/// <summary>The multi-commit has not been provisionally committed.<br/>Error code: 2002-4792; Inner value: 0x257002</summary>
|
||||
public static Result.Base InvalidMultiCommitContextState => new Result.Base(ModuleFs, 4792);
|
||||
|
||||
/// <summary>Error code: 2002-4811; Range: 4811-4819; Inner value: 0x259602</summary>
|
||||
public static Result.Base ZeroBitmapFileCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 4811, 4819); }
|
||||
/// <summary>Error code: 2002-4812; Inner value: 0x259802</summary>
|
||||
|
@ -306,6 +313,8 @@ namespace LibHac.Fs
|
|||
public static Result.Base InvalidSize => new Result.Base(ModuleFs, 6062);
|
||||
/// <summary>Error code: 2002-6063; Inner value: 0x2f5e02</summary>
|
||||
public static Result.Base NullptrArgument => new Result.Base(ModuleFs, 6063);
|
||||
/// <summary>Error code: 2002-6064; Inner value: 0x2f6002</summary>
|
||||
public static Result.Base InvalidAlignment => new Result.Base(ModuleFs, 6064);
|
||||
/// <summary>Error code: 2002-6065; Inner value: 0x2f6202</summary>
|
||||
public static Result.Base InvalidMountName => new Result.Base(ModuleFs, 6065);
|
||||
/// <summary>Error code: 2002-6066; Inner value: 0x2f6402</summary>
|
||||
|
@ -313,7 +322,13 @@ namespace LibHac.Fs
|
|||
/// <summary>Error code: 2002-6067; Inner value: 0x2f6602</summary>
|
||||
public static Result.Base ExtensionSizeInvalid => new Result.Base(ModuleFs, 6067);
|
||||
/// <summary>Error code: 2002-6068; Inner value: 0x2f6802</summary>
|
||||
public static Result.Base ReadOldSaveDataInfoReader => new Result.Base(ModuleFs, 6068);
|
||||
public static Result.Base InvalidSaveDataInfoReader => new Result.Base(ModuleFs, 6068);
|
||||
/// <summary>Error code: 2002-6069; Inner value: 0x2f6a02</summary>
|
||||
public static Result.Base InvalidCacheStorageSize => new Result.Base(ModuleFs, 6069);
|
||||
/// <summary>Error code: 2002-6070; Inner value: 0x2f6c02</summary>
|
||||
public static Result.Base InvalidCacheStorageIndex => new Result.Base(ModuleFs, 6070);
|
||||
/// <summary>Up to 10 file systems can be committed at the same time.<br/>Error code: 2002-6071; Inner value: 0x2f6e02</summary>
|
||||
public static Result.Base InvalidCommitNameCount => new Result.Base(ModuleFs, 6071);
|
||||
|
||||
/// <summary>Error code: 2002-6080; Range: 6080-6099; Inner value: 0x2f8002</summary>
|
||||
public static Result.Base InvalidEnumValue { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6080, 6099); }
|
||||
|
@ -357,6 +372,8 @@ namespace LibHac.Fs
|
|||
public static Result.Base UnsupportedOperationInConcatFsQueryEntry => new Result.Base(ModuleFs, 6359);
|
||||
/// <summary>Error code: 2002-6364; Inner value: 0x31b802</summary>
|
||||
public static Result.Base UnsupportedOperationModifyRomFsFileSystem => new Result.Base(ModuleFs, 6364);
|
||||
/// <summary>Called RomFsFileSystem::CommitProvisionally.<br/>Error code: 2002-6365; Inner value: 0x31ba02</summary>
|
||||
public static Result.Base UnsupportedOperationInRomFsFileSystem => new Result.Base(ModuleFs, 6365);
|
||||
/// <summary>Error code: 2002-6366; Inner value: 0x31bc02</summary>
|
||||
public static Result.Base UnsupportedOperationRomFsFileSystemGetSpace => new Result.Base(ModuleFs, 6366);
|
||||
/// <summary>Error code: 2002-6367; Inner value: 0x31be02</summary>
|
||||
|
@ -369,10 +386,14 @@ namespace LibHac.Fs
|
|||
public static Result.Base UnsupportedOperationModifyReadOnlyFile => new Result.Base(ModuleFs, 6372);
|
||||
/// <summary>Error code: 2002-6374; Inner value: 0x31cc02</summary>
|
||||
public static Result.Base UnsupportedOperationModifyPartitionFileSystem => new Result.Base(ModuleFs, 6374);
|
||||
/// <summary>Called PartitionFileSystemCore::CommitProvisionally.<br/>Error code: 2002-6375; Inner value: 0x31ce02</summary>
|
||||
public static Result.Base UnsupportedOperationInPartitionFileSystem => new Result.Base(ModuleFs, 6375);
|
||||
/// <summary>Error code: 2002-6376; Inner value: 0x31d002</summary>
|
||||
public static Result.Base UnsupportedOperationInPartitionFileSetSize => new Result.Base(ModuleFs, 6376);
|
||||
/// <summary>Error code: 2002-6377; Inner value: 0x31d202</summary>
|
||||
public static Result.Base UnsupportedOperationIdInPartitionFileSystem => new Result.Base(ModuleFs, 6377);
|
||||
/// <summary>Called DirectorySaveDataFileSystem::CommitProvisionally on a non-user savedata.<br/>Error code: 2002-6384; Inner value: 0x31e002</summary>
|
||||
public static Result.Base UnsupportedOperationInDirectorySaveDataFileSystem => new Result.Base(ModuleFs, 6384);
|
||||
|
||||
/// <summary>Error code: 2002-6400; Range: 6400-6449; Inner value: 0x320002</summary>
|
||||
public static Result.Base PermissionDenied { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6400, 6449); }
|
||||
|
@ -385,6 +406,8 @@ namespace LibHac.Fs
|
|||
public static Result.Base WriteModeFileNotClosed => new Result.Base(ModuleFs, 6457);
|
||||
/// <summary>Error code: 2002-6461; Inner value: 0x327a02</summary>
|
||||
public static Result.Base AllocatorAlignmentViolation => new Result.Base(ModuleFs, 6461);
|
||||
/// <summary>The provided file system has already been added to the multi-commit manager.<br/>Error code: 2002-6463; Inner value: 0x327e02</summary>
|
||||
public static Result.Base MultiCommitFileSystemAlreadyAdded => new Result.Base(ModuleFs, 6463);
|
||||
/// <summary>Error code: 2002-6465; Inner value: 0x328202</summary>
|
||||
public static Result.Base UserNotExist => new Result.Base(ModuleFs, 6465);
|
||||
|
||||
|
@ -401,6 +424,8 @@ namespace LibHac.Fs
|
|||
public static Result.Base AllocationTableInsufficientFreeBlocks => new Result.Base(ModuleFs, 6707);
|
||||
/// <summary>Error code: 2002-6709; Inner value: 0x346a02</summary>
|
||||
public static Result.Base OpenCountLimit => new Result.Base(ModuleFs, 6709);
|
||||
/// <summary>The maximum number of file systems have been added to the multi-commit manager.<br/>Error code: 2002-6710; Inner value: 0x346c02</summary>
|
||||
public static Result.Base MultiCommitFileSystemLimit => new Result.Base(ModuleFs, 6710);
|
||||
|
||||
/// <summary>Error code: 2002-6800; Range: 6800-6899; Inner value: 0x352002</summary>
|
||||
public static Result.Base MappingFailed { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6800, 6899); }
|
||||
|
|
|
@ -8,6 +8,7 @@ namespace LibHac.Fs
|
|||
[StructLayout(LayoutKind.Explicit, Size = 0x40)]
|
||||
public struct SaveDataAttribute : IEquatable<SaveDataAttribute>, IComparable<SaveDataAttribute>
|
||||
{
|
||||
// todo: rename to ProgramId
|
||||
[FieldOffset(0x00)] public TitleId TitleId;
|
||||
[FieldOffset(0x08)] public UserId UserId;
|
||||
[FieldOffset(0x18)] public ulong SaveDataId;
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
@ -160,10 +161,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 +407,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 +415,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 +1152,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;
|
||||
}
|
||||
|
@ -1175,6 +1176,28 @@ 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;
|
||||
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -42,7 +42,7 @@ namespace LibHac.FsService
|
|||
|
||||
SaveDataIndexerManager = new SaveDataIndexerManager(FsClient, SaveIndexerId);
|
||||
|
||||
fsProxy.CleanUpTemporaryStorage();
|
||||
fsProxy.CleanUpTemporaryStorage().IgnoreResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -100,5 +100,6 @@ namespace LibHac.FsService
|
|||
Result GetProgramIndexForAccessLog(out int programIndex, out int programCount);
|
||||
Result OverrideSaveDataTransferTokenSignVerificationKey(ReadOnlySpan<byte> key);
|
||||
Result CorruptSaveDataFileSystemByOffset(SaveDataSpaceId spaceId, ulong saveDataId, long offset);
|
||||
Result OpenMultiCommitManager(out IMultiCommitManager commitManager);
|
||||
}
|
||||
}
|
10
src/LibHac/FsService/IMultiCommitManager.cs
Normal file
10
src/LibHac/FsService/IMultiCommitManager.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using LibHac.Fs;
|
||||
|
||||
namespace LibHac.FsService
|
||||
{
|
||||
public interface IMultiCommitManager
|
||||
{
|
||||
Result Add(IFileSystem fileSystem);
|
||||
Result Commit();
|
||||
}
|
||||
}
|
272
src/LibHac/FsService/Impl/MultiCommitManager.cs
Normal file
272
src/LibHac/FsService/Impl/MultiCommitManager.cs
Normal file
|
@ -0,0 +1,272 @@
|
|||
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<IFileSystem> FileSystems { get; } = new List<IFileSystem>(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.Explicit, Size = 0x18)]
|
||||
private struct CommitContext
|
||||
{
|
||||
[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
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
_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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId,
|
||||
U8Span path)
|
||||
{
|
||||
|
|
|
@ -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<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId,
|
||||
U8Span path)
|
||||
{
|
||||
|
|
|
@ -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<byte> outPath, U8Span relativePath)
|
||||
{
|
||||
if (StringUtils.GetLength(relativePath, PathTools.MaxPathLength + 1) > PathTools.MaxPathLength)
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -70,6 +70,11 @@ namespace LibHac
|
|||
public bool IsSuccess() => _value == SuccessValue;
|
||||
public bool IsFailure() => !IsSuccess();
|
||||
|
||||
/// <summary>
|
||||
/// Specifies that the <see cref="Result"/> from a returned function is explicitly ignored.
|
||||
/// </summary>
|
||||
public void IgnoreResult() { }
|
||||
|
||||
public void ThrowIfFailure()
|
||||
{
|
||||
if (IsFailure())
|
||||
|
@ -212,6 +217,7 @@ namespace LibHac
|
|||
/// <c>new Result.Base(ModuleFs, 2000, 2499)</c>. The property will need to have the aggressive inlining flag set like so:
|
||||
/// <c>public static Result.Base SdCardAccessFailed { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 2000, 2499); }</c>
|
||||
/// </remarks>
|
||||
[DebuggerDisplay("{" + nameof(ToStringWithName) + "(),nq}")]
|
||||
public struct Base
|
||||
{
|
||||
private const int DescriptionEndBitsOffset = ReservedBitsOffset;
|
||||
|
|
Loading…
Reference in a new issue