Add AlignmentMatchingFileSystem and update some of the NCA service

This commit is contained in:
Alex Barney 2024-04-21 14:58:32 -07:00
parent c3cc7a69fb
commit 4699825564
8 changed files with 234 additions and 31 deletions

View file

@ -934,6 +934,10 @@ Module,DescriptionStart,DescriptionEnd,Flags,Namespace,Name,Summary
2,5326,,,,UnexpectedInCompressedStorageC, 2,5326,,,,UnexpectedInCompressedStorageC,
2,5327,,,,UnexpectedInCompressedStorageD, 2,5327,,,,UnexpectedInCompressedStorageD,
2,5328,,,,UnexpectedInPathA, 2,5328,,,,UnexpectedInPathA,
2,5333,,,,UnexpectedInSaveDataFileSystemCoreImplA,
2,5334,,,,UnexpectedInIntegritySaveDataFileSystemA,
2,5335,,,,UnexpectedInJournalIntegritySaveDataFileSystemD,
2,5336,,,,UnexpectedInAlignmentMatchableFileSystemA,
2,6000,6499,,,PreconditionViolation, 2,6000,6499,,,PreconditionViolation,
2,6001,6199,,,InvalidArgument, 2,6001,6199,,,InvalidArgument,
@ -1085,11 +1089,14 @@ Module,DescriptionStart,DescriptionEnd,Flags,Namespace,Name,Summary
2,6460,,,,GameCardLogoDataSizeInvalid, 2,6460,,,,GameCardLogoDataSizeInvalid,
2,6461,,,,AllocatorAlignmentViolation, 2,6461,,,,AllocatorAlignmentViolation,
2,6462,,,,GlobalFileDataCacheAlreadyEnabled, 2,6462,,,,GlobalFileDataCacheAlreadyEnabled,
2,6463,,,,MultiCommitHasOverlappingTargets,The provided file system has already been added to the multi-commit manager. 2,6463,,,,MultiCommitFileSystemDuplicated,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,6464,,,,SaveDataMultiCommitRepeated,A multi-commit was performed while another multi-commit operation was already running.
2,6465,,,,UserNotExist, 2,6465,,,,UserNotExist,
2,6466,,,,DefaultGlobalFileDataCacheEnabled, 2,6466,,,,DefaultGlobalFileDataCacheEnabled,
2,6467,,,,SaveDataRootPathUnavailable, 2,6467,,,,SaveDataRootPathUnavailable,
2,6470,,,,RomMountDivisionSizeUnitCountLimit,
2,6471,,,,RomMountCountLimit,
2,6472,,,,AocMountDivisionSizeUnitCountLimit,
2,6600,6699,,,NotFound, 2,6600,6699,,,NotFound,
2,6602,,,,FileNotFound, 2,6602,,,,FileNotFound,
@ -1150,7 +1157,7 @@ Module,DescriptionStart,DescriptionEnd,Flags,Namespace,Name,Summary
2,7113,,,,InvalidRamDiskSaveDataFileReadOffset, 2,7113,,,,InvalidRamDiskSaveDataFileReadOffset,
2,7114,,,,InvalidRamDiskSaveDataCoreDataStorageSize, 2,7114,,,,InvalidRamDiskSaveDataCoreDataStorageSize,
2,7121,7139,,,RamDiskDatabaseCorrupted, 2,7121,7129,,,RamDiskDatabaseCorrupted,
2,7122,,,,InvalidRamDiskAllocationTableBlock, 2,7122,,,,InvalidRamDiskAllocationTableBlock,
2,7123,,,,InvalidRamDiskKeyValueListElementIndex, 2,7123,,,,InvalidRamDiskKeyValueListElementIndex,
2,7124,,,,InvalidRamDiskAllocationTableChainEntry, 2,7124,,,,InvalidRamDiskAllocationTableChainEntry,

