mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Update MultiCommitManager
This commit is contained in:
parent
4934b1cbef
commit
4ceb925cde
4 changed files with 218 additions and 143 deletions
|
@ -31,6 +31,7 @@ namespace LibHac.FsSrv
|
||||||
public AccessControlGlobals AccessControl;
|
public AccessControlGlobals AccessControl;
|
||||||
public StorageDeviceManagerFactoryGlobals StorageDeviceManagerFactory;
|
public StorageDeviceManagerFactoryGlobals StorageDeviceManagerFactory;
|
||||||
public SaveDataSharedFileStorageGlobals SaveDataSharedFileStorage;
|
public SaveDataSharedFileStorageGlobals SaveDataSharedFileStorage;
|
||||||
|
public MultiCommitManagerGlobals MultiCommitManager;
|
||||||
|
|
||||||
public void Initialize(HorizonClient horizonClient, FileSystemServer fsServer)
|
public void Initialize(HorizonClient horizonClient, FileSystemServer fsServer)
|
||||||
{
|
{
|
||||||
|
@ -38,6 +39,7 @@ namespace LibHac.FsSrv
|
||||||
InitMutex = new object();
|
InitMutex = new object();
|
||||||
|
|
||||||
SaveDataSharedFileStorage.Initialize(fsServer);
|
SaveDataSharedFileStorage.Initialize(fsServer);
|
||||||
|
MultiCommitManager.Initialize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,49 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
|
using LibHac.Diag;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
|
||||||
using LibHac.Fs.Shim;
|
using LibHac.Fs.Shim;
|
||||||
using LibHac.FsSrv.Sf;
|
using LibHac.FsSrv.Sf;
|
||||||
|
using LibHac.Os;
|
||||||
using LibHac.Sf;
|
using LibHac.Sf;
|
||||||
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
|
|
||||||
using IFile = LibHac.Fs.Fsa.IFile;
|
using IFile = LibHac.Fs.Fsa.IFile;
|
||||||
|
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
|
||||||
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
|
||||||
|
|
||||||
namespace LibHac.FsSrv.Impl
|
namespace LibHac.FsSrv.Impl
|
||||||
{
|
{
|
||||||
|
internal struct MultiCommitManagerGlobals
|
||||||
|
{
|
||||||
|
public SdkMutexType MultiCommitMutex;
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
MultiCommitMutex.Initialize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Manages atomically committing a group of file systems.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The commit process is as follows:<br/>
|
||||||
|
/// 1. Create a commit context file that tracks the progress of the commit in case it is interrupted.<br/>
|
||||||
|
/// 2. Provisionally commit each file system individually. If any fail, rollback the file systems that were provisionally committed.<br/>
|
||||||
|
/// 3. Update the commit context file to note that the file systems have been provisionally committed.
|
||||||
|
/// If the multi-commit is interrupted past this point, the file systems will be fully committed during recovery.<br/>
|
||||||
|
/// 4. Fully commit each file system individually.<br/>
|
||||||
|
/// 5. Delete the commit context file.<br/>
|
||||||
|
///<br/>
|
||||||
|
/// Even though multi-commits are supposed to be atomic, issues can arise from errors during the process of fully committing the save data.
|
||||||
|
/// Save data image files are designed so that minimal changes are made when fully committing a provisionally committed save.
|
||||||
|
/// However if any commit fails for any reason, all other saves in the multi-commit will still be committed.
|
||||||
|
/// This can especially cause issues with directory save data where finishing a commit is much more involved.<br/>
|
||||||
|
/// <br/>
|
||||||
|
/// Based on FS 12.0.3 (nnSdk 12.3.1)
|
||||||
|
/// </remarks>
|
||||||
internal class MultiCommitManager : IMultiCommitManager
|
internal class MultiCommitManager : IMultiCommitManager
|
||||||
{
|
{
|
||||||
private const int MaxFileSystemCount = 10;
|
private const int MaxFileSystemCount = 10;
|
||||||
|
@ -27,42 +57,43 @@ namespace LibHac.FsSrv.Impl
|
||||||
private const long CommitContextFileSize = 0x200;
|
private const long CommitContextFileSize = 0x200;
|
||||||
|
|
||||||
// /commitinfo
|
// /commitinfo
|
||||||
private static U8Span CommitContextFileName =>
|
private static ReadOnlySpan<byte> CommitContextFileName =>
|
||||||
new U8Span(new[] { (byte)'/', (byte)'c', (byte)'o', (byte)'m', (byte)'m', (byte)'i', (byte)'t', (byte)'i', (byte)'n', (byte)'f', (byte)'o' });
|
new[] { (byte)'/', (byte)'c', (byte)'o', (byte)'m', (byte)'m', (byte)'i', (byte)'t', (byte)'i', (byte)'n', (byte)'f', (byte)'o' };
|
||||||
|
|
||||||
// Todo: Don't use global lock object
|
private ReferenceCountedDisposable<ISaveDataMultiCommitCoreInterface> _multiCommitInterface;
|
||||||
private static readonly object Locker = new object();
|
private readonly ReferenceCountedDisposable<IFileSystem>[] _fileSystems;
|
||||||
|
private int _fileSystemCount;
|
||||||
|
private long _counter;
|
||||||
|
|
||||||
private ReferenceCountedDisposable<ISaveDataMultiCommitCoreInterface> MultiCommitInterface { get; }
|
// Extra field used in LibHac
|
||||||
|
private readonly FileSystemServer _fsServer;
|
||||||
|
private ref MultiCommitManagerGlobals Globals => ref _fsServer.Globals.MultiCommitManager;
|
||||||
|
|
||||||
private List<ReferenceCountedDisposable<IFileSystem>> FileSystems { get; } =
|
public MultiCommitManager(FileSystemServer fsServer, ref ReferenceCountedDisposable<ISaveDataMultiCommitCoreInterface> multiCommitInterface)
|
||||||
new List<ReferenceCountedDisposable<IFileSystem>>(MaxFileSystemCount);
|
|
||||||
|
|
||||||
private long Counter { get; set; }
|
|
||||||
private HorizonClient Hos { get; }
|
|
||||||
|
|
||||||
public MultiCommitManager(
|
|
||||||
ref ReferenceCountedDisposable<ISaveDataMultiCommitCoreInterface> multiCommitInterface,
|
|
||||||
HorizonClient client)
|
|
||||||
{
|
{
|
||||||
Hos = client;
|
_fsServer = fsServer;
|
||||||
MultiCommitInterface = Shared.Move(ref multiCommitInterface);
|
|
||||||
|
_multiCommitInterface = Shared.Move(ref multiCommitInterface);
|
||||||
|
_fileSystems = new ReferenceCountedDisposable<IFileSystem>[MaxFileSystemCount];
|
||||||
|
_fileSystemCount = 0;
|
||||||
|
_counter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReferenceCountedDisposable<IMultiCommitManager> CreateShared(
|
public static ReferenceCountedDisposable<IMultiCommitManager> CreateShared(FileSystemServer fsServer,
|
||||||
ref ReferenceCountedDisposable<ISaveDataMultiCommitCoreInterface> multiCommitInterface,
|
ref ReferenceCountedDisposable<ISaveDataMultiCommitCoreInterface> multiCommitInterface)
|
||||||
HorizonClient client)
|
|
||||||
{
|
{
|
||||||
var manager = new MultiCommitManager(ref multiCommitInterface, client);
|
var manager = new MultiCommitManager(fsServer, ref multiCommitInterface);
|
||||||
return new ReferenceCountedDisposable<IMultiCommitManager>(manager);
|
return new ReferenceCountedDisposable<IMultiCommitManager>(manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
foreach (ReferenceCountedDisposable<IFileSystem> fs in FileSystems)
|
foreach (ReferenceCountedDisposable<IFileSystem> fs in _fileSystems)
|
||||||
{
|
{
|
||||||
fs.Dispose();
|
fs?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_multiCommitInterface?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -71,21 +102,27 @@ namespace LibHac.FsSrv.Impl
|
||||||
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||||
private Result EnsureSaveDataForContext()
|
private Result EnsureSaveDataForContext()
|
||||||
{
|
{
|
||||||
Result rc = MultiCommitInterface.Target.OpenMultiCommitContext(
|
ReferenceCountedDisposable<IFileSystem> contextFs = null;
|
||||||
out ReferenceCountedDisposable<IFileSystem> contextFs);
|
try
|
||||||
|
{
|
||||||
|
Result rc = _multiCommitInterface.Target.OpenMultiCommitContext(out contextFs);
|
||||||
|
|
||||||
if (rc.IsFailure())
|
if (rc.IsFailure())
|
||||||
{
|
{
|
||||||
if (!ResultFs.TargetNotFound.Includes(rc))
|
if (!ResultFs.TargetNotFound.Includes(rc))
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
rc = Hos.Fs.CreateSystemSaveData(SaveDataId, SaveDataSize, SaveJournalSize, SaveDataFlags.None);
|
rc = _fsServer.Hos.Fs.CreateSystemSaveData(SaveDataId, SaveDataSize, SaveJournalSize, SaveDataFlags.None);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
contextFs?.Dispose();
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
contextFs?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a file system to the list of file systems to be committed.
|
/// Adds a file system to the list of file systems to be committed.
|
||||||
|
@ -97,7 +134,7 @@ namespace LibHac.FsSrv.Impl
|
||||||
/// <see cref="ResultFs.MultiCommitFileSystemAlreadyAdded"/>: The provided file system has already been added.</returns>
|
/// <see cref="ResultFs.MultiCommitFileSystemAlreadyAdded"/>: The provided file system has already been added.</returns>
|
||||||
public Result Add(ReferenceCountedDisposable<IFileSystemSf> fileSystem)
|
public Result Add(ReferenceCountedDisposable<IFileSystemSf> fileSystem)
|
||||||
{
|
{
|
||||||
if (FileSystems.Count >= MaxFileSystemCount)
|
if (_fileSystemCount >= MaxFileSystemCount)
|
||||||
return ResultFs.MultiCommitFileSystemLimit.Log();
|
return ResultFs.MultiCommitFileSystemLimit.Log();
|
||||||
|
|
||||||
ReferenceCountedDisposable<IFileSystem> fsaFileSystem = null;
|
ReferenceCountedDisposable<IFileSystem> fsaFileSystem = null;
|
||||||
|
@ -107,14 +144,14 @@ namespace LibHac.FsSrv.Impl
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
// Check that the file system hasn't already been added
|
// Check that the file system hasn't already been added
|
||||||
foreach (ReferenceCountedDisposable<IFileSystem> fs in FileSystems)
|
for (int i = 0; i < _fileSystemCount; i++)
|
||||||
{
|
{
|
||||||
if (ReferenceEquals(fs.Target, fsaFileSystem.Target))
|
if (ReferenceEquals(fsaFileSystem.Target, _fileSystems[i].Target))
|
||||||
return ResultFs.MultiCommitFileSystemAlreadyAdded.Log();
|
return ResultFs.MultiCommitFileSystemAlreadyAdded.Log();
|
||||||
}
|
}
|
||||||
|
|
||||||
FileSystems.Add(fsaFileSystem);
|
_fileSystems[_fileSystemCount] = Shared.Move(ref fsaFileSystem);
|
||||||
fsaFileSystem = null;
|
_fileSystemCount++;
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
@ -132,32 +169,23 @@ namespace LibHac.FsSrv.Impl
|
||||||
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||||
private Result Commit(IFileSystem contextFileSystem)
|
private Result Commit(IFileSystem contextFileSystem)
|
||||||
{
|
{
|
||||||
ContextUpdater context = default;
|
_counter = 1;
|
||||||
|
|
||||||
try
|
using var contextUpdater = new ContextUpdater(contextFileSystem);
|
||||||
{
|
Result rc = contextUpdater.Create(_counter, _fileSystemCount);
|
||||||
Counter = 1;
|
|
||||||
|
|
||||||
context = new ContextUpdater(contextFileSystem);
|
|
||||||
Result rc = context.Create(Counter, FileSystems.Count);
|
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = CommitProvisionallyFileSystem(Counter);
|
rc = CommitProvisionallyFileSystem(_counter);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = context.CommitProvisionallyDone();
|
rc = contextUpdater.CommitProvisionallyDone();
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = CommitFileSystem();
|
rc = CommitFileSystem();
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = context.CommitDone();
|
rc = contextUpdater.CommitDone();
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
context.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
@ -168,15 +196,15 @@ namespace LibHac.FsSrv.Impl
|
||||||
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||||
public Result Commit()
|
public Result Commit()
|
||||||
{
|
{
|
||||||
lock (Locker)
|
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref Globals.MultiCommitMutex);
|
||||||
{
|
|
||||||
Result rc = EnsureSaveDataForContext();
|
|
||||||
if (rc.IsFailure()) return rc;
|
|
||||||
|
|
||||||
ReferenceCountedDisposable<IFileSystem> contextFs = null;
|
ReferenceCountedDisposable<IFileSystem> contextFs = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
rc = MultiCommitInterface.Target.OpenMultiCommitContext(out contextFs);
|
Result rc = EnsureSaveDataForContext();
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
rc = _multiCommitInterface.Target.OpenMultiCommitContext(out contextFs);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
return Commit(contextFs.Target);
|
return Commit(contextFs.Target);
|
||||||
|
@ -186,7 +214,6 @@ namespace LibHac.FsSrv.Impl
|
||||||
contextFs?.Dispose();
|
contextFs?.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to provisionally commit all the added file systems.
|
/// Tries to provisionally commit all the added file systems.
|
||||||
|
@ -198,9 +225,11 @@ namespace LibHac.FsSrv.Impl
|
||||||
Result rc = Result.Success;
|
Result rc = Result.Success;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < FileSystems.Count; i++)
|
for (i = 0; i < _fileSystemCount; i++)
|
||||||
{
|
{
|
||||||
rc = FileSystems[i].Target.CommitProvisionally(counter);
|
Assert.SdkNotNull(_fileSystems[i]);
|
||||||
|
|
||||||
|
rc = _fileSystems[i].Target.CommitProvisionally(counter);
|
||||||
|
|
||||||
if (rc.IsFailure())
|
if (rc.IsFailure())
|
||||||
break;
|
break;
|
||||||
|
@ -211,7 +240,9 @@ namespace LibHac.FsSrv.Impl
|
||||||
// Rollback all provisional commits including the failed commit
|
// Rollback all provisional commits including the failed commit
|
||||||
for (int j = 0; j <= i; j++)
|
for (int j = 0; j <= i; j++)
|
||||||
{
|
{
|
||||||
FileSystems[j].Target.Rollback().IgnoreResult();
|
Assert.SdkNotNull(_fileSystems[j]);
|
||||||
|
|
||||||
|
_fileSystems[j].Target.Rollback().IgnoreResult();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,14 +255,17 @@ namespace LibHac.FsSrv.Impl
|
||||||
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||||
private Result CommitFileSystem()
|
private Result CommitFileSystem()
|
||||||
{
|
{
|
||||||
// All file systems will try to be recovered committed, even if one fails.
|
// Try to commit all file systems even if one fails.
|
||||||
// If any commits fail, the result from the first failed recovery will be returned.
|
// If any commits fail, the result from the first failed recovery will be returned.
|
||||||
Result result = Result.Success;
|
Result result = Result.Success;
|
||||||
|
|
||||||
foreach (ReferenceCountedDisposable<IFileSystem> fs in FileSystems)
|
for (int i = 0; i < _fileSystemCount; i++)
|
||||||
{
|
{
|
||||||
Result rc = fs.Target.Commit();
|
Assert.SdkNotNull(_fileSystems[i]);
|
||||||
|
|
||||||
|
Result rc = _fileSystems[i].Target.Commit();
|
||||||
|
|
||||||
|
// If the commit failed, set the overall result if it hasn't been set yet.
|
||||||
if (result.IsSuccess() && rc.IsFailure())
|
if (result.IsSuccess() && rc.IsFailure())
|
||||||
{
|
{
|
||||||
result = rc;
|
result = rc;
|
||||||
|
@ -256,11 +290,15 @@ namespace LibHac.FsSrv.Impl
|
||||||
private static Result RecoverCommit(ISaveDataMultiCommitCoreInterface multiCommitInterface,
|
private static Result RecoverCommit(ISaveDataMultiCommitCoreInterface multiCommitInterface,
|
||||||
IFileSystem contextFs, SaveDataFileSystemServiceImpl saveService)
|
IFileSystem contextFs, SaveDataFileSystemServiceImpl saveService)
|
||||||
{
|
{
|
||||||
|
var contextFilePath = new Fs.Path();
|
||||||
|
Result rc = PathFunctions.SetUpFixedPath(ref contextFilePath, CommitContextFileName);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
IFile contextFile = null;
|
IFile contextFile = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Read the multi-commit context
|
// Read the multi-commit context
|
||||||
Result rc = contextFs.OpenFile(out contextFile, CommitContextFileName, OpenMode.ReadWrite);
|
rc = contextFs.OpenFile(out contextFile, in contextFilePath, OpenMode.ReadWrite);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
Unsafe.SkipInit(out Context context);
|
Unsafe.SkipInit(out Context context);
|
||||||
|
@ -330,12 +368,13 @@ namespace LibHac.FsSrv.Impl
|
||||||
{
|
{
|
||||||
rc = multiCommitInterface.RecoverProvisionallyCommittedSaveData(in savesToRecover[i], false);
|
rc = multiCommitInterface.RecoverProvisionallyCommittedSaveData(in savesToRecover[i], false);
|
||||||
|
|
||||||
if (recoveryResult.IsSuccess() && rc.IsFailure())
|
if (rc.IsFailure() && !recoveryResult.IsFailure())
|
||||||
{
|
{
|
||||||
recoveryResult = rc;
|
recoveryResult = rc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
contextFilePath.Dispose();
|
||||||
return recoveryResult;
|
return recoveryResult;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
@ -426,13 +465,18 @@ namespace LibHac.FsSrv.Impl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var contextFilePath = new Fs.Path();
|
||||||
|
rc = PathFunctions.SetUpFixedPath(ref contextFilePath, CommitContextFileName);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
// Delete the commit context file
|
// Delete the commit context file
|
||||||
rc = contextFs.DeleteFile(CommitContextFileName);
|
rc = contextFs.DeleteFile(in contextFilePath);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = contextFs.Commit();
|
rc = contextFs.Commit();
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
contextFilePath.Dispose();
|
||||||
return recoveryResult;
|
return recoveryResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -440,16 +484,18 @@ namespace LibHac.FsSrv.Impl
|
||||||
/// Recovers an interrupted multi-commit. The commit will either be completed or rolled back depending on
|
/// Recovers an interrupted multi-commit. The commit will either be completed or rolled back depending on
|
||||||
/// where in the commit process it was interrupted. Does nothing if there is no commit to recover.
|
/// where in the commit process it was interrupted. Does nothing if there is no commit to recover.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="fsServer">The <see cref="FileSystemServer"/> that contains the save data to recover.</param>
|
||||||
/// <param name="multiCommitInterface">The core interface used for multi-commits.</param>
|
/// <param name="multiCommitInterface">The core interface used for multi-commits.</param>
|
||||||
/// <param name="saveService">The save data service.</param>
|
/// <param name="saveService">The save data service.</param>
|
||||||
/// <returns>The <see cref="Result"/> of the operation.<br/>
|
/// <returns>The <see cref="Result"/> of the operation.<br/>
|
||||||
/// <see cref="Result.Success"/>: The recovery was successful or there was no multi-commit to recover.</returns>
|
/// <see cref="Result.Success"/>: The recovery was successful or there was no multi-commit to recover.</returns>
|
||||||
public static Result Recover(ISaveDataMultiCommitCoreInterface multiCommitInterface,
|
public static Result Recover(FileSystemServer fsServer, ISaveDataMultiCommitCoreInterface multiCommitInterface,
|
||||||
SaveDataFileSystemServiceImpl saveService)
|
SaveDataFileSystemServiceImpl saveService)
|
||||||
{
|
{
|
||||||
lock (Locker)
|
ref MultiCommitManagerGlobals globals = ref fsServer.Globals.MultiCommitManager;
|
||||||
{
|
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref globals.MultiCommitMutex);
|
||||||
bool needsRecover = true;
|
|
||||||
|
bool needsRecovery = true;
|
||||||
ReferenceCountedDisposable<IFileSystem> fileSystem = null;
|
ReferenceCountedDisposable<IFileSystem> fileSystem = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
|
@ -463,23 +509,29 @@ namespace LibHac.FsSrv.Impl
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
// Unable to open the multi-commit context file system, so there's nothing to recover
|
// Unable to open the multi-commit context file system, so there's nothing to recover
|
||||||
needsRecover = false;
|
needsRecovery = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsRecover)
|
if (needsRecovery)
|
||||||
{
|
{
|
||||||
rc = fileSystem.Target.OpenFile(out IFile file, CommitContextFileName, OpenMode.Read);
|
var contextFilePath = new Fs.Path();
|
||||||
|
rc = PathFunctions.SetUpFixedPath(ref contextFilePath, CommitContextFileName);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
rc = fileSystem.Target.OpenFile(out IFile file, in contextFilePath, OpenMode.Read);
|
||||||
file?.Dispose();
|
file?.Dispose();
|
||||||
|
|
||||||
if (rc.IsFailure())
|
if (rc.IsFailure())
|
||||||
{
|
{
|
||||||
// Unable to open the context file. No multi-commit to recover.
|
// Unable to open the context file. No multi-commit to recover.
|
||||||
if (ResultFs.PathNotFound.Includes(rc))
|
if (ResultFs.PathNotFound.Includes(rc))
|
||||||
needsRecover = false;
|
needsRecovery = false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!needsRecover)
|
contextFilePath.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!needsRecovery)
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
|
|
||||||
// There was a context file. Recover the unfinished commit.
|
// There was a context file. Recover the unfinished commit.
|
||||||
|
@ -490,7 +542,6 @@ namespace LibHac.FsSrv.Impl
|
||||||
fileSystem?.Dispose();
|
fileSystem?.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x18)]
|
[StructLayout(LayoutKind.Explicit, Size = 0x18)]
|
||||||
private struct Context
|
private struct Context
|
||||||
|
@ -509,41 +560,58 @@ namespace LibHac.FsSrv.Impl
|
||||||
ProvisionallyCommitted = 2
|
ProvisionallyCommitted = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct ContextUpdater
|
private struct ContextUpdater : IDisposable
|
||||||
{
|
{
|
||||||
private IFileSystem _fileSystem;
|
|
||||||
private Context _context;
|
private Context _context;
|
||||||
|
private IFileSystem _fileSystem;
|
||||||
|
|
||||||
public ContextUpdater(IFileSystem contextFileSystem)
|
public ContextUpdater(IFileSystem contextFileSystem)
|
||||||
{
|
{
|
||||||
_fileSystem = contextFileSystem;
|
|
||||||
_context = default;
|
_context = default;
|
||||||
|
_fileSystem = contextFileSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_fileSystem is null) return;
|
||||||
|
|
||||||
|
var contextFilePath = new Fs.Path();
|
||||||
|
PathFunctions.SetUpFixedPath(ref contextFilePath, CommitContextFileName).IgnoreResult();
|
||||||
|
_fileSystem.DeleteFile(in contextFilePath).IgnoreResult();
|
||||||
|
_fileSystem.Commit().IgnoreResult();
|
||||||
|
|
||||||
|
_fileSystem = null;
|
||||||
|
contextFilePath.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates and writes the initial commit context to a file.
|
/// Creates and writes the initial commit context to a file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="commitCount">The counter.</param>
|
/// <param name="counter">The counter.</param>
|
||||||
/// <param name="fileSystemCount">The number of file systems being committed.</param>
|
/// <param name="fileSystemCount">The number of file systems being committed.</param>
|
||||||
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||||
public Result Create(long commitCount, int fileSystemCount)
|
public Result Create(long counter, int fileSystemCount)
|
||||||
{
|
{
|
||||||
|
var contextFilePath = new Fs.Path();
|
||||||
|
Result rc = PathFunctions.SetUpFixedPath(ref contextFilePath, CommitContextFileName);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
IFile contextFile = null;
|
IFile contextFile = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Open context file and create if it doesn't exist
|
// Open context file and create if it doesn't exist
|
||||||
Result rc = _fileSystem.OpenFile(out contextFile, CommitContextFileName, OpenMode.Read);
|
rc = _fileSystem.OpenFile(out contextFile, in contextFilePath, OpenMode.Read);
|
||||||
|
|
||||||
if (rc.IsFailure())
|
if (rc.IsFailure())
|
||||||
{
|
{
|
||||||
if (!ResultFs.PathNotFound.Includes(rc))
|
if (!ResultFs.PathNotFound.Includes(rc))
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
rc = _fileSystem.CreateFile(CommitContextFileName, CommitContextFileSize, CreateFileOptions.None);
|
rc = _fileSystem.CreateFile(in contextFilePath, CommitContextFileSize);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = _fileSystem.OpenFile(out contextFile, CommitContextFileName, OpenMode.Read);
|
rc = _fileSystem.OpenFile(out contextFile, in contextFilePath, OpenMode.Read);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -554,13 +622,13 @@ namespace LibHac.FsSrv.Impl
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Result rc = _fileSystem.OpenFile(out contextFile, CommitContextFileName, OpenMode.ReadWrite);
|
rc = _fileSystem.OpenFile(out contextFile, in contextFilePath, OpenMode.ReadWrite);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
_context.Version = CurrentCommitContextVersion;
|
_context.Version = CurrentCommitContextVersion;
|
||||||
_context.State = CommitState.NotCommitted;
|
_context.State = CommitState.NotCommitted;
|
||||||
_context.FileSystemCount = fileSystemCount;
|
_context.FileSystemCount = fileSystemCount;
|
||||||
_context.Counter = commitCount;
|
_context.Counter = counter;
|
||||||
|
|
||||||
// Write the initial context to the file
|
// Write the initial context to the file
|
||||||
rc = contextFile.Write(0, SpanHelpers.AsByteSpan(ref _context), WriteOption.None);
|
rc = contextFile.Write(0, SpanHelpers.AsByteSpan(ref _context), WriteOption.None);
|
||||||
|
@ -574,7 +642,11 @@ namespace LibHac.FsSrv.Impl
|
||||||
contextFile?.Dispose();
|
contextFile?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
return _fileSystem.Commit();
|
rc = _fileSystem.Commit();
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
contextFilePath.Dispose();
|
||||||
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -584,11 +656,15 @@ namespace LibHac.FsSrv.Impl
|
||||||
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||||
public Result CommitProvisionallyDone()
|
public Result CommitProvisionallyDone()
|
||||||
{
|
{
|
||||||
|
var contextFilePath = new Fs.Path();
|
||||||
|
Result rc = PathFunctions.SetUpFixedPath(ref contextFilePath, CommitContextFileName);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
IFile contextFile = null;
|
IFile contextFile = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Result rc = _fileSystem.OpenFile(out contextFile, CommitContextFileName, OpenMode.ReadWrite);
|
rc = _fileSystem.OpenFile(out contextFile, in contextFilePath, OpenMode.ReadWrite);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
_context.State = CommitState.ProvisionallyCommitted;
|
_context.State = CommitState.ProvisionallyCommitted;
|
||||||
|
@ -604,6 +680,7 @@ namespace LibHac.FsSrv.Impl
|
||||||
contextFile?.Dispose();
|
contextFile?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
contextFilePath.Dispose();
|
||||||
return _fileSystem.Commit();
|
return _fileSystem.Commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -613,25 +690,20 @@ namespace LibHac.FsSrv.Impl
|
||||||
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
/// <returns>The <see cref="Result"/> of the operation.</returns>
|
||||||
public Result CommitDone()
|
public Result CommitDone()
|
||||||
{
|
{
|
||||||
Result rc = _fileSystem.DeleteFile(CommitContextFileName);
|
var contextFilePath = new Fs.Path();
|
||||||
|
Result rc = PathFunctions.SetUpFixedPath(ref contextFilePath, CommitContextFileName);
|
||||||
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
|
rc = _fileSystem.DeleteFile(in contextFilePath);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
rc = _fileSystem.Commit();
|
rc = _fileSystem.Commit();
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
_fileSystem = null;
|
_fileSystem = null;
|
||||||
|
contextFilePath.Dispose();
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_fileSystem is null) return;
|
|
||||||
|
|
||||||
_fileSystem.DeleteFile(CommitContextFileName).IgnoreResult();
|
|
||||||
_fileSystem.Commit().IgnoreResult();
|
|
||||||
|
|
||||||
_fileSystem = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2119,7 +2119,7 @@ namespace LibHac.FsSrv
|
||||||
saveService = SelfReference.AddReference();
|
saveService = SelfReference.AddReference();
|
||||||
commitInterface = saveService.AddReference<ISaveDataMultiCommitCoreInterface>();
|
commitInterface = saveService.AddReference<ISaveDataMultiCommitCoreInterface>();
|
||||||
|
|
||||||
commitManager = MultiCommitManager.CreateShared(ref commitInterface, Hos);
|
commitManager = MultiCommitManager.CreateShared(ServiceImpl.FsServer, ref commitInterface);
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
@ -2146,7 +2146,7 @@ namespace LibHac.FsSrv
|
||||||
|
|
||||||
public Result RecoverMultiCommit()
|
public Result RecoverMultiCommit()
|
||||||
{
|
{
|
||||||
return MultiCommitManager.Recover(this, ServiceImpl);
|
return MultiCommitManager.Recover(ServiceImpl.FsServer, this, ServiceImpl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Result IsProvisionallyCommittedSaveData(out bool isProvisionallyCommitted, in SaveDataInfo saveInfo)
|
public Result IsProvisionallyCommittedSaveData(out bool isProvisionallyCommitted, in SaveDataInfo saveInfo)
|
||||||
|
|
|
@ -26,6 +26,7 @@ namespace LibHac.FsSrv
|
||||||
private TimeStampGetter _timeStampGetter;
|
private TimeStampGetter _timeStampGetter;
|
||||||
|
|
||||||
internal HorizonClient Hos => _config.FsServer.Hos;
|
internal HorizonClient Hos => _config.FsServer.Hos;
|
||||||
|
internal FileSystemServer FsServer => _config.FsServer;
|
||||||
|
|
||||||
private class TimeStampGetter : ISaveDataCommitTimeStampGetter
|
private class TimeStampGetter : ISaveDataCommitTimeStampGetter
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue