From a1477cc9f931147b3065a48be8f7d165cb1e622f Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Tue, 18 May 2021 17:57:41 -0700 Subject: [PATCH] Use a lock file for directory save data --- .../FsSrv/SaveDataFileSystemServiceImpl.cs | 2 +- .../FsSystem/DirectorySaveDataFileSystem.cs | 41 +++++++++++++++---- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs b/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs index 5ef851c4..eb743f38 100644 --- a/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs +++ b/src/LibHac/FsSrv/SaveDataFileSystemServiceImpl.cs @@ -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 diff --git a/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs b/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs index a64448d0..3d9b4159 100644 --- a/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs +++ b/src/LibHac/FsSystem/DirectorySaveDataFileSystem.cs @@ -32,13 +32,15 @@ namespace LibHac.FsSystem { private const int IdealWorkBufferSize = 0x100000; // 1 MiB - private ReadOnlySpan CommittedDirectoryBytes => new[] { (byte)'/', (byte)'0', (byte)'/' }; - private ReadOnlySpan WorkingDirectoryBytes => new[] { (byte)'/', (byte)'1', (byte)'/' }; - private ReadOnlySpan SynchronizingDirectoryBytes => new[] { (byte)'/', (byte)'_', (byte)'/' }; + private static ReadOnlySpan CommittedDirectoryBytes => new[] { (byte)'/', (byte)'0', (byte)'/' }; + private static ReadOnlySpan WorkingDirectoryBytes => new[] { (byte)'/', (byte)'1', (byte)'/' }; + private static ReadOnlySpan SynchronizingDirectoryBytes => new[] { (byte)'/', (byte)'_', (byte)'/' }; + private static ReadOnlySpan 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()) {