mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Ensure more FS classes are updated for 13.1.0
- Add some PooledBuffer global functions - FsSrv.SaveDataInfoFilter - FsSrv.SaveDataInfoFilterReader - FsSrv.StatusReportService - FsSrv.StatusReportServiceImpl - FsSrv.TimeService - FsSrv.TimeServiceImpl - FsSystem.DefaultAsynchronousAccessSplitter - FsSystem.IAsynchronousAccessSplitter - FsSystem.StorageFile
This commit is contained in:
parent
2c2fed445f
commit
32ffda1d3f
9 changed files with 333 additions and 197 deletions
|
@ -1,5 +1,6 @@
|
|||
using LibHac.FsSrv.Impl;
|
||||
using LibHac.FsSrv.Storage;
|
||||
using LibHac.FsSystem;
|
||||
|
||||
namespace LibHac.FsSrv;
|
||||
|
||||
|
@ -34,6 +35,7 @@ internal struct FileSystemServerGlobals
|
|||
public SaveDataSharedFileStorageGlobals SaveDataSharedFileStorage;
|
||||
public MultiCommitManagerGlobals MultiCommitManager;
|
||||
public LocationResolverSetGlobals LocationResolverSet;
|
||||
public PooledBufferGlobals PooledBuffer;
|
||||
|
||||
public void Initialize(HorizonClient horizonClient, FileSystemServer fsServer)
|
||||
{
|
||||
|
@ -43,6 +45,7 @@ internal struct FileSystemServerGlobals
|
|||
SaveDataSharedFileStorage.Initialize(fsServer);
|
||||
MultiCommitManager.Initialize();
|
||||
LocationResolverSet.Initialize();
|
||||
PooledBuffer.Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -150,8 +150,8 @@ public static class FileSystemServerInitializer
|
|||
var saveFsService = new SaveDataFileSystemServiceImpl(in saveFsServiceConfig);
|
||||
|
||||
var statusReportServiceConfig = new StatusReportServiceImpl.Configuration();
|
||||
statusReportServiceConfig.NcaFsServiceImpl = ncaFsService;
|
||||
statusReportServiceConfig.SaveFsServiceImpl = saveFsService;
|
||||
statusReportServiceConfig.NcaFileSystemServiceImpl = ncaFsService;
|
||||
statusReportServiceConfig.SaveDataFileSystemServiceImpl = saveFsService;
|
||||
statusReportServiceConfig.BufferManagerMemoryReport = null;
|
||||
statusReportServiceConfig.ExpHeapMemoryReport = null;
|
||||
statusReportServiceConfig.BufferPoolMemoryReport = null;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
|
@ -8,6 +9,108 @@ using LibHac.Util;
|
|||
|
||||
namespace LibHac.FsSrv;
|
||||
|
||||
/// <summary>
|
||||
/// Contains filter parameters for <see cref="SaveDataInfo"/> and can check
|
||||
/// to see if a <see cref="SaveDataInfo"/> matches those parameters.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
internal struct SaveDataInfoFilter
|
||||
{
|
||||
private Optional<SaveDataSpaceId> _spaceId;
|
||||
private Optional<ProgramId> _programId;
|
||||
private Optional<SaveDataType> _saveDataType;
|
||||
private Optional<UserId> _userId;
|
||||
private Optional<ulong> _saveDataId;
|
||||
private Optional<ushort> _index;
|
||||
private int _rank;
|
||||
|
||||
public SaveDataInfoFilter(in SaveDataInfoFilter filter)
|
||||
{
|
||||
this = filter;
|
||||
}
|
||||
|
||||
public SaveDataInfoFilter(SaveDataSpaceId spaceId, in SaveDataFilter filter)
|
||||
{
|
||||
// Start out with no optional values
|
||||
this = default;
|
||||
|
||||
_spaceId = new Optional<SaveDataSpaceId>(spaceId);
|
||||
_rank = (int)filter.Rank;
|
||||
|
||||
if (filter.FilterByProgramId)
|
||||
{
|
||||
_programId = new Optional<ProgramId>(filter.Attribute.ProgramId);
|
||||
}
|
||||
|
||||
if (filter.FilterBySaveDataType)
|
||||
{
|
||||
_saveDataType = new Optional<SaveDataType>(filter.Attribute.Type);
|
||||
}
|
||||
|
||||
if (filter.FilterByUserId)
|
||||
{
|
||||
_userId = new Optional<UserId>(in filter.Attribute.UserId);
|
||||
}
|
||||
|
||||
if (filter.FilterBySaveDataId)
|
||||
{
|
||||
_saveDataId = new Optional<ulong>(filter.Attribute.StaticSaveDataId);
|
||||
}
|
||||
|
||||
if (filter.FilterByIndex)
|
||||
{
|
||||
_index = new Optional<ushort>(filter.Attribute.Index);
|
||||
}
|
||||
}
|
||||
|
||||
public SaveDataInfoFilter(Optional<SaveDataSpaceId> spaceId, Optional<ProgramId> programId,
|
||||
Optional<SaveDataType> saveDataType, Optional<UserId> userId, Optional<ulong> saveDataId,
|
||||
Optional<ushort> index, int rank)
|
||||
{
|
||||
_spaceId = spaceId;
|
||||
_programId = programId;
|
||||
_saveDataType = saveDataType;
|
||||
_userId = userId;
|
||||
_saveDataId = saveDataId;
|
||||
_index = index;
|
||||
_rank = rank;
|
||||
}
|
||||
|
||||
public bool Includes(in SaveDataInfo saveInfo)
|
||||
{
|
||||
if (_spaceId.HasValue && saveInfo.SpaceId != _spaceId.Value)
|
||||
return false;
|
||||
|
||||
if (_programId.HasValue && saveInfo.ProgramId != _programId.Value)
|
||||
return false;
|
||||
|
||||
if (_saveDataType.HasValue && saveInfo.Type != _saveDataType.Value)
|
||||
return false;
|
||||
|
||||
if (_userId.HasValue && saveInfo.UserId != _userId.Value)
|
||||
return false;
|
||||
|
||||
if (_saveDataId.HasValue && saveInfo.SaveDataId != _saveDataId.Value)
|
||||
return false;
|
||||
|
||||
if (_index.HasValue && saveInfo.Index != _index.Value)
|
||||
return false;
|
||||
|
||||
var filterRank = (SaveDataRank)(_rank & 1);
|
||||
|
||||
// When filtering by secondary rank, match on both primary and secondary ranks
|
||||
if (filterRank == SaveDataRank.Primary && saveInfo.Rank == SaveDataRank.Secondary)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wraps a <see cref="SaveDataInfoReaderImpl"/> and only allows <see cref="SaveDataInfo"/>
|
||||
/// that match a provided <see cref="SaveDataInfoFilter"/> to be returned.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
internal class SaveDataInfoFilterReader : SaveDataInfoReaderImpl
|
||||
{
|
||||
private SharedRef<SaveDataInfoReaderImpl> _reader;
|
||||
|
@ -19,146 +122,35 @@ internal class SaveDataInfoFilterReader : SaveDataInfoReaderImpl
|
|||
_infoFilter = infoFilter;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_reader.Destroy();
|
||||
}
|
||||
|
||||
[SkipLocalsInit]
|
||||
public Result Read(out long readCount, OutBuffer saveDataInfoBuffer)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out readCount);
|
||||
|
||||
Span<SaveDataInfo> outInfo = MemoryMarshal.Cast<byte, SaveDataInfo>(saveDataInfoBuffer.Buffer);
|
||||
|
||||
SaveDataInfo tempInfo = default;
|
||||
Span<byte> tempInfoBytes = SpanHelpers.AsByteSpan(ref tempInfo);
|
||||
|
||||
SaveDataInfoReaderImpl reader = _reader.Get;
|
||||
int count = 0;
|
||||
|
||||
while (count < outInfo.Length)
|
||||
{
|
||||
Result rc = reader.Read(out long baseReadCount, new OutBuffer(tempInfoBytes));
|
||||
Unsafe.SkipInit(out SaveDataInfo info);
|
||||
Result rc = _reader.Get.Read(out long baseReadCount, OutBuffer.FromStruct(ref info));
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (baseReadCount == 0) break;
|
||||
|
||||
if (_infoFilter.Includes(in tempInfo))
|
||||
if (_infoFilter.Includes(in info))
|
||||
{
|
||||
outInfo[count] = tempInfo;
|
||||
|
||||
outInfo[count] = info;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
readCount = count;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_reader.Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
internal struct SaveDataInfoFilter
|
||||
{
|
||||
public Optional<SaveDataSpaceId> SpaceId;
|
||||
public Optional<ProgramId> ProgramId;
|
||||
public Optional<SaveDataType> SaveDataType;
|
||||
public Optional<UserId> UserId;
|
||||
public Optional<ulong> SaveDataId;
|
||||
public Optional<ushort> Index;
|
||||
public int Rank;
|
||||
|
||||
public SaveDataInfoFilter(in SaveDataInfoFilter filter)
|
||||
{
|
||||
this = filter;
|
||||
}
|
||||
|
||||
public SaveDataInfoFilter(SaveDataSpaceId spaceId, in SaveDataFilter filter)
|
||||
{
|
||||
// Start out with no optional values
|
||||
this = new SaveDataInfoFilter();
|
||||
|
||||
SpaceId = new Optional<SaveDataSpaceId>(spaceId);
|
||||
Rank = (int)filter.Rank;
|
||||
|
||||
if (filter.FilterByProgramId)
|
||||
{
|
||||
ProgramId = new Optional<ProgramId>(in filter.Attribute.ProgramId);
|
||||
}
|
||||
|
||||
if (filter.FilterBySaveDataType)
|
||||
{
|
||||
SaveDataType = new Optional<SaveDataType>(in filter.Attribute.Type);
|
||||
}
|
||||
|
||||
if (filter.FilterByUserId)
|
||||
{
|
||||
UserId = new Optional<UserId>(in filter.Attribute.UserId);
|
||||
}
|
||||
|
||||
if (filter.FilterBySaveDataId)
|
||||
{
|
||||
SaveDataId = new Optional<ulong>(in filter.Attribute.StaticSaveDataId);
|
||||
}
|
||||
|
||||
if (filter.FilterByIndex)
|
||||
{
|
||||
Index = new Optional<ushort>(in filter.Attribute.Index);
|
||||
}
|
||||
}
|
||||
|
||||
public SaveDataInfoFilter(Optional<SaveDataSpaceId> spaceId, Optional<ProgramId> programId,
|
||||
Optional<SaveDataType> saveDataType, Optional<UserId> userId, Optional<ulong> saveDataId,
|
||||
Optional<ushort> index, int rank)
|
||||
{
|
||||
SpaceId = spaceId;
|
||||
ProgramId = programId;
|
||||
SaveDataType = saveDataType;
|
||||
UserId = userId;
|
||||
SaveDataId = saveDataId;
|
||||
Index = index;
|
||||
Rank = rank;
|
||||
}
|
||||
|
||||
public bool Includes(in SaveDataInfo saveInfo)
|
||||
{
|
||||
if (SpaceId.HasValue && saveInfo.SpaceId != SpaceId.Value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ProgramId.HasValue && saveInfo.ProgramId != ProgramId.Value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SaveDataType.HasValue && saveInfo.Type != SaveDataType.Value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (UserId.HasValue && saveInfo.UserId != UserId.Value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SaveDataId.HasValue && saveInfo.SaveDataId != SaveDataId.Value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Index.HasValue && saveInfo.Index != Index.Value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var filterRank = (SaveDataRank)(Rank & 1);
|
||||
|
||||
// When filtering by secondary rank, match on both primary and secondary ranks
|
||||
if (filterRank == SaveDataRank.Primary && saveInfo.Rank == SaveDataRank.Secondary)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,16 @@
|
|||
using LibHac.Diag;
|
||||
using LibHac.Fs;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.Os;
|
||||
|
||||
namespace LibHac.FsSrv;
|
||||
|
||||
/// <summary>
|
||||
/// Handles status-report-related calls for <see cref="FileSystemProxyImpl"/>.
|
||||
/// </summary>
|
||||
/// <remarks><para>This struct handles forwarding calls to the <see cref="StatusReportServiceImpl"/> object.
|
||||
/// No permissions are needed to call any of this struct's functions.</para>
|
||||
/// <para>Based on FS 13.1.0 (nnSdk 13.4.0)</para></remarks>
|
||||
public readonly struct StatusReportService
|
||||
{
|
||||
private readonly StatusReportServiceImpl _serviceImpl;
|
||||
|
@ -30,6 +37,10 @@ public readonly struct StatusReportService
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manages getting and resetting various status reports and statistics about parts of the FS service.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
public class StatusReportServiceImpl
|
||||
{
|
||||
private Configuration _config;
|
||||
|
@ -38,13 +49,13 @@ public class StatusReportServiceImpl
|
|||
public StatusReportServiceImpl(in Configuration configuration)
|
||||
{
|
||||
_config = configuration;
|
||||
_mutex.Initialize();
|
||||
_mutex = new SdkMutexType();
|
||||
}
|
||||
|
||||
public struct Configuration
|
||||
{
|
||||
public NcaFileSystemServiceImpl NcaFsServiceImpl;
|
||||
public SaveDataFileSystemServiceImpl SaveFsServiceImpl;
|
||||
public NcaFileSystemServiceImpl NcaFileSystemServiceImpl;
|
||||
public SaveDataFileSystemServiceImpl SaveDataFileSystemServiceImpl;
|
||||
// Missing: FatFileSystemCreator (Not an IFatFileSystemCreator)
|
||||
public MemoryReport BufferManagerMemoryReport;
|
||||
public MemoryReport ExpHeapMemoryReport;
|
||||
|
@ -60,15 +71,19 @@ public class StatusReportServiceImpl
|
|||
|
||||
public Result GetAndClearFileSystemProxyErrorInfo(out FileSystemProxyErrorInfo errorInfo)
|
||||
{
|
||||
errorInfo = new FileSystemProxyErrorInfo();
|
||||
errorInfo = default;
|
||||
|
||||
_config.NcaFsServiceImpl.GetAndClearRomFsErrorInfo(out errorInfo.RemountForDataCorruptionCount,
|
||||
Assert.SdkRequiresNotNull(_config.NcaFileSystemServiceImpl);
|
||||
|
||||
_config.NcaFileSystemServiceImpl.GetAndClearRomFsErrorInfo(out errorInfo.RemountForDataCorruptionCount,
|
||||
out errorInfo.UnrecoverableDataCorruptionByRemountCount,
|
||||
out errorInfo.RecoveredByInvalidateCacheCount);
|
||||
|
||||
// Missing: GetFatInfo
|
||||
|
||||
Result rc = _config.SaveFsServiceImpl.GetSaveDataIndexCount(out int count);
|
||||
Assert.SdkRequiresNotNull(_config.SaveDataFileSystemServiceImpl);
|
||||
|
||||
Result rc = _config.SaveDataFileSystemServiceImpl.GetSaveDataIndexCount(out int count);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
errorInfo.SaveDataIndexCount = count;
|
||||
|
@ -77,40 +92,40 @@ public class StatusReportServiceImpl
|
|||
|
||||
public Result GetAndClearMemoryReportInfo(out MemoryReportInfo reportInfo)
|
||||
{
|
||||
using ScopedLock<SdkMutexType> lk = ScopedLock.Lock(ref _mutex);
|
||||
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
|
||||
|
||||
reportInfo = new MemoryReportInfo();
|
||||
reportInfo = default;
|
||||
|
||||
// Missing: Get and clear pooled buffer stats
|
||||
reportInfo.PooledBufferFreeSizePeak = 0;
|
||||
reportInfo.PooledBufferRetriedCount = 0;
|
||||
reportInfo.PooledBufferReduceAllocationCount = 0;
|
||||
reportInfo.PooledBufferFreeSizePeak = _config.FsServer.GetPooledBufferFreeSizePeak();
|
||||
reportInfo.PooledBufferRetriedCount = _config.FsServer.GetPooledBufferRetriedCount();
|
||||
reportInfo.PooledBufferReduceAllocationCount = _config.FsServer.GetPooledBufferReduceAllocationCount();
|
||||
reportInfo.PooledBufferFailedIdealAllocationCountOnAsyncAccess =
|
||||
_config.FsServer.GetPooledBufferFailedIdealAllocationCountOnAsyncAccess();
|
||||
|
||||
MemoryReport report = _config.BufferManagerMemoryReport;
|
||||
if (report != null)
|
||||
_config.FsServer.ClearPooledBufferPeak();
|
||||
|
||||
if (_config.BufferManagerMemoryReport is not null)
|
||||
{
|
||||
reportInfo.BufferManagerFreeSizePeak = report.GetFreeSizePeak();
|
||||
reportInfo.BufferManagerTotalAllocatableSizePeak = report.GetTotalAllocatableSizePeak();
|
||||
reportInfo.BufferManagerRetriedCount = report.GetRetriedCount();
|
||||
report.Clear();
|
||||
reportInfo.BufferManagerFreeSizePeak = _config.BufferManagerMemoryReport.GetFreeSizePeak();
|
||||
reportInfo.BufferManagerTotalAllocatableSizePeak = _config.BufferManagerMemoryReport.GetTotalAllocatableSizePeak();
|
||||
reportInfo.BufferManagerRetriedCount = _config.BufferManagerMemoryReport.GetRetriedCount();
|
||||
_config.BufferManagerMemoryReport.Clear();
|
||||
}
|
||||
|
||||
report = _config.ExpHeapMemoryReport;
|
||||
if (report != null)
|
||||
if (_config.ExpHeapMemoryReport is not null)
|
||||
{
|
||||
reportInfo.ExpHeapFreeSizePeak = report.GetFreeSizePeak();
|
||||
report.Clear();
|
||||
reportInfo.ExpHeapFreeSizePeak = _config.ExpHeapMemoryReport.GetFreeSizePeak();
|
||||
_config.ExpHeapMemoryReport.Clear();
|
||||
}
|
||||
|
||||
report = _config.BufferPoolMemoryReport;
|
||||
if (report != null)
|
||||
if (_config.BufferPoolMemoryReport is not null)
|
||||
{
|
||||
reportInfo.BufferPoolFreeSizePeak = report.GetFreeSizePeak();
|
||||
reportInfo.BufferPoolAllocateSizeMax = report.GetAllocateSizeMax();
|
||||
report.Clear();
|
||||
reportInfo.BufferPoolFreeSizePeak = _config.BufferPoolMemoryReport.GetFreeSizePeak();
|
||||
reportInfo.BufferPoolAllocateSizeMax = _config.BufferPoolMemoryReport.GetAllocateSizeMax();
|
||||
_config.BufferPoolMemoryReport.Clear();
|
||||
}
|
||||
|
||||
if (_config.GetPatrolAllocateCounts != null)
|
||||
if (_config.GetPatrolAllocateCounts is not null)
|
||||
{
|
||||
_config.GetPatrolAllocateCounts(out reportInfo.PatrolReadAllocateBufferSuccessCount,
|
||||
out reportInfo.PatrolReadAllocateBufferFailureCount);
|
||||
|
|
|
@ -6,6 +6,12 @@ using LibHac.Os;
|
|||
|
||||
namespace LibHac.FsSrv;
|
||||
|
||||
/// <summary>
|
||||
/// Handles time-related calls for <see cref="FileSystemProxyImpl"/>.
|
||||
/// </summary>
|
||||
/// <remarks><para>This struct handles checking a process' permissions before forwarding
|
||||
/// a request to the <see cref="TimeServiceImpl"/> object.</para>
|
||||
/// <para>Based on FS 13.1.0 (nnSdk 13.4.0)</para></remarks>
|
||||
public readonly struct TimeService
|
||||
{
|
||||
private readonly TimeServiceImpl _serviceImpl;
|
||||
|
@ -19,7 +25,8 @@ public readonly struct TimeService
|
|||
|
||||
public Result SetCurrentPosixTimeWithTimeDifference(long currentTime, int timeDifference)
|
||||
{
|
||||
Result rc = GetProgramInfo(out ProgramInfo programInfo);
|
||||
using var programRegistry = new ProgramRegistryImpl(_serviceImpl.FsServer);
|
||||
Result rc = programRegistry.GetProgramInfo(out ProgramInfo programInfo, _processId);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (!programInfo.AccessControl.CanCall(OperationType.SetCurrentPosixTime))
|
||||
|
@ -28,32 +35,33 @@ public readonly struct TimeService
|
|||
_serviceImpl.SetCurrentPosixTimeWithTimeDifference(currentTime, timeDifference);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private Result GetProgramInfo(out ProgramInfo programInfo)
|
||||
{
|
||||
return _serviceImpl.GetProgramInfo(out programInfo, _processId);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manages the current time used by the FS service.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
public class TimeServiceImpl
|
||||
{
|
||||
private long _basePosixTime;
|
||||
private int _timeDifference;
|
||||
private int _timeDifferenceSeconds;
|
||||
private SdkMutexType _mutex;
|
||||
|
||||
private FileSystemServer _fsServer;
|
||||
// LibHac addition
|
||||
internal FileSystemServer FsServer { get; }
|
||||
|
||||
public TimeServiceImpl(FileSystemServer fsServer)
|
||||
{
|
||||
_fsServer = fsServer;
|
||||
_basePosixTime = 0;
|
||||
_timeDifference = 0;
|
||||
_mutex.Initialize();
|
||||
_timeDifferenceSeconds = 0;
|
||||
_mutex = new SdkMutexType();
|
||||
|
||||
FsServer = fsServer;
|
||||
}
|
||||
|
||||
private long GetSystemSeconds()
|
||||
{
|
||||
OsState os = _fsServer.Hos.Os;
|
||||
OsState os = FsServer.Hos.Os;
|
||||
|
||||
Tick tick = os.GetSystemTick();
|
||||
TimeSpan timeSpan = os.ConvertToTimeSpan(tick);
|
||||
|
@ -69,7 +77,7 @@ public class TimeServiceImpl
|
|||
{
|
||||
UnsafeHelpers.SkipParamInit(out currentTime, out timeDifference);
|
||||
|
||||
using ScopedLock<SdkMutexType> lk = ScopedLock.Lock(ref _mutex);
|
||||
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
|
||||
|
||||
if (_basePosixTime == 0)
|
||||
return ResultFs.NotInitialized.Log();
|
||||
|
@ -81,7 +89,7 @@ public class TimeServiceImpl
|
|||
|
||||
if (!Unsafe.IsNullRef(ref timeDifference))
|
||||
{
|
||||
timeDifference = _timeDifference;
|
||||
timeDifference = _timeDifferenceSeconds;
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
|
@ -89,15 +97,9 @@ public class TimeServiceImpl
|
|||
|
||||
public void SetCurrentPosixTimeWithTimeDifference(long currentTime, int timeDifference)
|
||||
{
|
||||
using ScopedLock<SdkMutexType> lk = ScopedLock.Lock(ref _mutex);
|
||||
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
|
||||
|
||||
_basePosixTime = currentTime - GetSystemSeconds();
|
||||
_timeDifference = timeDifference;
|
||||
_timeDifferenceSeconds = timeDifference;
|
||||
}
|
||||
|
||||
internal Result GetProgramInfo(out ProgramInfo programInfo, ulong processId)
|
||||
{
|
||||
var registry = new ProgramRegistryImpl(_fsServer);
|
||||
return registry.GetProgramInfo(out programInfo, processId);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,19 @@
|
|||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Util;
|
||||
|
||||
namespace LibHac.FsSystem;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Splits read and write requests on an <see cref="IFile"/> or <see cref="IStorage"/> into smaller chunks
|
||||
/// so the request can be processed by multiple threads simultaneously.</para>
|
||||
/// <para>This interface exists because of <see cref="CompressedStorage"/> where it will split requests into
|
||||
/// chunks that start and end on the boundaries of the compressed blocks.</para>
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
public interface IAsynchronousAccessSplitter : IDisposable
|
||||
{
|
||||
private static readonly DefaultAsynchronousAccessSplitter DefaultAccessSplitter = new();
|
||||
|
@ -57,6 +66,11 @@ public interface IAsynchronousAccessSplitter : IDisposable
|
|||
Result QueryAppropriateOffset(out long offsetAppropriate, long startOffset, long accessSize, long alignmentSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The default <see cref="IAsynchronousAccessSplitter"/> that is used when an <see cref="IStorage"/>
|
||||
/// or <see cref="IFile"/> doesn't need any special logic to split a request into multiple chunks.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
public class DefaultAsynchronousAccessSplitter : IAsynchronousAccessSplitter
|
||||
{
|
||||
public void Dispose() { }
|
||||
|
|
|
@ -1,9 +1,64 @@
|
|||
using System;
|
||||
using System.Buffers;
|
||||
using LibHac.Diag;
|
||||
using LibHac.FsSrv;
|
||||
using LibHac.Os;
|
||||
|
||||
namespace LibHac.FsSystem;
|
||||
|
||||
public static class PooledBufferGlobalMethods
|
||||
{
|
||||
public static long GetPooledBufferRetriedCount(this FileSystemServer fsSrv)
|
||||
{
|
||||
return fsSrv.Globals.PooledBuffer.CountRetried;
|
||||
}
|
||||
|
||||
public static long GetPooledBufferReduceAllocationCount(this FileSystemServer fsSrv)
|
||||
{
|
||||
return fsSrv.Globals.PooledBuffer.CountReduceAllocation;
|
||||
}
|
||||
|
||||
public static long GetPooledBufferFailedIdealAllocationCountOnAsyncAccess(this FileSystemServer fsSrv)
|
||||
{
|
||||
return fsSrv.Globals.PooledBuffer.CountFailedIdealAllocationOnAsyncAccess;
|
||||
}
|
||||
|
||||
public static long GetPooledBufferFreeSizePeak(this FileSystemServer fsSrv)
|
||||
{
|
||||
ref PooledBufferGlobals g = ref fsSrv.Globals.PooledBuffer;
|
||||
|
||||
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref g.HeapMutex);
|
||||
return g.SizeHeapFreePeak;
|
||||
}
|
||||
|
||||
public static void ClearPooledBufferPeak(this FileSystemServer fsSrv)
|
||||
{
|
||||
ref PooledBufferGlobals g = ref fsSrv.Globals.PooledBuffer;
|
||||
|
||||
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref g.HeapMutex);
|
||||
|
||||
// Missing: Get SizeHeapFreePeak from the heap object
|
||||
g.CountRetried = 0;
|
||||
g.CountReduceAllocation = 0;
|
||||
g.CountFailedIdealAllocationOnAsyncAccess = 0;
|
||||
}
|
||||
}
|
||||
|
||||
internal struct PooledBufferGlobals
|
||||
{
|
||||
public SdkMutexType HeapMutex;
|
||||
public long SizeHeapFreePeak;
|
||||
public Memory<byte> HeapBuffer;
|
||||
public long CountRetried;
|
||||
public long CountReduceAllocation;
|
||||
public long CountFailedIdealAllocationOnAsyncAccess;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
HeapMutex = new SdkMutexType();
|
||||
}
|
||||
}
|
||||
|
||||
// Implement the PooledBuffer interface using .NET ArrayPools
|
||||
public struct PooledBuffer : IDisposable
|
||||
{
|
||||
|
|
|
@ -1,59 +1,68 @@
|
|||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
|
||||
namespace LibHac.FsSystem;
|
||||
|
||||
/// <summary>
|
||||
/// Allows interacting with an <see cref="IStorage"/> via an <see cref="IFile"/> interface.
|
||||
/// </summary>
|
||||
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
|
||||
public class StorageFile : IFile
|
||||
{
|
||||
private IStorage BaseStorage { get; }
|
||||
private OpenMode Mode { get; }
|
||||
private IStorage _baseStorage;
|
||||
private OpenMode _mode;
|
||||
|
||||
public StorageFile(IStorage baseStorage, OpenMode mode)
|
||||
{
|
||||
BaseStorage = baseStorage;
|
||||
Mode = mode;
|
||||
_baseStorage = baseStorage;
|
||||
_mode = mode;
|
||||
}
|
||||
|
||||
protected override Result DoRead(out long bytesRead, long offset, Span<byte> destination,
|
||||
in ReadOption option)
|
||||
protected override Result DoRead(out long bytesRead, long offset, Span<byte> destination, in ReadOption option)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out bytesRead);
|
||||
|
||||
Result rc = DryRead(out long toRead, offset, destination.Length, in option, Mode);
|
||||
Assert.SdkRequiresNotNull(_baseStorage);
|
||||
|
||||
Result rc = DryRead(out long readSize, offset, destination.Length, in option, _mode);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (toRead == 0)
|
||||
if (readSize == 0)
|
||||
{
|
||||
bytesRead = 0;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
rc = BaseStorage.Read(offset, destination.Slice(0, (int)toRead));
|
||||
rc = _baseStorage.Read(offset, destination.Slice(0, (int)readSize));
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
bytesRead = toRead;
|
||||
bytesRead = readSize;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
protected override Result DoWrite(long offset, ReadOnlySpan<byte> source, in WriteOption option)
|
||||
{
|
||||
Result rc = DryWrite(out bool isResizeNeeded, offset, source.Length, in option, Mode);
|
||||
Assert.SdkRequiresNotNull(_baseStorage);
|
||||
|
||||
Result rc = DryWrite(out bool isAppendNeeded, offset, source.Length, in option, _mode);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (isResizeNeeded)
|
||||
if (isAppendNeeded)
|
||||
{
|
||||
rc = DoSetSize(offset + source.Length);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
rc = BaseStorage.Write(offset, source);
|
||||
rc = _baseStorage.Write(offset, source);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (option.HasFlushFlag())
|
||||
{
|
||||
return Flush();
|
||||
rc = Flush();
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
|
@ -61,28 +70,68 @@ public class StorageFile : IFile
|
|||
|
||||
protected override Result DoFlush()
|
||||
{
|
||||
if (!Mode.HasFlag(OpenMode.Write))
|
||||
Assert.SdkRequiresNotNull(_baseStorage);
|
||||
|
||||
if (!_mode.HasFlag(OpenMode.Write))
|
||||
return Result.Success;
|
||||
|
||||
return BaseStorage.Flush();
|
||||
return _baseStorage.Flush();
|
||||
}
|
||||
|
||||
protected override Result DoGetSize(out long size)
|
||||
{
|
||||
return BaseStorage.GetSize(out size);
|
||||
Assert.SdkRequiresNotNull(_baseStorage);
|
||||
|
||||
return _baseStorage.GetSize(out size);
|
||||
}
|
||||
|
||||
protected override Result DoSetSize(long size)
|
||||
{
|
||||
if (!Mode.HasFlag(OpenMode.Write))
|
||||
return ResultFs.WriteUnpermitted.Log();
|
||||
Assert.SdkRequiresNotNull(_baseStorage);
|
||||
|
||||
return BaseStorage.SetSize(size);
|
||||
Result rc = DrySetSize(size, _mode);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
return _baseStorage.SetSize(size);
|
||||
}
|
||||
|
||||
protected override Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size,
|
||||
ReadOnlySpan<byte> inBuffer)
|
||||
{
|
||||
return ResultFs.NotImplemented.Log();
|
||||
Assert.SdkRequiresNotNull(_baseStorage);
|
||||
|
||||
switch (operationId)
|
||||
{
|
||||
case OperationId.InvalidateCache:
|
||||
{
|
||||
if (!_mode.HasFlag(OpenMode.Read))
|
||||
return ResultFs.ReadUnpermitted.Log();
|
||||
|
||||
Result rc = _baseStorage.OperateRange(OperationId.InvalidateCache, offset, size);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
break;
|
||||
}
|
||||
case OperationId.QueryRange:
|
||||
{
|
||||
if (offset < 0)
|
||||
return ResultFs.InvalidOffset.Log();
|
||||
|
||||
Result rc = GetSize(out long fileSize);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
long operableSize = Math.Max(0, fileSize - offset);
|
||||
long operateSize = Math.Min(operableSize, size);
|
||||
|
||||
rc = _baseStorage.OperateRange(outBuffer, operationId, offset, operateSize, inBuffer);
|
||||
if (rc.IsFailure()) return rc.Miss();
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return ResultFs.UnsupportedOperateRangeForStorageFile.Log();
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -35,6 +35,12 @@ public struct Optional<T>
|
|||
_hasValue = true;
|
||||
}
|
||||
|
||||
public Optional(T value)
|
||||
{
|
||||
_value = value;
|
||||
_hasValue = true;
|
||||
}
|
||||
|
||||
public static implicit operator Optional<T>(in T value) => new Optional<T>(in value);
|
||||
|
||||
public void Set(T value)
|
||||
|
|
Loading…
Reference in a new issue