1 Module DescriptionStart DescriptionEnd Flags Namespace Name Summary
934 2 6360 6356 UnsupportedOperateRangeForConcatenationFile UnsupportedOperateRangeForStorageFile
935 2 6361 6357 UnsupportedSetSizeForZeroBitmapFile UnsupportedSetSizeForInternalStorageConcatenationFile
936 2 6362 6358 UnsupportedOperateRangeForFileServiceObjectAdapter UnsupportedOperateRangeForInternalStorageConcatenationFile Called OperateRange with an invalid operation ID.
937 2 6359 UnsupportedQueryEntryForConcatenationFileSystem
938 2 6360 UnsupportedOperateRangeForConcatenationFile
939 2 6361 UnsupportedSetSizeForZeroBitmapFile
940 2 6362 UnsupportedOperateRangeForFileServiceObjectAdapter Called OperateRange with an invalid operation ID.
941 2 6363 UnsupportedOperateRangeForAesXtsFile
942 2 6364 UnsupportedWriteForRomFsFileSystem
943 2 6365 UnsupportedCommitProvisionallyForRomFsFileSystem Called RomFsFileSystem::CommitProvisionally.
1089 5 5 2 ContentNotFound PlaceHolderAlreadyExists
1090 5 7 3 ContentMetaNotFound PlaceHolderNotFound
1091 5 8 4 AllocationFailed ContentAlreadyExists
1092 5 12 5 UnknownStorage ContentNotFound
1093 5 100 7 InvalidContentStorage ContentMetaNotFound
1094 5 110 8 InvalidContentMetaDatabase AllocationFailed
1095 5 130 12 InvalidPackageFormat UnknownStorage
1096 5 140 100 InvalidContentHash InvalidContentStorage
1097 5 110 InvalidContentMetaDatabase
1098 5 130 InvalidPackageFormat
1099 5 140 InvalidContentHash
1100 5 160 InvalidInstallTaskState
1101 5 170 InvalidPlaceHolderFile
1102 5 180 BufferInsufficient
1157 9 7 TooManyProcesses
1158 9 8 NotPinned
1159 9 9 InvalidProgramId
1160 9 10 InvalidVersion
1161 9 11 InvalidAcidSignature
1162 9 12 InvalidNcaSignature
1163 9 51 InsufficientAddressSpace

View file

@ -28,7 +28,8 @@ public enum ContentStorageId
{ {
System = 0, System = 0,
User = 1, User = 1,
SdCard = 2 SdCard = 2,
System0 = 3
} }
public enum GameCardPartition public enum GameCardPartition

View file

