Update MultiCommitManager

This commit is contained in:
Alex Barney 2021-07-19 01:13:56 -07:00
parent 4934b1cbef
commit 4ceb925cde
4 changed files with 218 additions and 143 deletions

View file

@ -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();
} }
} }

View file

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

View file

@ -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)

View file

@ -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
{ {