diff --git a/build/CodeGen/results.csv b/build/CodeGen/results.csv
index f6eeebe6..4fb9abc7 100644
--- a/build/CodeGen/results.csv
+++ b/build/CodeGen/results.csv
@@ -934,6 +934,10 @@ Module,DescriptionStart,DescriptionEnd,Flags,Namespace,Name,Summary
2,5326,,,,UnexpectedInCompressedStorageC,
2,5327,,,,UnexpectedInCompressedStorageD,
2,5328,,,,UnexpectedInPathA,
+2,5333,,,,UnexpectedInSaveDataFileSystemCoreImplA,
+2,5334,,,,UnexpectedInIntegritySaveDataFileSystemA,
+2,5335,,,,UnexpectedInJournalIntegritySaveDataFileSystemD,
+2,5336,,,,UnexpectedInAlignmentMatchableFileSystemA,
2,6000,6499,,,PreconditionViolation,
2,6001,6199,,,InvalidArgument,
@@ -1085,11 +1089,14 @@ Module,DescriptionStart,DescriptionEnd,Flags,Namespace,Name,Summary
2,6460,,,,GameCardLogoDataSizeInvalid,
2,6461,,,,AllocatorAlignmentViolation,
2,6462,,,,GlobalFileDataCacheAlreadyEnabled,
-2,6463,,,,MultiCommitHasOverlappingTargets,The provided file system has already been added to the multi-commit manager.
-2,6464,,,,MultiCommitAlreadyInProgress,A multi-commit was performed while another multi-commit operation was already running.
+2,6463,,,,MultiCommitFileSystemDuplicated,The provided file system has already been added to the multi-commit manager.
+2,6464,,,,SaveDataMultiCommitRepeated,A multi-commit was performed while another multi-commit operation was already running.
2,6465,,,,UserNotExist,
2,6466,,,,DefaultGlobalFileDataCacheEnabled,
2,6467,,,,SaveDataRootPathUnavailable,
+2,6470,,,,RomMountDivisionSizeUnitCountLimit,
+2,6471,,,,RomMountCountLimit,
+2,6472,,,,AocMountDivisionSizeUnitCountLimit,
2,6600,6699,,,NotFound,
2,6602,,,,FileNotFound,
@@ -1150,7 +1157,7 @@ Module,DescriptionStart,DescriptionEnd,Flags,Namespace,Name,Summary
2,7113,,,,InvalidRamDiskSaveDataFileReadOffset,
2,7114,,,,InvalidRamDiskSaveDataCoreDataStorageSize,
-2,7121,7139,,,RamDiskDatabaseCorrupted,
+2,7121,7129,,,RamDiskDatabaseCorrupted,
2,7122,,,,InvalidRamDiskAllocationTableBlock,
2,7123,,,,InvalidRamDiskKeyValueListElementIndex,
2,7124,,,,InvalidRamDiskAllocationTableChainEntry,
diff --git a/src/LibHac/Fs/FsEnums.cs b/src/LibHac/Fs/FsEnums.cs
index cf5693b2..c255d46e 100644
--- a/src/LibHac/Fs/FsEnums.cs
+++ b/src/LibHac/Fs/FsEnums.cs
@@ -28,7 +28,8 @@ public enum ContentStorageId
{
System = 0,
User = 1,
- SdCard = 2
+ SdCard = 2,
+ System0 = 3
}
public enum GameCardPartition
diff --git a/src/LibHac/Fs/ResultFs.cs b/src/LibHac/Fs/ResultFs.cs
index b6b9cdc5..81fc7d1f 100644
--- a/src/LibHac/Fs/ResultFs.cs
+++ b/src/LibHac/Fs/ResultFs.cs
@@ -1699,6 +1699,14 @@ public static class ResultFs
public static Result.Base UnexpectedInCompressedStorageD => new Result.Base(ModuleFs, 5327);
/// Error code: 2002-5328; Inner value: 0x29a002
public static Result.Base UnexpectedInPathA => new Result.Base(ModuleFs, 5328);
+ /// Error code: 2002-5333; Inner value: 0x29aa02
+ public static Result.Base UnexpectedInSaveDataFileSystemCoreImplA => new Result.Base(ModuleFs, 5333);
+ /// Error code: 2002-5334; Inner value: 0x29ac02
+ public static Result.Base UnexpectedInIntegritySaveDataFileSystemA => new Result.Base(ModuleFs, 5334);
+ /// Error code: 2002-5335; Inner value: 0x29ae02
+ public static Result.Base UnexpectedInJournalIntegritySaveDataFileSystemD => new Result.Base(ModuleFs, 5335);
+ /// Error code: 2002-5336; Inner value: 0x29b002
+ public static Result.Base UnexpectedInAlignmentMatchableFileSystemA => new Result.Base(ModuleFs, 5336);
/// Error code: 2002-6000; Range: 6000-6499; Inner value: 0x2ee002
public static Result.Base PreconditionViolation { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6000, 6499); }
@@ -1993,15 +2001,21 @@ public static class ResultFs
/// Error code: 2002-6462; Inner value: 0x327c02
public static Result.Base GlobalFileDataCacheAlreadyEnabled => new Result.Base(ModuleFs, 6462);
/// The provided file system has already been added to the multi-commit manager.
Error code: 2002-6463; Inner value: 0x327e02
- public static Result.Base MultiCommitHasOverlappingTargets => new Result.Base(ModuleFs, 6463);
+ public static Result.Base MultiCommitFileSystemDuplicated => new Result.Base(ModuleFs, 6463);
/// A multi-commit was performed while another multi-commit operation was already running.
Error code: 2002-6464; Inner value: 0x328002
- public static Result.Base MultiCommitAlreadyInProgress => new Result.Base(ModuleFs, 6464);
+ public static Result.Base SaveDataMultiCommitRepeated => new Result.Base(ModuleFs, 6464);
/// Error code: 2002-6465; Inner value: 0x328202
public static Result.Base UserNotExist => new Result.Base(ModuleFs, 6465);
/// Error code: 2002-6466; Inner value: 0x328402
public static Result.Base DefaultGlobalFileDataCacheEnabled => new Result.Base(ModuleFs, 6466);
/// Error code: 2002-6467; Inner value: 0x328602
public static Result.Base SaveDataRootPathUnavailable => new Result.Base(ModuleFs, 6467);
+ /// Error code: 2002-6470; Inner value: 0x328c02
+ public static Result.Base RomMountDivisionSizeUnitCountLimit => new Result.Base(ModuleFs, 6470);
+ /// Error code: 2002-6471; Inner value: 0x328e02
+ public static Result.Base RomMountCountLimit => new Result.Base(ModuleFs, 6471);
+ /// Error code: 2002-6472; Inner value: 0x329002
+ public static Result.Base AocMountDivisionSizeUnitCountLimit => new Result.Base(ModuleFs, 6472);
/// Error code: 2002-6600; Range: 6600-6699; Inner value: 0x339002
public static Result.Base NotFound { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6600, 6699); }
@@ -2112,8 +2126,8 @@ public static class ResultFs
/// Error code: 2002-7114; Inner value: 0x379402
public static Result.Base InvalidRamDiskSaveDataCoreDataStorageSize => new Result.Base(ModuleFs, 7114);
- /// Error code: 2002-7121; Range: 7121-7139; Inner value: 0x37a202
- public static Result.Base RamDiskDatabaseCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 7121, 7139); }
+ /// Error code: 2002-7121; Range: 7121-7129; Inner value: 0x37a202
+ public static Result.Base RamDiskDatabaseCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 7121, 7129); }
/// Error code: 2002-7122; Inner value: 0x37a402
public static Result.Base InvalidRamDiskAllocationTableBlock => new Result.Base(ModuleFs, 7122);
/// Error code: 2002-7123; Inner value: 0x37a602
diff --git a/src/LibHac/FsSrv/Impl/MultiCommitManager.cs b/src/LibHac/FsSrv/Impl/MultiCommitManager.cs
index 60c9794f..2837c93f 100644
--- a/src/LibHac/FsSrv/Impl/MultiCommitManager.cs
+++ b/src/LibHac/FsSrv/Impl/MultiCommitManager.cs
@@ -120,7 +120,7 @@ internal class MultiCommitManager : IMultiCommitManager
/// : The operation was successful.
/// : The maximum number of file systems have been added.
/// file systems may be added to a single multi-commit.
- /// : The provided file system has already been added.
+ /// : The provided file system has already been added.
public Result Add(ref SharedRef fileSystem)
{
if (_fileSystemCount >= MaxFileSystemCount)
@@ -134,7 +134,7 @@ internal class MultiCommitManager : IMultiCommitManager
for (int i = 0; i < _fileSystemCount; i++)
{
if (ReferenceEquals(fsaFileSystem.Get, _fileSystems[i].Get))
- return ResultFs.MultiCommitHasOverlappingTargets.Log();
+ return ResultFs.MultiCommitFileSystemDuplicated.Log();
}
_fileSystems[_fileSystemCount].SetByMove(ref fsaFileSystem.Ref);
diff --git a/src/LibHac/FsSrv/NcaFileSystemService.cs b/src/LibHac/FsSrv/NcaFileSystemService.cs
index 3912f7a5..400203bd 100644
--- a/src/LibHac/FsSrv/NcaFileSystemService.cs
+++ b/src/LibHac/FsSrv/NcaFileSystemService.cs
@@ -506,13 +506,12 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
ContentStorageId contentStorageId)
{
StorageLayoutType storageFlag = contentStorageId == ContentStorageId.System ? StorageLayoutType.Bis : StorageLayoutType.All;
- using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(storageFlag);
+ using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
Result res = GetProgramInfo(out ProgramInfo programInfo);
if (res.IsFailure()) return res.Miss();
- Accessibility accessibility =
- programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountContentStorage);
+ Accessibility accessibility = programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountContentStorage);
if (!accessibility.CanRead || !accessibility.CanWrite)
return ResultFs.PermissionDenied.Log();
@@ -522,14 +521,10 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
if (res.IsFailure()) return res.Miss();
// Add all the file system wrappers
- using var typeSetFileSystem =
- new SharedRef(new StorageLayoutTypeSetFileSystem(ref fileSystem.Ref, storageFlag));
-
- using var asyncFileSystem =
- new SharedRef(new AsynchronousAccessFileSystem(ref typeSetFileSystem.Ref));
-
- using SharedRef fileSystemAdapter =
- FileSystemInterfaceAdapter.CreateShared(ref asyncFileSystem.Ref, false);
+ using var typeSetFileSystem = new SharedRef(new StorageLayoutTypeSetFileSystem(ref fileSystem.Ref, storageFlag));
+ using var alignmentMatchableFileSystem = new SharedRef(new AlignmentMatchableFileSystem(ref fileSystem.Ref));
+ using var asyncFileSystem = new SharedRef(new AsynchronousAccessFileSystem(ref alignmentMatchableFileSystem.Ref));
+ using SharedRef fileSystemAdapter = FileSystemInterfaceAdapter.CreateShared(ref asyncFileSystem.Ref, allowAllOperations: false);
outFileSystem.SetByMove(ref fileSystemAdapter.Ref);
@@ -544,7 +539,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
if (!programInfo.AccessControl.CanCall(OperationType.RegisterExternalKey))
return ResultFs.PermissionDenied.Log();
- return _serviceImpl.RegisterExternalKey(in rightsId, in accessKey);
+ return _serviceImpl.RegisterExternalKey(in rightsId, in accessKey).Ret();
}
public Result UnregisterExternalKey(in RightsId rightsId)
@@ -555,7 +550,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
if (!programInfo.AccessControl.CanCall(OperationType.RegisterExternalKey))
return ResultFs.PermissionDenied.Log();
- return _serviceImpl.UnregisterExternalKey(in rightsId);
+ return _serviceImpl.UnregisterExternalKey(in rightsId).Ret();
}
public Result UnregisterAllExternalKey()
@@ -584,13 +579,13 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
targetProgramId, programInfo.StorageId);
if (res.IsFailure()) return res.Miss();
- return _serviceImpl.RegisterUpdatePartition(targetProgramId, in programPath, contentAttributes);
+ return _serviceImpl.RegisterUpdatePartition(targetProgramId, in programPath, contentAttributes).Ret();
}
public Result OpenRegisteredUpdatePartition(ref SharedRef outFileSystem)
{
- var storageFlag = StorageLayoutType.All;
- using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(storageFlag);
+ const StorageLayoutType storageFlag = StorageLayoutType.All;
+ using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
Result res = GetProgramInfo(out ProgramInfo programInfo);
if (res.IsFailure()) return res.Miss();
@@ -612,7 +607,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
new SharedRef(new AsynchronousAccessFileSystem(ref typeSetFileSystem.Ref));
using SharedRef fileSystemAdapter =
- FileSystemInterfaceAdapter.CreateShared(ref asyncFileSystem.Ref, false);
+ FileSystemInterfaceAdapter.CreateShared(ref asyncFileSystem.Ref, allowAllOperations: false);
outFileSystem.SetByMove(ref fileSystemAdapter.Ref);
@@ -632,7 +627,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
if (!programInfo.AccessControl.CanCall(OperationType.SetEncryptionSeed))
return ResultFs.PermissionDenied.Log();
- return _serviceImpl.SetSdCardEncryptionSeed(in encryptionSeed);
+ return _serviceImpl.SetSdCardEncryptionSeed(in encryptionSeed).Ret();
}
public Result OpenHostFileSystem(ref SharedRef outFileSystem, ref readonly FspPath path)
@@ -652,7 +647,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
public Result HandleResolubleAccessFailure(out bool wasDeferred, Result nonDeferredResult)
{
- return _serviceImpl.HandleResolubleAccessFailure(out wasDeferred, nonDeferredResult, _processId);
+ return _serviceImpl.HandleResolubleAccessFailure(out wasDeferred, nonDeferredResult, _processId).Ret();
}
Result IRomFileSystemAccessFailureManager.OpenDataStorageCore(ref SharedRef outStorage,
diff --git a/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs b/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs
index 24c214fc..8af41156 100644
--- a/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs
+++ b/src/LibHac/FsSrv/NcaFileSystemServiceImpl.cs
@@ -260,12 +260,17 @@ public class NcaFileSystemServiceImpl : IDisposable
public Result RegisterUpdatePartition(ulong programId, ref readonly Path path, ContentAttributes attributes)
{
- throw new NotImplementedException();
+ return _updatePartitionPath.Set(programId, in path, attributes).Ret();
}
public Result OpenRegisteredUpdatePartition(ref SharedRef outFileSystem)
{
- throw new NotImplementedException();
+ using var path = new Path();
+ Result res = _updatePartitionPath.Get(ref path.Ref(), out ContentAttributes contentAttributes, out ulong updaterProgramId);
+ if (res.IsFailure()) return res.Miss();
+
+ return OpenFileSystem(ref outFileSystem, in path, contentAttributes, FileSystemProxyType.RegisteredUpdate,
+ updaterProgramId, isDirectory: false).Ret();
}
private Result ParseMountName(ref U8Span path, ref SharedRef outFileSystem, out MountInfo outMountInfo)
diff --git a/src/LibHac/FsSystem/AlignmentMatchableFileSystem.cs b/src/LibHac/FsSystem/AlignmentMatchableFileSystem.cs
new file mode 100644
index 00000000..68399b58
--- /dev/null
+++ b/src/LibHac/FsSystem/AlignmentMatchableFileSystem.cs
@@ -0,0 +1,128 @@
+using System;
+using LibHac.Common;
+using LibHac.Diag;
+using LibHac.Fs;
+using LibHac.Fs.Fsa;
+using LibHac.Util;
+
+namespace LibHac.FsSystem;
+
+public class AlignmentMatchingFile : ForwardingFile
+{
+ private readonly OpenMode _openMode;
+
+ public AlignmentMatchingFile(ref UniqueRef baseFile, OpenMode openMode) : base(ref baseFile)
+ {
+ _openMode = openMode;
+ }
+
+ protected override Result DoWrite(long offset, ReadOnlySpan source, in WriteOption option)
+ {
+ Assert.SdkNotNull(BaseFile);
+
+ Result res = DryWrite(out bool needsAppend, offset, source.Length, in option, _openMode);
+ if (res.IsFailure()) return res.Miss();
+
+ if (needsAppend)
+ {
+ res = SetSize(offset + source.Length);
+ if (res.IsFailure()) return res.Miss();
+ }
+
+ res = GetSize(out long fileSize);
+ if (res.IsFailure()) return res.Miss();
+
+ const int writeOffsetAlignment = 0x4000; // All writes to the base file must start on a multiple of this value
+ const int writeSizeAlignment = 0x200; // All writes to the base file must end on a multiple of this value, or end at the end of the file
+
+ // The offset and size of the head and tail blocks will be aligned to this value.
+ // Must be >= writeSizeAlignment and <= writeOffsetAlignment
+ const int blockAlignment = 0x4000;
+
+ long alignedStartOffset = Alignment.AlignDown(offset, writeOffsetAlignment);
+ long endOffset = offset + source.Length;
+ long alignedEndOffset = Math.Min(Alignment.AlignUp(endOffset, writeSizeAlignment), fileSize);
+
+ if (alignedStartOffset == offset && alignedEndOffset == endOffset)
+ {
+ return BaseFile.Get.Write(offset, source, in option).Ret();
+ }
+
+ if (!_openMode.HasFlag(OpenMode.Read))
+ return ResultFs.UnexpectedInAlignmentMatchableFileSystemA.Log();
+
+ long alignedBufferEndOffset = Math.Min(Alignment.AlignUp(endOffset, blockAlignment), fileSize);
+ int pooledBufferSize = (int)(alignedBufferEndOffset - alignedStartOffset);
+
+ Assert.SdkLessEqual(pooledBufferSize, PooledBuffer.GetAllocatableSizeMax());
+
+ using var pooledBuffer = new PooledBuffer();
+ pooledBuffer.Allocate(pooledBufferSize, pooledBufferSize);
+ Assert.SdkNotNull(pooledBuffer.GetBuffer());
+
+ // Read the head block into the buffer if the start offset isn't aligned
+ if (offset != alignedStartOffset)
+ {
+ long headEndOffset = Math.Min(Alignment.AlignUp(offset, blockAlignment), fileSize);
+ int headSize = (int)(headEndOffset - alignedStartOffset);
+ Span headBuffer = pooledBuffer.GetBuffer().Slice(0, headSize);
+
+ res = BaseFile.Get.Read(out long readSizeActual, alignedStartOffset, headBuffer, ReadOption.None);
+ if (res.IsFailure()) return res.Miss();
+
+ if (headSize != readSizeActual)
+ return ResultFs.UnexpectedInAlignmentMatchableFileSystemA.Log();
+ }
+
+ // In the case where the write size is small enough that it's entirely contained in the head block,
+ // we don't need to check if we need to read a tail block
+ bool headBlockCoversEntireBuffer = offset != alignedStartOffset &&
+ alignedBufferEndOffset == Math.Min(Alignment.AlignUp(offset, blockAlignment), fileSize);
+
+ // Read the tail block into the buffer if the end offset isn't aligned
+ if (!headBlockCoversEntireBuffer && endOffset != alignedEndOffset)
+ {
+ long tailStartOffset = Alignment.AlignDown(endOffset, blockAlignment);
+ int tailSize = (int)(alignedBufferEndOffset - tailStartOffset);
+ Span tailBuffer = pooledBuffer.GetBuffer().Slice((int)(tailStartOffset - alignedStartOffset), tailSize);
+
+ res = BaseFile.Get.Read(out long readSizeActual, tailStartOffset, tailBuffer, ReadOption.None);
+ if (res.IsFailure()) return res.Miss();
+
+ if (tailSize != readSizeActual)
+ return ResultFs.UnexpectedInAlignmentMatchableFileSystemA.Log();
+ }
+
+ source.CopyTo(pooledBuffer.GetBuffer().Slice((int)(offset - alignedStartOffset)));
+
+ Span writeBuffer = pooledBuffer.GetBuffer().Slice(0, (int)(alignedEndOffset - alignedStartOffset));
+ res = BaseFile.Get.Write(alignedStartOffset, writeBuffer, in option);
+ if (res.IsFailure()) return res.Miss();
+
+ return Result.Success;
+ }
+}
+
+public class AlignmentMatchableFileSystem : ForwardingFileSystem
+{
+ public AlignmentMatchableFileSystem(ref SharedRef baseFileSystem) : base(ref baseFileSystem) { }
+
+ protected override Result DoOpenFile(ref UniqueRef outFile, ref readonly Path path, OpenMode mode)
+ {
+ using var baseFile = new UniqueRef();
+ Result res = BaseFileSystem.Get.OpenFile(ref baseFile.Ref, in path, mode);
+ if (res.IsFailure()) return res.Miss();
+
+ if (!mode.HasFlag(OpenMode.Read))
+ {
+ using var alignmentMatchingFile = new UniqueRef(new AlignmentMatchingFile(ref baseFile.Ref, mode));
+ outFile.Set(ref alignmentMatchingFile.Ref);
+ }
+ else
+ {
+ outFile.Set(ref baseFile.Ref);
+ }
+
+ return Result.Success;
+ }
+}
\ No newline at end of file
diff --git a/tests/LibHac.Tests/FsSystem/AlignmentMatchingFileTests.cs b/tests/LibHac.Tests/FsSystem/AlignmentMatchingFileTests.cs
new file mode 100644
index 00000000..15c47888
--- /dev/null
+++ b/tests/LibHac.Tests/FsSystem/AlignmentMatchingFileTests.cs
@@ -0,0 +1,53 @@
+using System;
+using LibHac.Common;
+using LibHac.Fs;
+using LibHac.Fs.Fsa;
+using LibHac.FsSystem;
+using LibHac.Tests.Fs;
+using Xunit;
+
+namespace LibHac.Tests.FsSystem;
+
+public class AlignmentMatchingFileTests
+{
+ [Fact]
+ public void ReadWrite_AccessCorrectnessTestAgainstMemoryStorage()
+ {
+ SetupRandomAccessTest().Run(1000);
+ }
+
+ private StorageTester SetupRandomAccessTest()
+ {
+ byte[] fileBuffer = new byte[0x180000];
+ byte[] referenceBuffer = new byte[0x180000];
+
+ fileBuffer.AsSpan().Fill(0x55);
+ referenceBuffer.AsSpan().Fill(0x55);
+
+ var referenceStorage = new MemoryStorage(referenceBuffer);
+
+ using var baseFile = new UniqueRef(new StorageFile(new MemoryStorage(fileBuffer), OpenMode.All));
+ var alignmentFile = new AlignmentMatchingFile(ref baseFile.Ref, OpenMode.All);
+ var alignmentStorage = new FileStorage(alignmentFile);
+
+ var referenceEntry = new StorageTester.Entry(referenceStorage, referenceBuffer);
+ var alignmentEntry = new StorageTester.Entry(alignmentStorage, fileBuffer);
+
+ var testerConfig = new StorageTester.Configuration
+ {
+ Entries = [referenceEntry, alignmentEntry],
+ AccessParams = [
+ new(50, 0x100),
+ new(50, 0x4000),
+ new(50, 0, 0x40000, 0x100, 0x100),
+ new(50, 0, 0x4000, 0x100, 0)
+ ],
+ TaskProbs = [50, 50, 1],
+ AccessTypeProbs = [10, 10, 5],
+ RngSeed = 64972,
+ FrequentAccessBlockCount = 3
+ };
+
+ return new StorageTester(testerConfig);
+ }
+}
\ No newline at end of file