@ -1699,6 +1699,14 @@ public static class ResultFs
public static Result.Base UnexpectedInCompressedStorageD => new Result.Base(ModuleFs, 5327); public static Result.Base UnexpectedInCompressedStorageD => new Result.Base(ModuleFs, 5327);
/// <summary>Error code: 2002-5328; Inner value: 0x29a002</summary> /// <summary>Error code: 2002-5328; Inner value: 0x29a002</summary>
public static Result.Base UnexpectedInPathA => new Result.Base(ModuleFs, 5328); public static Result.Base UnexpectedInPathA => new Result.Base(ModuleFs, 5328);
/// <summary>Error code: 2002-5333; Inner value: 0x29aa02</summary>
public static Result.Base UnexpectedInSaveDataFileSystemCoreImplA => new Result.Base(ModuleFs, 5333);
/// <summary>Error code: 2002-5334; Inner value: 0x29ac02</summary>
public static Result.Base UnexpectedInIntegritySaveDataFileSystemA => new Result.Base(ModuleFs, 5334);
/// <summary>Error code: 2002-5335; Inner value: 0x29ae02</summary>
public static Result.Base UnexpectedInJournalIntegritySaveDataFileSystemD => new Result.Base(ModuleFs, 5335);
/// <summary>Error code: 2002-5336; Inner value: 0x29b002</summary>
public static Result.Base UnexpectedInAlignmentMatchableFileSystemA => new Result.Base(ModuleFs, 5336);
/// <summary>Error code: 2002-6000; Range: 6000-6499; Inner value: 0x2ee002</summary> /// <summary>Error code: 2002-6000; Range: 6000-6499; Inner value: 0x2ee002</summary>
public static Result.Base PreconditionViolation { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6000, 6499); } public static Result.Base PreconditionViolation { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6000, 6499); }
@ -1993,15 +2001,21 @@ public static class ResultFs
/// <summary>Error code: 2002-6462; Inner value: 0x327c02</summary> /// <summary>Error code: 2002-6462; Inner value: 0x327c02</summary>
public static Result.Base GlobalFileDataCacheAlreadyEnabled => new Result.Base(ModuleFs, 6462); public static Result.Base GlobalFileDataCacheAlreadyEnabled => new Result.Base(ModuleFs, 6462);
/// <summary>The provided file system has already been added to the multi-commit manager.<br/>Error code: 2002-6463; Inner value: 0x327e02</summary> /// <summary>The provided file system has already been added to the multi-commit manager.<br/>Error code: 2002-6463; Inner value: 0x327e02</summary>
public static Result.Base MultiCommitHasOverlappingTargets => new Result.Base(ModuleFs, 6463); public static Result.Base MultiCommitFileSystemDuplicated => new Result.Base(ModuleFs, 6463);
/// <summary>A multi-commit was performed while another multi-commit operation was already running.<br/>Error code: 2002-6464; Inner value: 0x328002</summary> /// <summary>A multi-commit was performed while another multi-commit operation was already running.<br/>Error code: 2002-6464; Inner value: 0x328002</summary>
public static Result.Base MultiCommitAlreadyInProgress => new Result.Base(ModuleFs, 6464); public static Result.Base SaveDataMultiCommitRepeated => new Result.Base(ModuleFs, 6464);
/// <summary>Error code: 2002-6465; Inner value: 0x328202</summary> /// <summary>Error code: 2002-6465; Inner value: 0x328202</summary>
public static Result.Base UserNotExist => new Result.Base(ModuleFs, 6465); public static Result.Base UserNotExist => new Result.Base(ModuleFs, 6465);
/// <summary>Error code: 2002-6466; Inner value: 0x328402</summary> /// <summary>Error code: 2002-6466; Inner value: 0x328402</summary>
public static Result.Base DefaultGlobalFileDataCacheEnabled => new Result.Base(ModuleFs, 6466); public static Result.Base DefaultGlobalFileDataCacheEnabled => new Result.Base(ModuleFs, 6466);
/// <summary>Error code: 2002-6467; Inner value: 0x328602</summary> /// <summary>Error code: 2002-6467; Inner value: 0x328602</summary>
public static Result.Base SaveDataRootPathUnavailable => new Result.Base(ModuleFs, 6467); public static Result.Base SaveDataRootPathUnavailable => new Result.Base(ModuleFs, 6467);
/// <summary>Error code: 2002-6470; Inner value: 0x328c02</summary>
public static Result.Base RomMountDivisionSizeUnitCountLimit => new Result.Base(ModuleFs, 6470);
/// <summary>Error code: 2002-6471; Inner value: 0x328e02</summary>
public static Result.Base RomMountCountLimit => new Result.Base(ModuleFs, 6471);
/// <summary>Error code: 2002-6472; Inner value: 0x329002</summary>
public static Result.Base AocMountDivisionSizeUnitCountLimit => new Result.Base(ModuleFs, 6472);
/// <summary>Error code: 2002-6600; Range: 6600-6699; Inner value: 0x339002</summary> /// <summary>Error code: 2002-6600; Range: 6600-6699; Inner value: 0x339002</summary>
public static Result.Base NotFound { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6600, 6699); } public static Result.Base NotFound { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 6600, 6699); }
@ -2112,8 +2126,8 @@ public static class ResultFs
/// <summary>Error code: 2002-7114; Inner value: 0x379402</summary> /// <summary>Error code: 2002-7114; Inner value: 0x379402</summary>
public static Result.Base InvalidRamDiskSaveDataCoreDataStorageSize => new Result.Base(ModuleFs, 7114); public static Result.Base InvalidRamDiskSaveDataCoreDataStorageSize => new Result.Base(ModuleFs, 7114);
/// <summary>Error code: 2002-7121; Range: 7121-7139; Inner value: 0x37a202</summary> /// <summary>Error code: 2002-7121; Range: 7121-7129; Inner value: 0x37a202</summary>
public static Result.Base RamDiskDatabaseCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 7121, 7139); } public static Result.Base RamDiskDatabaseCorrupted { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new Result.Base(ModuleFs, 7121, 7129); }
/// <summary>Error code: 2002-7122; Inner value: 0x37a402</summary> /// <summary>Error code: 2002-7122; Inner value: 0x37a402</summary>
public static Result.Base InvalidRamDiskAllocationTableBlock => new Result.Base(ModuleFs, 7122); public static Result.Base InvalidRamDiskAllocationTableBlock => new Result.Base(ModuleFs, 7122);
/// <summary>Error code: 2002-7123; Inner value: 0x37a602</summary> /// <summary>Error code: 2002-7123; Inner value: 0x37a602</summary>

View file

