Use a lock file for directory save data

This commit is contained in:
Alex Barney 2021-05-18 17:57:41 -07:00
parent ccb8c078aa
commit a1477cc9f9
2 changed files with 35 additions and 8 deletions

View file

@ -135,7 +135,7 @@ namespace LibHac.FsSrv
bool allowDirectorySaveData = IsAllowedDirectorySaveData2(spaceId, saveDataRootPath);
// Note: When directory save data is allowed, Nintendo creates the save directory if it doesn't exist.
// This bypasses normal save data// creation, leaving the save with empty extra data.
// This bypasses normal save data creation, leaving the save with empty extra data.
// Instead, we return that the save doesn't exist if the directory is missing.
// Note: Nintendo doesn't cache directory save data

View file

@ -32,13 +32,15 @@ namespace LibHac.FsSystem
{
private const int IdealWorkBufferSize = 0x100000; // 1 MiB
private ReadOnlySpan<byte> CommittedDirectoryBytes => new[] { (byte)'/', (byte)'0', (byte)'/' };
private ReadOnlySpan<byte> WorkingDirectoryBytes => new[] { (byte)'/', (byte)'1', (byte)'/' };
private ReadOnlySpan<byte> SynchronizingDirectoryBytes => new[] { (byte)'/', (byte)'_', (byte)'/' };
private static ReadOnlySpan<byte> CommittedDirectoryBytes => new[] { (byte)'/', (byte)'0', (byte)'/' };
private static ReadOnlySpan<byte> WorkingDirectoryBytes => new[] { (byte)'/', (byte)'1', (byte)'/' };
private static ReadOnlySpan<byte> SynchronizingDirectoryBytes => new[] { (byte)'/', (byte)'_', (byte)'/' };
private static ReadOnlySpan<byte> LockFileBytes => new[] { (byte)'/', (byte)'.', (byte)'l', (byte)'o', (byte)'c', (byte)'k' };
private U8Span CommittedDirectoryPath => new U8Span(CommittedDirectoryBytes);
private U8Span WorkingDirectoryPath => new U8Span(WorkingDirectoryBytes);
private U8Span SynchronizingDirectoryPath => new U8Span(SynchronizingDirectoryBytes);
private static U8Span CommittedDirectoryPath => new U8Span(CommittedDirectoryBytes);
private static U8Span WorkingDirectoryPath => new U8Span(WorkingDirectoryBytes);
private static U8Span SynchronizingDirectoryPath => new U8Span(SynchronizingDirectoryBytes);
private static U8Span LockFilePath => new U8Span(LockFileBytes);
private FileSystemClient _fsClient;
private IFileSystem _baseFs;
@ -60,6 +62,9 @@ namespace LibHac.FsSystem
private SaveDataSpaceId _spaceId;
private ulong _saveDataId;
// Additions to ensure only one directory save data fs is opened at a time
private IFile _lockFile;
private class DirectorySaveDataFile : IFile
{
private IFile _baseFile;
@ -183,6 +188,9 @@ namespace LibHac.FsSystem
protected override void Dispose(bool disposing)
{
_lockFile?.Dispose();
_lockFile = null;
_cacheObserver?.Unregister(_spaceId, _saveDataId);
_baseFs?.Dispose();
base.Dispose(disposing);
@ -196,14 +204,33 @@ namespace LibHac.FsSystem
public Result Initialize(ISaveDataCommitTimeStampGetter timeStampGetter, RandomDataGenerator randomGenerator,
bool isJournalingSupported, bool isMultiCommitSupported, bool isJournalingEnabled)
{
Result rc;
_isJournalingSupported = isJournalingSupported;
_isMultiCommitSupported = isMultiCommitSupported;
_isJournalingEnabled = isJournalingEnabled;
_timeStampGetter = timeStampGetter ?? _timeStampGetter;
_randomGenerator = randomGenerator ?? _randomGenerator;
// Open the lock file
if (_lockFile is null)
{
rc = _baseFs.OpenFile(out _lockFile, LockFilePath, OpenMode.ReadWrite);
if (rc.IsFailure())
{
if (!ResultFs.PathNotFound.Includes(rc)) return rc;
rc = _baseFs.CreateFile(LockFilePath, 0);
if (rc.IsFailure()) return rc;
rc = _baseFs.OpenFile(out _lockFile, LockFilePath, OpenMode.ReadWrite);
if (rc.IsFailure()) return rc;
}
}
// Ensure the working directory exists
Result rc = _baseFs.GetEntryType(out _, WorkingDirectoryPath);
rc = _baseFs.GetEntryType(out _, WorkingDirectoryPath);
if (rc.IsFailure())
{