diff --git a/src/LibHac/FsSystem/ConcatenationFileSystem.cs b/src/LibHac/FsSystem/ConcatenationFileSystem.cs
index 2ded58e4..d20119ed 100644
--- a/src/LibHac/FsSystem/ConcatenationFileSystem.cs
+++ b/src/LibHac/FsSystem/ConcatenationFileSystem.cs
@@ -8,6 +8,7 @@ using LibHac.Common.FixedArrays;
using LibHac.Diag;
using LibHac.Fs;
using LibHac.Fs.Fsa;
+using LibHac.Os;
using LibHac.Util;
using static LibHac.FsSystem.Utility;
@@ -27,7 +28,7 @@ namespace LibHac.FsSystem;
/// Each internal file except the final one must have the internal file size that was specified
/// at the creation of the .
///
-/// Based on FS 12.1.0 (nnSdk 12.3.1)
+/// Based on FS 13.1.0 (nnSdk 13.4.0)
///
public class ConcatenationFileSystem : IFileSystem
{
@@ -211,6 +212,8 @@ public class ConcatenationFileSystem : IFileSystem
if (size > currentSize)
{
+ Assert.SdkAssert(_fileArray.Count > currentTailIndex);
+
rc = _fileArray[currentTailIndex].SetSize(GetInternalFileSize(size, currentTailIndex));
if (rc.IsFailure()) return rc.Miss();
@@ -285,12 +288,13 @@ public class ConcatenationFileSystem : IFileSystem
if (!_mode.HasFlag(OpenMode.Read))
return ResultFs.ReadUnpermitted.Log();
- var closure = new OperateRangeClosure();
- closure.OutBuffer = outBuffer;
- closure.InBuffer = inBuffer;
- closure.OperationId = operationId;
+ foreach (IFile file in _fileArray)
+ {
+ Result rc = file.OperateRange(operationId, 0, long.MaxValue);
+ if (rc.IsFailure()) return rc.Miss();
+ }
- return DoOperateRangeImpl(offset, size, InvalidateCacheImpl, ref closure).Ret();
+ return Result.Success;
}
case OperationId.QueryRange:
{
@@ -312,11 +316,6 @@ public class ConcatenationFileSystem : IFileSystem
return ResultFs.UnsupportedOperateRangeForConcatenationFile.Log();
}
- static Result InvalidateCacheImpl(IFile file, long offset, long size, ref OperateRangeClosure closure)
- {
- return file.OperateRange(closure.OutBuffer, closure.OperationId, offset, size, closure.InBuffer).Ret();
- }
-
static Result QueryRangeImpl(IFile file, long offset, long size, ref OperateRangeClosure closure)
{
Unsafe.SkipInit(out QueryRangeInfo infoEntry);
@@ -370,7 +369,6 @@ public class ConcatenationFileSystem : IFileSystem
private ref struct OperateRangeClosure
{
- public Span OutBuffer;
public ReadOnlySpan InBuffer;
public OperationId OperationId;
public QueryRangeInfo InfoMerged;
@@ -496,6 +494,7 @@ public class ConcatenationFileSystem : IFileSystem
private UniqueRef _baseFileSystem;
private long _internalFileSize;
+ private SdkMutexType _mutex;
///
/// Initializes a new with an internal file size of .
@@ -516,6 +515,7 @@ public class ConcatenationFileSystem : IFileSystem
{
_baseFileSystem = new UniqueRef(ref baseFileSystem);
_internalFileSize = internalFileSize;
+ _mutex = new SdkMutexType();
}
public override void Dispose()
@@ -611,6 +611,8 @@ public class ConcatenationFileSystem : IFileSystem
protected override Result DoGetEntryType(out DirectoryEntryType entryType, in Path path)
{
+ using var scopedLock = new ScopedLock(ref _mutex);
+
if (IsConcatenationFile(in path))
{
entryType = DirectoryEntryType.File;
@@ -622,26 +624,36 @@ public class ConcatenationFileSystem : IFileSystem
protected override Result DoGetFreeSpaceSize(out long freeSpace, in Path path)
{
+ using var scopedLock = new ScopedLock(ref _mutex);
+
return _baseFileSystem.Get.GetFreeSpaceSize(out freeSpace, path).Ret();
}
protected override Result DoGetTotalSpaceSize(out long totalSpace, in Path path)
{
+ using var scopedLock = new ScopedLock(ref _mutex);
+
return _baseFileSystem.Get.GetTotalSpaceSize(out totalSpace, path).Ret();
}
protected override Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, in Path path)
{
+ using var scopedLock = new ScopedLock(ref _mutex);
+
return _baseFileSystem.Get.GetFileTimeStampRaw(out timeStamp, path).Ret();
}
protected override Result DoFlush()
{
+ using var scopedLock = new ScopedLock(ref _mutex);
+
return _baseFileSystem.Get.Flush().Ret();
}
protected override Result DoOpenFile(ref UniqueRef outFile, in Path path, OpenMode mode)
{
+ using var scopedLock = new ScopedLock(ref _mutex);
+
if (!IsConcatenationFile(in path))
{
return _baseFileSystem.Get.OpenFile(ref outFile, in path, mode).Ret();
@@ -700,6 +712,8 @@ public class ConcatenationFileSystem : IFileSystem
protected override Result DoOpenDirectory(ref UniqueRef outDirectory, in Path path,
OpenDirectoryMode mode)
{
+ using var scopedLock = new ScopedLock(ref _mutex);
+
if (IsConcatenationFile(path))
{
return ResultFs.PathNotFound.Log();
@@ -720,6 +734,8 @@ public class ConcatenationFileSystem : IFileSystem
protected override Result DoCreateFile(in Path path, long size, CreateFileOptions option)
{
+ using var scopedLock = new ScopedLock(ref _mutex);
+
CreateFileOptions newOption = option & ~CreateFileOptions.CreateConcatenationFile;
// Create a normal file if the concatenation file flag isn't set
@@ -802,6 +818,8 @@ public class ConcatenationFileSystem : IFileSystem
protected override Result DoDeleteFile(in Path path)
{
+ using var scopedLock = new ScopedLock(ref _mutex);
+
if (!IsConcatenationFile(in path))
{
return _baseFileSystem.Get.DeleteFile(in path).Ret();
@@ -834,6 +852,8 @@ public class ConcatenationFileSystem : IFileSystem
protected override Result DoCreateDirectory(in Path path)
{
+ using var scopedLock = new ScopedLock(ref _mutex);
+
// Check if the parent path is a concatenation file because we can't create a directory inside one.
using var parentPath = new Path();
Result rc = GenerateParentPath(ref parentPath.Ref(), in path);
@@ -847,6 +867,8 @@ public class ConcatenationFileSystem : IFileSystem
protected override Result DoDeleteDirectory(in Path path)
{
+ using var scopedLock = new ScopedLock(ref _mutex);
+
// Make sure the directory isn't a concatenation file.
if (IsConcatenationFile(path))
return ResultFs.PathNotFound.Log();
@@ -875,7 +897,13 @@ public class ConcatenationFileSystem : IFileSystem
protected override Result DoDeleteDirectoryRecursively(in Path path)
{
- if (IsConcatenationFile(in path))
+ bool isConcatenationFile;
+ using (new ScopedLock(ref _mutex))
+ {
+ isConcatenationFile = IsConcatenationFile(in path);
+ }
+
+ if (isConcatenationFile)
return ResultFs.PathNotFound.Log();
Result rc = CleanDirectoryRecursivelyImpl(in path);
@@ -886,7 +914,13 @@ public class ConcatenationFileSystem : IFileSystem
protected override Result DoCleanDirectoryRecursively(in Path path)
{
- if (IsConcatenationFile(in path))
+ bool isConcatenationFile;
+ using (new ScopedLock(ref _mutex))
+ {
+ isConcatenationFile = IsConcatenationFile(in path);
+ }
+
+ if (isConcatenationFile)
return ResultFs.PathNotFound.Log();
return CleanDirectoryRecursivelyImpl(in path).Ret();
@@ -894,6 +928,8 @@ public class ConcatenationFileSystem : IFileSystem
protected override Result DoRenameFile(in Path currentPath, in Path newPath)
{
+ using var scopedLock = new ScopedLock(ref _mutex);
+
if (IsConcatenationFile(in currentPath))
return _baseFileSystem.Get.RenameDirectory(in currentPath, in newPath).Ret();
@@ -902,6 +938,8 @@ public class ConcatenationFileSystem : IFileSystem
protected override Result DoRenameDirectory(in Path currentPath, in Path newPath)
{
+ using var scopedLock = new ScopedLock(ref _mutex);
+
if (IsConcatenationFile(in currentPath))
return ResultFs.PathNotFound.Log();
@@ -912,6 +950,8 @@ public class ConcatenationFileSystem : IFileSystem
{
UnsafeHelpers.SkipParamInit(out size);
+ using var scopedLock = new ScopedLock(ref _mutex);
+
using var internalFilePath = new Path();
Result rc = internalFilePath.Initialize(in path);
if (rc.IsFailure()) return rc.Miss();
@@ -950,6 +990,8 @@ public class ConcatenationFileSystem : IFileSystem
protected override Result DoQueryEntry(Span outBuffer, ReadOnlySpan inBuffer, QueryId queryId,
in Path path)
{
+ using var scopedLock = new ScopedLock(ref _mutex);
+
if (queryId != QueryId.SetConcatenationFileAttribute)
return ResultFs.UnsupportedQueryEntryForConcatenationFileSystem.Log();
@@ -958,11 +1000,15 @@ public class ConcatenationFileSystem : IFileSystem
protected override Result DoCommit()
{
+ using var scopedLock = new ScopedLock(ref _mutex);
+
return _baseFileSystem.Get.Commit().Ret();
}
protected override Result DoCommitProvisionally(long counter)
{
+ using var scopedLock = new ScopedLock(ref _mutex);
+
return _baseFileSystem.Get.CommitProvisionally(counter).Ret();
}
}
\ No newline at end of file