@ -120,7 +120,7 @@ internal class MultiCommitManager : IMultiCommitManager
/// <returns><see cref="Result.Success"/>: The operation was successful.<br/> /// <returns><see cref="Result.Success"/>: The operation was successful.<br/>
/// <see cref="ResultFs.MultiCommitFileSystemLimit"/>: The maximum number of file systems have been added. /// <see cref="ResultFs.MultiCommitFileSystemLimit"/>: The maximum number of file systems have been added.
/// <see cref="MaxFileSystemCount"/> file systems may be added to a single multi-commit.<br/> /// <see cref="MaxFileSystemCount"/> file systems may be added to a single multi-commit.<br/>
/// <see cref="ResultFs.MultiCommitHasOverlappingTargets"/>: The provided file system has already been added.</returns> /// <see cref="ResultFs.MultiCommitFileSystemDuplicated"/>: The provided file system has already been added.</returns>
public Result Add(ref SharedRef<IFileSystemSf> fileSystem) public Result Add(ref SharedRef<IFileSystemSf> fileSystem)
{ {
if (_fileSystemCount >= MaxFileSystemCount) if (_fileSystemCount >= MaxFileSystemCount)
@ -134,7 +134,7 @@ internal class MultiCommitManager : IMultiCommitManager
for (int i = 0; i < _fileSystemCount; i++) for (int i = 0; i < _fileSystemCount; i++)
{ {
if (ReferenceEquals(fsaFileSystem.Get, _fileSystems[i].Get)) if (ReferenceEquals(fsaFileSystem.Get, _fileSystems[i].Get))
return ResultFs.MultiCommitHasOverlappingTargets.Log(); return ResultFs.MultiCommitFileSystemDuplicated.Log();
} }
_fileSystems[_fileSystemCount].SetByMove(ref fsaFileSystem.Ref); _fileSystems[_fileSystemCount].SetByMove(ref fsaFileSystem.Ref);

View file

@ -506,13 +506,12 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
ContentStorageId contentStorageId) ContentStorageId contentStorageId)
{ {
StorageLayoutType storageFlag = contentStorageId == ContentStorageId.System ? StorageLayoutType.Bis : StorageLayoutType.All; 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); Result res = GetProgramInfo(out ProgramInfo programInfo);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
Accessibility accessibility = Accessibility accessibility = programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountContentStorage);
programInfo.AccessControl.GetAccessibilityFor(AccessibilityType.MountContentStorage);
if (!accessibility.CanRead || !accessibility.CanWrite) if (!accessibility.CanRead || !accessibility.CanWrite)
return ResultFs.PermissionDenied.Log(); return ResultFs.PermissionDenied.Log();
@ -522,14 +521,10 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
// Add all the file system wrappers // Add all the file system wrappers
using var typeSetFileSystem = using var typeSetFileSystem = new SharedRef<IFileSystem>(new StorageLayoutTypeSetFileSystem(ref fileSystem.Ref, storageFlag));
new SharedRef<IFileSystem>(new StorageLayoutTypeSetFileSystem(ref fileSystem.Ref, storageFlag)); using var alignmentMatchableFileSystem = new SharedRef<IFileSystem>(new AlignmentMatchableFileSystem(ref fileSystem.Ref));
using var asyncFileSystem = new SharedRef<IFileSystem>(new AsynchronousAccessFileSystem(ref alignmentMatchableFileSystem.Ref));
using var asyncFileSystem = using SharedRef<IFileSystemSf> fileSystemAdapter = FileSystemInterfaceAdapter.CreateShared(ref asyncFileSystem.Ref, allowAllOperations: false);
new SharedRef<IFileSystem>(new AsynchronousAccessFileSystem(ref typeSetFileSystem.Ref));
using SharedRef<IFileSystemSf> fileSystemAdapter =
FileSystemInterfaceAdapter.CreateShared(ref asyncFileSystem.Ref, false);
outFileSystem.SetByMove(ref fileSystemAdapter.Ref); outFileSystem.SetByMove(ref fileSystemAdapter.Ref);
@ -544,7 +539,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
if (!programInfo.AccessControl.CanCall(OperationType.RegisterExternalKey)) if (!programInfo.AccessControl.CanCall(OperationType.RegisterExternalKey))
return ResultFs.PermissionDenied.Log(); 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) public Result UnregisterExternalKey(in RightsId rightsId)
@ -555,7 +550,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
if (!programInfo.AccessControl.CanCall(OperationType.RegisterExternalKey)) if (!programInfo.AccessControl.CanCall(OperationType.RegisterExternalKey))
return ResultFs.PermissionDenied.Log(); return ResultFs.PermissionDenied.Log();
return _serviceImpl.UnregisterExternalKey(in rightsId); return _serviceImpl.UnregisterExternalKey(in rightsId).Ret();
} }
public Result UnregisterAllExternalKey() public Result UnregisterAllExternalKey()
@ -584,13 +579,13 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
targetProgramId, programInfo.StorageId); targetProgramId, programInfo.StorageId);
if (res.IsFailure()) return res.Miss(); 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<IFileSystemSf> outFileSystem) public Result OpenRegisteredUpdatePartition(ref SharedRef<IFileSystemSf> outFileSystem)
{ {
var storageFlag = StorageLayoutType.All; const StorageLayoutType storageFlag = StorageLayoutType.All;
using var scopedLayoutType = new ScopedStorageLayoutTypeSetter(storageFlag); using var scopedContext = new ScopedStorageLayoutTypeSetter(storageFlag);
Result res = GetProgramInfo(out ProgramInfo programInfo); Result res = GetProgramInfo(out ProgramInfo programInfo);
if (res.IsFailure()) return res.Miss(); if (res.IsFailure()) return res.Miss();
@ -612,7 +607,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
new SharedRef<IFileSystem>(new AsynchronousAccessFileSystem(ref typeSetFileSystem.Ref)); new SharedRef<IFileSystem>(new AsynchronousAccessFileSystem(ref typeSetFileSystem.Ref));
using SharedRef<IFileSystemSf> fileSystemAdapter = using SharedRef<IFileSystemSf> fileSystemAdapter =
FileSystemInterfaceAdapter.CreateShared(ref asyncFileSystem.Ref, false); FileSystemInterfaceAdapter.CreateShared(ref asyncFileSystem.Ref, allowAllOperations: false);
outFileSystem.SetByMove(ref fileSystemAdapter.Ref); outFileSystem.SetByMove(ref fileSystemAdapter.Ref);
@ -632,7 +627,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
if (!programInfo.AccessControl.CanCall(OperationType.SetEncryptionSeed)) if (!programInfo.AccessControl.CanCall(OperationType.SetEncryptionSeed))
return ResultFs.PermissionDenied.Log(); return ResultFs.PermissionDenied.Log();
return _serviceImpl.SetSdCardEncryptionSeed(in encryptionSeed); return _serviceImpl.SetSdCardEncryptionSeed(in encryptionSeed).Ret();
} }
public Result OpenHostFileSystem(ref SharedRef<IFileSystemSf> outFileSystem, ref readonly FspPath path) public Result OpenHostFileSystem(ref SharedRef<IFileSystemSf> outFileSystem, ref readonly FspPath path)
@ -652,7 +647,7 @@ internal class NcaFileSystemService : IRomFileSystemAccessFailureManager
public Result HandleResolubleAccessFailure(out bool wasDeferred, Result nonDeferredResult) 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<IStorage> outStorage, Result IRomFileSystemAccessFailureManager.OpenDataStorageCore(ref SharedRef<IStorage> outStorage,

View file

@ -260,12 +260,17 @@ public class NcaFileSystemServiceImpl : IDisposable
public Result RegisterUpdatePartition(ulong programId, ref readonly Path path, ContentAttributes attributes) 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<IFileSystem> outFileSystem) public Result OpenRegisteredUpdatePartition(ref SharedRef<IFileSystem> 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<IFileSystem> outFileSystem, out MountInfo outMountInfo) private Result ParseMountName(ref U8Span path, ref SharedRef<IFileSystem> outFileSystem, out MountInfo outMountInfo)

View file

@ -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<IFile> baseFile, OpenMode openMode) : base(ref baseFile)
{
_openMode = openMode;
}
protected override Result DoWrite(long offset, ReadOnlySpan<byte> 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<byte> 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<byte> 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<byte> 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<IFileSystem> baseFileSystem) : base(ref baseFileSystem) { }
protected override Result DoOpenFile(ref UniqueRef<IFile> outFile, ref readonly Path path, OpenMode mode)
{
using var baseFile = new UniqueRef<IFile>();
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<IFile>(new AlignmentMatchingFile(ref baseFile.Ref, mode));
outFile.Set(ref alignmentMatchingFile.Ref);
}
else
{
outFile.Set(ref baseFile.Ref);
}
return Result.Success;
}
}

View file

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