Implement MemoryStorageCreator and NspRootFileSystemCreator

This commit is contained in:
Alex Barney 2024-07-16 20:47:31 -07:00
parent 3f845e2964
commit dc6f4fa489
5 changed files with 205 additions and 2 deletions

View file

@ -75,6 +75,94 @@ public class MemoryStorage : IStorage
return Result.Success;
}
public override Result OperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size,
ReadOnlySpan<byte> inBuffer)
{
switch (operationId)
{
case OperationId.InvalidateCache:
return Result.Success;
case OperationId.QueryRange:
if (outBuffer.Length != Unsafe.SizeOf<QueryRangeInfo>())
return ResultFs.InvalidSize.Log();
Unsafe.As<byte, QueryRangeInfo>(ref MemoryMarshal.GetReference(outBuffer)).Clear();
return Result.Success;
default:
return ResultFs.UnsupportedOperateRangeForMemoryStorage.Log();
}
}
}
/// <summary>
/// Allows interacting with a <see cref="Memory{T}"/> via the <see cref="IStorage"/> interface.
/// </summary>
/// <remarks>Based on nnSdk 16.2.0 (FS 16.0.0)</remarks>
internal class MemoryStorageFromMemory : IStorage
{
private Memory<byte> _buffer;
private int _size;
public MemoryStorageFromMemory(Memory<byte> buffer)
{
_buffer = buffer;
_size = buffer.Length;
}
public MemoryStorageFromMemory(Memory<byte> buffer, int size)
{
Assert.SdkRequiresInRange(size, 0, buffer.Length);
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
Abort.DoAbortUnless(0 <= size && size < buffer.Length);
_buffer = buffer;
_size = size;
}
public override Result Read(long offset, Span<byte> destination)
{
if (destination.Length == 0)
return Result.Success;
Result res = CheckAccessRange(offset, destination.Length, _size);
if (res.IsFailure()) return res.Miss();
_buffer.Span.Slice((int)offset, destination.Length).CopyTo(destination);
return Result.Success;
}
public override Result Write(long offset, ReadOnlySpan<byte> source)
{
if (source.Length == 0)
return Result.Success;
Result res = CheckAccessRange(offset, source.Length, _size);
if (res.IsFailure()) return res.Miss();
source.CopyTo(_buffer.Span.Slice((int)offset));
return Result.Success;
}
public override Result Flush()
{
return Result.Success;
}
public override Result SetSize(long size)
{
return ResultFs.UnsupportedSetSizeForMemoryStorage.Log();
}
public override Result GetSize(out long size)
{
size = _size;
return Result.Success;
}
public override Result OperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size,
ReadOnlySpan<byte> inBuffer)
{

View file

@ -1,10 +1,20 @@
using System;
using LibHac.Common;
using LibHac.Fs;
namespace LibHac.FsSrv.FsCreator;
public interface IMemoryStorageCreator
{
Result Create(out IStorage storage, out Memory<byte> buffer, int storageId);
Result RegisterBuffer(int storageId, Memory<byte> buffer);
public enum MemoryStorageId
{
UserPartitionFatFs,
SignedSystemPartitionRaw,
SystemPartitionFatFs,
Id4,
Count
}
Result Create(ref SharedRef<IStorage> outStorage, out Memory<byte> outBuffer, MemoryStorageId id);
Result RegisterBuffer(MemoryStorageId id, Memory<byte> buffer);
}

View file

@ -4,6 +4,10 @@ using LibHac.Fs.Fsa;
namespace LibHac.FsSrv.FsCreator;
/// <summary>
/// Creates an <see cref="IFileSystem"/> from an <see cref="IStorage"/> containing a partition filesystem of version 0 or version 1.
/// </summary>
/// <remarks>Based on nnSdk 18.3.0 (FS 18.0.0)</remarks>
public interface INspRootFileSystemCreator
{
Result Create(ref SharedRef<IFileSystem> outFileSystem, ref readonly SharedRef<IStorage> baseStorage);

View file

@ -0,0 +1,78 @@
using System;
using LibHac.Common;
using LibHac.Common.FixedArrays;
using LibHac.Diag;
using LibHac.Fs;
using LibHac.Os;
namespace LibHac.FsSrv.FsCreator;
/// <summary>
/// Creates <see cref="MemoryStorage"/>s from registered memory buffers.
/// </summary>
/// <remarks>
/// <para>Used for in-memory System and User partitions when booting in safe mode.
/// On startup, FS registers buffers which can be used later if needed.</para>
/// <para>Based on nnSdk 18.3.0 (FS 18.0.0)</para>
/// </remarks>
public class MemoryStorageCreator : IMemoryStorageCreator
{
private SdkMutexType _mutex;
private Array4<Buffer> _bufferArray;
private struct Buffer
{
public Memory<byte> MemoryBuffer;
public bool IsInUse; // Each registered buffer can only be used to create a MemoryStorage a single time
public Buffer()
{
MemoryBuffer = default;
IsInUse = false;
}
}
public MemoryStorageCreator()
{
_mutex = new SdkMutexType();
for (int i = 0; i < _bufferArray.Length; i++)
{
_bufferArray[i] = new Buffer();
}
}
public Result Create(ref SharedRef<IStorage> outStorage, out Memory<byte> outBuffer, IMemoryStorageCreator.MemoryStorageId id)
{
UnsafeHelpers.SkipParamInit(out outBuffer);
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
ref Buffer buffer = ref _bufferArray[(int)id];
if (buffer.IsInUse)
return ResultFs.AllocationMemoryFailed.Log();
if (buffer.MemoryBuffer.IsEmpty)
return ResultFs.AllocationMemoryFailed.Log();
using var storage = new SharedRef<MemoryStorageFromMemory>(new MemoryStorageFromMemory(buffer.MemoryBuffer));
buffer.MemoryBuffer.Span.Clear();
outStorage.SetByMove(ref storage.Ref);
outBuffer = buffer.MemoryBuffer;
buffer.IsInUse = true;
return Result.Success;
}
public Result RegisterBuffer(IMemoryStorageCreator.MemoryStorageId id, Memory<byte> buffer)
{
Assert.SdkAssert(id < IMemoryStorageCreator.MemoryStorageId.Count);
Assert.SdkAssert(_bufferArray[(int)id].MemoryBuffer.IsEmpty);
_bufferArray[(int)id].MemoryBuffer = buffer;
return Result.Success;
}
}

View file

@ -0,0 +1,23 @@
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem;
namespace LibHac.FsSrv.FsCreator;
/// <inheritdoc cref="INspRootFileSystemCreator"/>
public class NspRootFileSystemCreator : INspRootFileSystemCreator
{
public Result Create(ref SharedRef<IFileSystem> outFileSystem, ref readonly SharedRef<IStorage> baseStorage)
{
using var nspFs = new SharedRef<NintendoSubmissionPackageRootFileSystem>(new NintendoSubmissionPackageRootFileSystem());
if (!nspFs.HasValue)
return ResultFs.AllocationMemoryFailedInPartitionFileSystemCreatorA.Log();
Result res = nspFs.Get.Initialize(in baseStorage);
if (res.IsFailure()) return res.Miss();
outFileSystem.SetByMove(ref nspFs.Ref);
return Result.Success;
}
}