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.Impl;
|
||||||
using LibHac.FsSrv.Storage;
|
using LibHac.FsSrv.Storage;
|
||||||
|
using LibHac.FsSystem;
|
||||||
|
|
||||||
namespace LibHac.FsSrv;
|
namespace LibHac.FsSrv;
|
||||||
|
|
||||||
|
@ -34,6 +35,7 @@ internal struct FileSystemServerGlobals
|
||||||
public SaveDataSharedFileStorageGlobals SaveDataSharedFileStorage;
|
public SaveDataSharedFileStorageGlobals SaveDataSharedFileStorage;
|
||||||
public MultiCommitManagerGlobals MultiCommitManager;
|
public MultiCommitManagerGlobals MultiCommitManager;
|
||||||
public LocationResolverSetGlobals LocationResolverSet;
|
public LocationResolverSetGlobals LocationResolverSet;
|
||||||
|
public PooledBufferGlobals PooledBuffer;
|
||||||
|
|
||||||
public void Initialize(HorizonClient horizonClient, FileSystemServer fsServer)
|
public void Initialize(HorizonClient horizonClient, FileSystemServer fsServer)
|
||||||
{
|
{
|
||||||
|
@ -43,6 +45,7 @@ internal struct FileSystemServerGlobals
|
||||||
SaveDataSharedFileStorage.Initialize(fsServer);
|
SaveDataSharedFileStorage.Initialize(fsServer);
|
||||||
MultiCommitManager.Initialize();
|
MultiCommitManager.Initialize();
|
||||||
LocationResolverSet.Initialize();
|
LocationResolverSet.Initialize();
|
||||||
|
PooledBuffer.Initialize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -150,8 +150,8 @@ public static class FileSystemServerInitializer
|
||||||
var saveFsService = new SaveDataFileSystemServiceImpl(in saveFsServiceConfig);
|
var saveFsService = new SaveDataFileSystemServiceImpl(in saveFsServiceConfig);
|
||||||
|
|
||||||
var statusReportServiceConfig = new StatusReportServiceImpl.Configuration();
|
var statusReportServiceConfig = new StatusReportServiceImpl.Configuration();
|
||||||
statusReportServiceConfig.NcaFsServiceImpl = ncaFsService;
|
statusReportServiceConfig.NcaFileSystemServiceImpl = ncaFsService;
|
||||||
statusReportServiceConfig.SaveFsServiceImpl = saveFsService;
|
statusReportServiceConfig.SaveDataFileSystemServiceImpl = saveFsService;
|
||||||
statusReportServiceConfig.BufferManagerMemoryReport = null;
|
statusReportServiceConfig.BufferManagerMemoryReport = null;
|
||||||
statusReportServiceConfig.ExpHeapMemoryReport = null;
|
statusReportServiceConfig.ExpHeapMemoryReport = null;
|
||||||
statusReportServiceConfig.BufferPoolMemoryReport = null;
|
statusReportServiceConfig.BufferPoolMemoryReport = null;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
|
@ -8,6 +9,108 @@ using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.FsSrv;
|
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
|
internal class SaveDataInfoFilterReader : SaveDataInfoReaderImpl
|
||||||
{
|
{
|
||||||
private SharedRef<SaveDataInfoReaderImpl> _reader;
|
private SharedRef<SaveDataInfoReaderImpl> _reader;
|
||||||
|
@ -19,146 +122,35 @@ internal class SaveDataInfoFilterReader : SaveDataInfoReaderImpl
|
||||||
_infoFilter = infoFilter;
|
_infoFilter = infoFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_reader.Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
[SkipLocalsInit]
|
||||||
public Result Read(out long readCount, OutBuffer saveDataInfoBuffer)
|
public Result Read(out long readCount, OutBuffer saveDataInfoBuffer)
|
||||||
{
|
{
|
||||||
UnsafeHelpers.SkipParamInit(out readCount);
|
UnsafeHelpers.SkipParamInit(out readCount);
|
||||||
|
|
||||||
Span<SaveDataInfo> outInfo = MemoryMarshal.Cast<byte, SaveDataInfo>(saveDataInfoBuffer.Buffer);
|
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;
|
int count = 0;
|
||||||
|
|
||||||
while (count < outInfo.Length)
|
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 (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
if (baseReadCount == 0) break;
|
if (baseReadCount == 0) break;
|
||||||
|
|
||||||
if (_infoFilter.Includes(in tempInfo))
|
if (_infoFilter.Includes(in info))
|
||||||
{
|
{
|
||||||
outInfo[count] = tempInfo;
|
outInfo[count] = info;
|
||||||
|
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readCount = count;
|
readCount = count;
|
||||||
|
|
||||||
return Result.Success;
|
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.Diag;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
|
using LibHac.FsSystem;
|
||||||
using LibHac.Os;
|
using LibHac.Os;
|
||||||
|
|
||||||
namespace LibHac.FsSrv;
|
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
|
public readonly struct StatusReportService
|
||||||
{
|
{
|
||||||
private readonly StatusReportServiceImpl _serviceImpl;
|
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
|
public class StatusReportServiceImpl
|
||||||
{
|
{
|
||||||
private Configuration _config;
|
private Configuration _config;
|
||||||
|
@ -38,13 +49,13 @@ public class StatusReportServiceImpl
|
||||||
public StatusReportServiceImpl(in Configuration configuration)
|
public StatusReportServiceImpl(in Configuration configuration)
|
||||||
{
|
{
|
||||||
_config = configuration;
|
_config = configuration;
|
||||||
_mutex.Initialize();
|
_mutex = new SdkMutexType();
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Configuration
|
public struct Configuration
|
||||||
{
|
{
|
||||||
public NcaFileSystemServiceImpl NcaFsServiceImpl;
|
public NcaFileSystemServiceImpl NcaFileSystemServiceImpl;
|
||||||
public SaveDataFileSystemServiceImpl SaveFsServiceImpl;
|
public SaveDataFileSystemServiceImpl SaveDataFileSystemServiceImpl;
|
||||||
// Missing: FatFileSystemCreator (Not an IFatFileSystemCreator)
|
// Missing: FatFileSystemCreator (Not an IFatFileSystemCreator)
|
||||||
public MemoryReport BufferManagerMemoryReport;
|
public MemoryReport BufferManagerMemoryReport;
|
||||||
public MemoryReport ExpHeapMemoryReport;
|
public MemoryReport ExpHeapMemoryReport;
|
||||||
|
@ -60,15 +71,19 @@ public class StatusReportServiceImpl
|
||||||
|
|
||||||
public Result GetAndClearFileSystemProxyErrorInfo(out FileSystemProxyErrorInfo errorInfo)
|
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.UnrecoverableDataCorruptionByRemountCount,
|
||||||
out errorInfo.RecoveredByInvalidateCacheCount);
|
out errorInfo.RecoveredByInvalidateCacheCount);
|
||||||
|
|
||||||
// Missing: GetFatInfo
|
// 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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
errorInfo.SaveDataIndexCount = count;
|
errorInfo.SaveDataIndexCount = count;
|
||||||
|
@ -77,40 +92,40 @@ public class StatusReportServiceImpl
|
||||||
|
|
||||||
public Result GetAndClearMemoryReportInfo(out MemoryReportInfo reportInfo)
|
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 = _config.FsServer.GetPooledBufferFreeSizePeak();
|
||||||
reportInfo.PooledBufferFreeSizePeak = 0;
|
reportInfo.PooledBufferRetriedCount = _config.FsServer.GetPooledBufferRetriedCount();
|
||||||
reportInfo.PooledBufferRetriedCount = 0;
|
reportInfo.PooledBufferReduceAllocationCount = _config.FsServer.GetPooledBufferReduceAllocationCount();
|
||||||
reportInfo.PooledBufferReduceAllocationCount = 0;
|
reportInfo.PooledBufferFailedIdealAllocationCountOnAsyncAccess =
|
||||||
|
_config.FsServer.GetPooledBufferFailedIdealAllocationCountOnAsyncAccess();
|
||||||
|
|
||||||
MemoryReport report = _config.BufferManagerMemoryReport;
|
_config.FsServer.ClearPooledBufferPeak();
|
||||||
if (report != null)
|
|
||||||
|
if (_config.BufferManagerMemoryReport is not null)
|
||||||
{
|
{
|
||||||
reportInfo.BufferManagerFreeSizePeak = report.GetFreeSizePeak();
|
reportInfo.BufferManagerFreeSizePeak = _config.BufferManagerMemoryReport.GetFreeSizePeak();
|
||||||
reportInfo.BufferManagerTotalAllocatableSizePeak = report.GetTotalAllocatableSizePeak();
|
reportInfo.BufferManagerTotalAllocatableSizePeak = _config.BufferManagerMemoryReport.GetTotalAllocatableSizePeak();
|
||||||
reportInfo.BufferManagerRetriedCount = report.GetRetriedCount();
|
reportInfo.BufferManagerRetriedCount = _config.BufferManagerMemoryReport.GetRetriedCount();
|
||||||
report.Clear();
|
_config.BufferManagerMemoryReport.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
report = _config.ExpHeapMemoryReport;
|
if (_config.ExpHeapMemoryReport is not null)
|
||||||
if (report != null)
|
|
||||||
{
|
{
|
||||||
reportInfo.ExpHeapFreeSizePeak = report.GetFreeSizePeak();
|
reportInfo.ExpHeapFreeSizePeak = _config.ExpHeapMemoryReport.GetFreeSizePeak();
|
||||||
report.Clear();
|
_config.ExpHeapMemoryReport.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
report = _config.BufferPoolMemoryReport;
|
if (_config.BufferPoolMemoryReport is not null)
|
||||||
if (report != null)
|
|
||||||
{
|
{
|
||||||
reportInfo.BufferPoolFreeSizePeak = report.GetFreeSizePeak();
|
reportInfo.BufferPoolFreeSizePeak = _config.BufferPoolMemoryReport.GetFreeSizePeak();
|
||||||
reportInfo.BufferPoolAllocateSizeMax = report.GetAllocateSizeMax();
|
reportInfo.BufferPoolAllocateSizeMax = _config.BufferPoolMemoryReport.GetAllocateSizeMax();
|
||||||
report.Clear();
|
_config.BufferPoolMemoryReport.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_config.GetPatrolAllocateCounts != null)
|
if (_config.GetPatrolAllocateCounts is not null)
|
||||||
{
|
{
|
||||||
_config.GetPatrolAllocateCounts(out reportInfo.PatrolReadAllocateBufferSuccessCount,
|
_config.GetPatrolAllocateCounts(out reportInfo.PatrolReadAllocateBufferSuccessCount,
|
||||||
out reportInfo.PatrolReadAllocateBufferFailureCount);
|
out reportInfo.PatrolReadAllocateBufferFailureCount);
|
||||||
|
|
|
@ -6,6 +6,12 @@ using LibHac.Os;
|
||||||
|
|
||||||
namespace LibHac.FsSrv;
|
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
|
public readonly struct TimeService
|
||||||
{
|
{
|
||||||
private readonly TimeServiceImpl _serviceImpl;
|
private readonly TimeServiceImpl _serviceImpl;
|
||||||
|
@ -19,7 +25,8 @@ public readonly struct TimeService
|
||||||
|
|
||||||
public Result SetCurrentPosixTimeWithTimeDifference(long currentTime, int timeDifference)
|
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 (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
if (!programInfo.AccessControl.CanCall(OperationType.SetCurrentPosixTime))
|
if (!programInfo.AccessControl.CanCall(OperationType.SetCurrentPosixTime))
|
||||||
|
@ -28,32 +35,33 @@ public readonly struct TimeService
|
||||||
_serviceImpl.SetCurrentPosixTimeWithTimeDifference(currentTime, timeDifference);
|
_serviceImpl.SetCurrentPosixTimeWithTimeDifference(currentTime, timeDifference);
|
||||||
return Result.Success;
|
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
|
public class TimeServiceImpl
|
||||||
{
|
{
|
||||||
private long _basePosixTime;
|
private long _basePosixTime;
|
||||||
private int _timeDifference;
|
private int _timeDifferenceSeconds;
|
||||||
private SdkMutexType _mutex;
|
private SdkMutexType _mutex;
|
||||||
|
|
||||||
private FileSystemServer _fsServer;
|
// LibHac addition
|
||||||
|
internal FileSystemServer FsServer { get; }
|
||||||
|
|
||||||
public TimeServiceImpl(FileSystemServer fsServer)
|
public TimeServiceImpl(FileSystemServer fsServer)
|
||||||
{
|
{
|
||||||
_fsServer = fsServer;
|
|
||||||
_basePosixTime = 0;
|
_basePosixTime = 0;
|
||||||
_timeDifference = 0;
|
_timeDifferenceSeconds = 0;
|
||||||
_mutex.Initialize();
|
_mutex = new SdkMutexType();
|
||||||
|
|
||||||
|
FsServer = fsServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private long GetSystemSeconds()
|
private long GetSystemSeconds()
|
||||||
{
|
{
|
||||||
OsState os = _fsServer.Hos.Os;
|
OsState os = FsServer.Hos.Os;
|
||||||
|
|
||||||
Tick tick = os.GetSystemTick();
|
Tick tick = os.GetSystemTick();
|
||||||
TimeSpan timeSpan = os.ConvertToTimeSpan(tick);
|
TimeSpan timeSpan = os.ConvertToTimeSpan(tick);
|
||||||
|
@ -69,7 +77,7 @@ public class TimeServiceImpl
|
||||||
{
|
{
|
||||||
UnsafeHelpers.SkipParamInit(out currentTime, out timeDifference);
|
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)
|
if (_basePosixTime == 0)
|
||||||
return ResultFs.NotInitialized.Log();
|
return ResultFs.NotInitialized.Log();
|
||||||
|
@ -81,7 +89,7 @@ public class TimeServiceImpl
|
||||||
|
|
||||||
if (!Unsafe.IsNullRef(ref timeDifference))
|
if (!Unsafe.IsNullRef(ref timeDifference))
|
||||||
{
|
{
|
||||||
timeDifference = _timeDifference;
|
timeDifference = _timeDifferenceSeconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
|
@ -89,15 +97,9 @@ public class TimeServiceImpl
|
||||||
|
|
||||||
public void SetCurrentPosixTimeWithTimeDifference(long currentTime, int timeDifference)
|
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();
|
_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 System;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Diag;
|
using LibHac.Diag;
|
||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.Fs.Fsa;
|
||||||
using LibHac.Util;
|
using LibHac.Util;
|
||||||
|
|
||||||
namespace LibHac.FsSystem;
|
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
|
public interface IAsynchronousAccessSplitter : IDisposable
|
||||||
{
|
{
|
||||||
private static readonly DefaultAsynchronousAccessSplitter DefaultAccessSplitter = new();
|
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);
|
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 class DefaultAsynchronousAccessSplitter : IAsynchronousAccessSplitter
|
||||||
{
|
{
|
||||||
public void Dispose() { }
|
public void Dispose() { }
|
||||||
|
|
|
@ -1,9 +1,64 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using LibHac.Diag;
|
using LibHac.Diag;
|
||||||
|
using LibHac.FsSrv;
|
||||||
|
using LibHac.Os;
|
||||||
|
|
||||||
namespace LibHac.FsSystem;
|
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
|
// Implement the PooledBuffer interface using .NET ArrayPools
|
||||||
public struct PooledBuffer : IDisposable
|
public struct PooledBuffer : IDisposable
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,59 +1,68 @@
|
||||||
using System;
|
using System;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
|
using LibHac.Diag;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
|
|
||||||
namespace LibHac.FsSystem;
|
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
|
public class StorageFile : IFile
|
||||||
{
|
{
|
||||||
private IStorage BaseStorage { get; }
|
private IStorage _baseStorage;
|
||||||
private OpenMode Mode { get; }
|
private OpenMode _mode;
|
||||||
|
|
||||||
public StorageFile(IStorage baseStorage, OpenMode mode)
|
public StorageFile(IStorage baseStorage, OpenMode mode)
|
||||||
{
|
{
|
||||||
BaseStorage = baseStorage;
|
_baseStorage = baseStorage;
|
||||||
Mode = mode;
|
_mode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Result DoRead(out long bytesRead, long offset, Span<byte> destination,
|
protected override Result DoRead(out long bytesRead, long offset, Span<byte> destination, in ReadOption option)
|
||||||
in ReadOption option)
|
|
||||||
{
|
{
|
||||||
UnsafeHelpers.SkipParamInit(out bytesRead);
|
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 (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
if (toRead == 0)
|
if (readSize == 0)
|
||||||
{
|
{
|
||||||
bytesRead = 0;
|
bytesRead = 0;
|
||||||
return Result.Success;
|
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;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
bytesRead = toRead;
|
bytesRead = readSize;
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Result DoWrite(long offset, ReadOnlySpan<byte> source, in WriteOption option)
|
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 (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
if (isResizeNeeded)
|
if (isAppendNeeded)
|
||||||
{
|
{
|
||||||
rc = DoSetSize(offset + source.Length);
|
rc = DoSetSize(offset + source.Length);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = BaseStorage.Write(offset, source);
|
rc = _baseStorage.Write(offset, source);
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
if (option.HasFlushFlag())
|
if (option.HasFlushFlag())
|
||||||
{
|
{
|
||||||
return Flush();
|
rc = Flush();
|
||||||
|
if (rc.IsFailure()) return rc.Miss();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
|
@ -61,28 +70,68 @@ public class StorageFile : IFile
|
||||||
|
|
||||||
protected override Result DoFlush()
|
protected override Result DoFlush()
|
||||||
{
|
{
|
||||||
if (!Mode.HasFlag(OpenMode.Write))
|
Assert.SdkRequiresNotNull(_baseStorage);
|
||||||
|
|
||||||
|
if (!_mode.HasFlag(OpenMode.Write))
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
|
|
||||||
return BaseStorage.Flush();
|
return _baseStorage.Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Result DoGetSize(out long size)
|
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)
|
protected override Result DoSetSize(long size)
|
||||||
{
|
{
|
||||||
if (!Mode.HasFlag(OpenMode.Write))
|
Assert.SdkRequiresNotNull(_baseStorage);
|
||||||
return ResultFs.WriteUnpermitted.Log();
|
|
||||||
|
|
||||||
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,
|
protected override Result DoOperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size,
|
||||||
ReadOnlySpan<byte> inBuffer)
|
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;
|
_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 static implicit operator Optional<T>(in T value) => new Optional<T>(in value);
|
||||||
|
|
||||||
public void Set(T value)
|
public void Set(T value)
|
||||||
|
|
Loading…
Reference in a new issue