Fixup some mounted-FS-related classes and update them to 13.1.0

- FileSystemAccessor
- FileAccessor
- DirectoryAccessor
- MountTable
- UserMountTable
- FileDataCacheAccessor
- IFileDataCache
- GlobalFileDataCacheAccessorReadableScopedPointer
- FileDataCache shim functions
- PathBasedFileDataCache shim functions
This commit is contained in:
Alex Barney 2021-12-22 16:43:04 -07:00
parent 375b5b9220
commit cf7062788f
14 changed files with 414 additions and 142 deletions

13
src/LibHac/Common/Box.cs Normal file
View file

@ -0,0 +1,13 @@
namespace LibHac.Common;
public class Box<T> where T : struct
{
private T _value;
public ref T Value => ref _value;
public Box()
{
_value = new T();
}
}

View file

@ -35,6 +35,7 @@ internal struct FileSystemClientGlobals : IDisposable
public FsContextHandlerGlobals FsContextHandler; public FsContextHandlerGlobals FsContextHandler;
public ResultHandlingUtilityGlobals ResultHandlingUtility; public ResultHandlingUtilityGlobals ResultHandlingUtility;
public DirectorySaveDataFileSystemGlobals DirectorySaveDataFileSystem; public DirectorySaveDataFileSystemGlobals DirectorySaveDataFileSystem;
public FileDataCacheShim.Globals FileDataCache;
public void Initialize(FileSystemClient fsClient, HorizonClient horizonClient) public void Initialize(FileSystemClient fsClient, HorizonClient horizonClient)
{ {
@ -60,4 +61,4 @@ public readonly struct FileSystemClientImpl
internal ref FileSystemClientGlobals Globals => ref Fs.Globals; internal ref FileSystemClientGlobals Globals => ref Fs.Globals;
internal FileSystemClientImpl(FileSystemClient parentClient) => Fs = parentClient; internal FileSystemClientImpl(FileSystemClient parentClient) => Fs = parentClient;
} }

View file

@ -5,6 +5,10 @@ using LibHac.Fs.Fsa;
// ReSharper disable once CheckNamespace // ReSharper disable once CheckNamespace
namespace LibHac.Fs.Impl; namespace LibHac.Fs.Impl;
/// <summary>
/// Provides access to a directory in a mounted file system and handles closing the directory.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
internal class DirectoryAccessor : IDisposable internal class DirectoryAccessor : IDisposable
{ {
private UniqueRef<IDirectory> _directory; private UniqueRef<IDirectory> _directory;
@ -35,4 +39,4 @@ internal class DirectoryAccessor : IDisposable
{ {
return _directory.Get.GetEntryCount(out entryCount); return _directory.Get.GetEntryCount(out entryCount);
} }
} }

View file

@ -2,6 +2,7 @@
using LibHac.Common; using LibHac.Common;
using LibHac.Diag; using LibHac.Diag;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
using LibHac.Fs.Shim;
using LibHac.Os; using LibHac.Os;
using static LibHac.Fs.Impl.AccessLogStrings; using static LibHac.Fs.Impl.AccessLogStrings;
@ -15,6 +16,10 @@ internal enum WriteState
Failed, Failed,
} }
/// <summary>
/// Provides access to a mount <see cref="IFile"/> and handles caching it.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
internal class FileAccessor : IDisposable internal class FileAccessor : IDisposable
{ {
private const string NeedFlushMessage = "Error: nn::fs::CloseFile() failed because the file was not flushed.\n"; private const string NeedFlushMessage = "Error: nn::fs::CloseFile() failed because the file was not flushed.\n";
@ -24,8 +29,7 @@ internal class FileAccessor : IDisposable
private WriteState _writeState; private WriteState _writeState;
private Result _lastResult; private Result _lastResult;
private OpenMode _openMode; private OpenMode _openMode;
private FilePathHash _filePathHash; private Box<FilePathHash> _filePathHash;
// ReSharper disable once NotAccessedField.Local
private int _pathHashIndex; private int _pathHashIndex;
internal HorizonClient Hos { get; } internal HorizonClient Hos { get; }
@ -59,7 +63,7 @@ internal class FileAccessor : IDisposable
public WriteState GetWriteState() => _writeState; public WriteState GetWriteState() => _writeState;
public FileSystemAccessor GetParent() => _parentFileSystem; public FileSystemAccessor GetParent() => _parentFileSystem;
public void SetFilePathHash(FilePathHash filePathHash, int index) public void SetFilePathHash(Box<FilePathHash> filePathHash, int index)
{ {
_filePathHash = filePathHash; _filePathHash = filePathHash;
_pathHashIndex = index; _pathHashIndex = index;
@ -73,18 +77,18 @@ internal class FileAccessor : IDisposable
return result; return result;
} }
public Result ReadWithoutCacheAccessLog(out long bytesRead, long offset, Span<byte> destination,
in ReadOption option)
{
return _file.Get.Read(out bytesRead, offset, destination, in option);
}
private Result ReadWithCacheAccessLog(out long bytesRead, long offset, Span<byte> destination, private Result ReadWithCacheAccessLog(out long bytesRead, long offset, Span<byte> destination,
in ReadOption option, bool usePathCache, bool useDataCache) in ReadOption option, bool usePathCache, bool useDataCache)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public Result ReadWithoutCacheAccessLog(out long bytesRead, long offset, Span<byte> destination,
in ReadOption option)
{
return _file.Get.Read(out bytesRead, offset, destination, in option);
}
public Result Read(out long bytesRead, long offset, Span<byte> destination, in ReadOption option) public Result Read(out long bytesRead, long offset, Span<byte> destination, in ReadOption option)
{ {
UnsafeHelpers.SkipParamInit(out bytesRead); UnsafeHelpers.SkipParamInit(out bytesRead);
@ -113,42 +117,36 @@ internal class FileAccessor : IDisposable
return _lastResult; return _lastResult;
} }
// ReSharper disable ConditionIsAlwaysTrueOrFalse bool usePathCache = _parentFileSystem is not null && _filePathHash is not null;
bool usePathCache = _parentFileSystem is not null && _filePathHash.Data != 0; bool useDataCache = Hos.Fs.Impl.IsGlobalFileDataCacheEnabled() && _parentFileSystem is not null &&
_parentFileSystem.IsFileDataCacheAttachable();
// Todo: Call IsGlobalFileDataCacheEnabled
#pragma warning disable 162
bool useDataCache = false && _parentFileSystem is not null && _parentFileSystem.IsFileDataCacheAttachable();
#pragma warning restore 162
if (usePathCache || useDataCache) if (usePathCache || useDataCache)
{ {
return ReadWithCacheAccessLog(out bytesRead, offset, destination, in option, usePathCache, return ReadWithCacheAccessLog(out bytesRead, offset, destination, in option, usePathCache,
useDataCache); useDataCache);
} }
if (Hos.Fs.Impl.IsEnabledAccessLog() && Hos.Fs.Impl.IsEnabledHandleAccessLog(handle))
{
Tick start = Hos.Os.GetSystemTick();
rc = ReadWithoutCacheAccessLog(out bytesRead, offset, destination, in option);
Tick end = Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
sb.Append(LogOffset).AppendFormat(offset)
.Append(LogSize).AppendFormat(destination.Length)
.Append(LogReadSize).AppendFormat(AccessLogImpl.DereferenceOutValue(in bytesRead, rc));
Hos.Fs.Impl.OutputAccessLog(rc, start, end, handle, new U8Span(logBuffer),
nameof(UserFile.ReadFile));
}
else else
{ {
if (Hos.Fs.Impl.IsEnabledAccessLog() && Hos.Fs.Impl.IsEnabledHandleAccessLog(handle)) rc = ReadWithoutCacheAccessLog(out bytesRead, offset, destination, in option);
{
Tick start = Hos.Os.GetSystemTick();
rc = ReadWithoutCacheAccessLog(out bytesRead, offset, destination, in option);
Tick end = Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
sb.Append(LogOffset).AppendFormat(offset)
.Append(LogSize).AppendFormat(destination.Length)
.Append(LogReadSize).AppendFormat(AccessLogImpl.DereferenceOutValue(in bytesRead, rc));
Hos.Fs.Impl.OutputAccessLog(rc, start, end, handle, new U8Span(logBuffer),
nameof(UserFile.ReadFile));
}
else
{
rc = ReadWithoutCacheAccessLog(out bytesRead, offset, destination, in option);
}
return rc;
} }
// ReSharper restore ConditionIsAlwaysTrueOrFalse
return rc;
} }
public Result Write(long offset, ReadOnlySpan<byte> source, in WriteOption option) public Result Write(long offset, ReadOnlySpan<byte> source, in WriteOption option)
@ -159,9 +157,11 @@ internal class FileAccessor : IDisposable
using ScopedSetter<WriteState> setter = using ScopedSetter<WriteState> setter =
ScopedSetter<WriteState>.MakeScopedSetter(ref _writeState, WriteState.Failed); ScopedSetter<WriteState>.MakeScopedSetter(ref _writeState, WriteState.Failed);
if (_filePathHash.Data != 0) if (_filePathHash is not null)
{ {
throw new NotImplementedException(); Result rc = UpdateLastResult(Hos.Fs.Impl.WriteViaPathBasedFileDataCache(_file.Get, (int)GetOpenMode(),
_parentFileSystem, in _filePathHash.Value, _pathHashIndex, offset, source, in option));
if (rc.IsFailure()) return rc.Miss();
} }
else else
{ {
@ -193,19 +193,27 @@ internal class FileAccessor : IDisposable
if (_lastResult.IsFailure()) if (_lastResult.IsFailure())
return _lastResult; return _lastResult;
WriteState oldWriteState = _writeState; WriteState originalWriteState = _writeState;
using ScopedSetter<WriteState> setter = using ScopedSetter<WriteState> setter =
ScopedSetter<WriteState>.MakeScopedSetter(ref _writeState, WriteState.Failed); ScopedSetter<WriteState>.MakeScopedSetter(ref _writeState, WriteState.Failed);
Result rc = UpdateLastResult(_file.Get.SetSize(size)); if (_filePathHash is not null)
if (rc.IsFailure()) return rc;
if (_filePathHash.Data != 0)
{ {
throw new NotImplementedException(); using UniqueLock lk = Hos.Fs.Impl.LockPathBasedFileDataCacheEntries();
Result rc = UpdateLastResult(_file.Get.SetSize(size));
if (rc.IsFailure()) return rc.Miss();
Hos.Fs.Impl.InvalidatePathBasedFileDataCacheEntry(_parentFileSystem, in _filePathHash.Value, _pathHashIndex);
if (rc.IsFailure()) return rc.Miss();
}
else
{
Result rc = UpdateLastResult(_file.Get.SetSize(size));
if (rc.IsFailure()) return rc;
} }
setter.Set(oldWriteState); setter.Set(originalWriteState);
return Result.Success; return Result.Success;
} }
@ -224,4 +232,4 @@ internal class FileAccessor : IDisposable
{ {
return _file.Get.OperateRange(outBuffer, operationId, offset, size, inBuffer); return _file.Get.OperateRange(outBuffer, operationId, offset, size, inBuffer);
} }
} }

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using LibHac.Common; using LibHac.Common;
using LibHac.Diag; using LibHac.Diag;
using LibHac.Fs.Fsa; using LibHac.Fs.Fsa;
using LibHac.Fs.Shim;
using LibHac.Os; using LibHac.Os;
using LibHac.Util; using LibHac.Util;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem; using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
@ -10,6 +11,13 @@ using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
// ReSharper disable once CheckNamespace // ReSharper disable once CheckNamespace
namespace LibHac.Fs.Impl; namespace LibHac.Fs.Impl;
/// <summary>
/// Provides access to a mounted <see cref="IFileSystem"/> and contains metadata and objects related to it.
/// This data includes the mount name, open files and directories, whether the access log is enabled for this file
/// system, whether caching is being used, how to get a save file system's <see cref="SaveDataAttribute"/> and
/// the target used to include a save file system in a multi-commit operation.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
internal class FileSystemAccessor : IDisposable internal class FileSystemAccessor : IDisposable
{ {
private const string EmptyMountNameMessage = "Error: Mount failed because the mount name was empty.\n"; private const string EmptyMountNameMessage = "Error: Mount failed because the mount name was empty.\n";
@ -31,7 +39,7 @@ internal class FileSystemAccessor : IDisposable
private bool _isPathCacheAttached; private bool _isPathCacheAttached;
private IMultiCommitTarget _multiCommitTarget; private IMultiCommitTarget _multiCommitTarget;
private PathFlags _pathFlags; private PathFlags _pathFlags;
private Optional<Ncm.DataId> _dataId; private IStorage _storageForPurgeFileDataCache;
internal HorizonClient Hos { get; } internal HorizonClient Hos { get; }
@ -44,7 +52,7 @@ internal class FileSystemAccessor : IDisposable
_fileSystem = new UniqueRef<IFileSystem>(ref fileSystem); _fileSystem = new UniqueRef<IFileSystem>(ref fileSystem);
_openFiles = new LinkedList<FileAccessor>(); _openFiles = new LinkedList<FileAccessor>();
_openDirectories = new LinkedList<DirectoryAccessor>(); _openDirectories = new LinkedList<DirectoryAccessor>();
_openListLock.Initialize(); _openListLock = new SdkMutexType();
_mountNameGenerator = new UniqueRef<ICommonMountNameGenerator>(ref mountNameGenerator); _mountNameGenerator = new UniqueRef<ICommonMountNameGenerator>(ref mountNameGenerator);
_saveDataAttributeGetter = new UniqueRef<ISaveDataAttributeGetter>(ref saveAttributeGetter); _saveDataAttributeGetter = new UniqueRef<ISaveDataAttributeGetter>(ref saveAttributeGetter);
_multiCommitTarget = multiCommitTarget; _multiCommitTarget = multiCommitTarget;
@ -90,7 +98,8 @@ internal class FileSystemAccessor : IDisposable
if (_isPathCacheAttached) if (_isPathCacheAttached)
{ {
throw new NotImplementedException(); using UniqueLock lk = Hos.Fs.Impl.LockPathBasedFileDataCacheEntries();
Hos.Fs.Impl.InvalidatePathBasedFileDataCacheEntries(this);
} }
} }
@ -113,7 +122,16 @@ internal class FileSystemAccessor : IDisposable
} }
public void SetAccessLog(bool isEnabled) => _isAccessLogEnabled = isEnabled; public void SetAccessLog(bool isEnabled) => _isAccessLogEnabled = isEnabled;
public void SetFileDataCacheAttachable(bool isAttachable) => _isDataCacheAttachable = isAttachable;
public void SetFileDataCacheAttachable(bool isAttachable, IStorage storageForPurgeFileDataCache)
{
if (isAttachable)
Assert.SdkAssert(storageForPurgeFileDataCache is not null);
_isDataCacheAttachable = isAttachable;
_storageForPurgeFileDataCache = storageForPurgeFileDataCache;
}
public void SetPathBasedFileDataCacheAttachable(bool isAttachable) => _isPathCacheAttachable = isAttachable; public void SetPathBasedFileDataCacheAttachable(bool isAttachable) => _isPathCacheAttachable = isAttachable;
public bool IsEnabledAccessLog() => _isAccessLogEnabled; public bool IsEnabledAccessLog() => _isAccessLogEnabled;
@ -126,10 +144,7 @@ internal class FileSystemAccessor : IDisposable
_isPathCacheAttached = true; _isPathCacheAttached = true;
} }
public Optional<Ncm.DataId> GetDataId() => _dataId; private Result SetUpPath(ref Path path, U8Span pathBuffer)
public void SetDataId(Optional<Ncm.DataId> dataId) => _dataId = dataId;
public Result SetUpPath(ref Path path, U8Span pathBuffer)
{ {
Result rc = PathFormatter.IsNormalized(out bool isNormalized, out _, pathBuffer, _pathFlags); Result rc = PathFormatter.IsNormalized(out bool isNormalized, out _, pathBuffer, _pathFlags);
@ -168,7 +183,12 @@ internal class FileSystemAccessor : IDisposable
if (_isPathCacheAttached) if (_isPathCacheAttached)
{ {
throw new NotImplementedException(); using UniqueLock lk = Hos.Fs.Impl.LockPathBasedFileDataCacheEntries();
rc = _fileSystem.Get.CreateFile(in pathNormalized, size, option);
if (rc.IsFailure()) return rc.Miss();
Hos.Fs.Impl.InvalidatePathBasedFileDataCacheEntry(this, in pathNormalized);
} }
else else
{ {
@ -251,7 +271,12 @@ internal class FileSystemAccessor : IDisposable
if (_isPathCacheAttached) if (_isPathCacheAttached)
{ {
throw new NotImplementedException(); using UniqueLock lk = Hos.Fs.Impl.LockPathBasedFileDataCacheEntries();
rc = _fileSystem.Get.RenameFile(in currentPathNormalized, in newPathNormalized);
if (rc.IsFailure()) return rc.Miss();
Hos.Fs.Impl.InvalidatePathBasedFileDataCacheEntry(this, in newPathNormalized);
} }
else else
{ {
@ -274,7 +299,12 @@ internal class FileSystemAccessor : IDisposable
if (_isPathCacheAttached) if (_isPathCacheAttached)
{ {
throw new NotImplementedException(); using UniqueLock lk = Hos.Fs.Impl.LockPathBasedFileDataCacheEntries();
rc = _fileSystem.Get.RenameDirectory(in currentPathNormalized, in newPathNormalized);
if (rc.IsFailure()) return rc.Miss();
Hos.Fs.Impl.InvalidatePathBasedFileDataCacheEntries(this);
} }
else else
{ {
@ -347,11 +377,17 @@ internal class FileSystemAccessor : IDisposable
{ {
if (mode.HasFlag(OpenMode.AllowAppend)) if (mode.HasFlag(OpenMode.AllowAppend))
{ {
throw new NotImplementedException(); using UniqueLock lk = Hos.Fs.Impl.LockPathBasedFileDataCacheEntries();
Hos.Fs.Impl.InvalidatePathBasedFileDataCacheEntry(this, in pathNormalized);
} }
else else
{ {
throw new NotImplementedException(); var hash = new Box<FilePathHash>();
if (Hos.Fs.Impl.FindPathBasedFileDataCacheEntry(out hash.Value, out int hashIndex, this, in pathNormalized))
{
accessor.SetFilePathHash(hash, hashIndex);
}
} }
} }
@ -432,6 +468,13 @@ internal class FileSystemAccessor : IDisposable
return Result.Success; return Result.Success;
} }
public void PurgeFileDataCache(FileDataCacheAccessor accessor)
{
Assert.SdkAssert(_storageForPurgeFileDataCache is not null);
accessor.Purge(_storageForPurgeFileDataCache);
}
public U8Span GetName() public U8Span GetName()
{ {
return new U8Span(_mountName.Name); return new U8Span(_mountName.Name);
@ -470,11 +513,6 @@ internal class FileSystemAccessor : IDisposable
} }
} }
public void PurgeFileDataCache(FileDataCacheAccessor cacheAccessor)
{
cacheAccessor.Purge(_fileSystem.Get);
}
internal void NotifyCloseFile(FileAccessor file) internal void NotifyCloseFile(FileAccessor file)
{ {
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _openListLock); using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _openListLock);
@ -673,4 +711,4 @@ internal class FileSystemAccessor : IDisposable
} }
} }
} }
} }

View file

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using LibHac.Common; using LibHac.Common;
using LibHac.Diag; using LibHac.Diag;
using LibHac.Ncm;
using LibHac.Os; using LibHac.Os;
using LibHac.Util; using LibHac.Util;
@ -14,7 +13,7 @@ namespace LibHac.Fs.Impl;
/// Holds a list of <see cref="FileSystemAccessor"/>s that are indexed by their name. /// Holds a list of <see cref="FileSystemAccessor"/>s that are indexed by their name.
/// These may be retrieved or removed using their name as a key. /// These may be retrieved or removed using their name as a key.
/// </summary> /// </summary>
/// <remarks>Based on FS 12.1.0 (nnSdk 12.3.1)</remarks> /// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
internal class MountTable : IDisposable internal class MountTable : IDisposable
{ {
private LinkedList<FileSystemAccessor> _fileSystemList; private LinkedList<FileSystemAccessor> _fileSystemList;
@ -76,9 +75,11 @@ internal class MountTable : IDisposable
currentNode is not null; currentNode is not null;
currentNode = currentNode.Next) currentNode = currentNode.Next)
{ {
if (!Matches(currentNode.Value, name)) continue; if (Matches(currentNode.Value, name))
accessor = currentNode.Value; {
return Result.Success; accessor = currentNode.Value;
return Result.Success;
}
} }
return ResultFs.NotMounted.Log(); return ResultFs.NotMounted.Log();
@ -121,44 +122,4 @@ internal class MountTable : IDisposable
return true; return true;
} }
public int GetDataIdCount()
{
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
int count = 0;
for (LinkedListNode<FileSystemAccessor> currentNode = _fileSystemList.First;
currentNode is not null;
currentNode = currentNode.Next)
{
if (currentNode.Value.GetDataId().HasValue)
count++;
}
return count;
}
public Result ListDataId(out int dataIdCount, Span<DataId> dataIdBuffer)
{
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _mutex);
int count = 0;
for (LinkedListNode<FileSystemAccessor> currentNode = _fileSystemList.First;
currentNode is not null && count < dataIdBuffer.Length;
currentNode = currentNode.Next)
{
Optional<DataId> dataId = currentNode.Value.GetDataId();
if (dataId.HasValue)
{
dataIdBuffer[count] = dataId.Value;
count++;
}
}
dataIdCount = count;
return Result.Success;
}
} }

View file

@ -97,9 +97,9 @@ public static class Registrar
if (!accessor.HasValue) if (!accessor.HasValue)
return ResultFs.AllocationMemoryFailedInRegisterB.Log(); return ResultFs.AllocationMemoryFailedInRegisterB.Log();
accessor.Get.SetFileDataCacheAttachable(useDataCache); // accessor.Get.SetFileDataCacheAttachable(useDataCache);
accessor.Get.SetPathBasedFileDataCacheAttachable(usePathCache); accessor.Get.SetPathBasedFileDataCacheAttachable(usePathCache);
accessor.Get.SetDataId(dataId); // accessor.Get.SetDataId(dataId);
Result rc = fs.Impl.Register(ref accessor.Ref()); Result rc = fs.Impl.Register(ref accessor.Ref());
if (rc.IsFailure()) return rc.Miss(); if (rc.IsFailure()) return rc.Miss();
@ -111,4 +111,4 @@ public static class Registrar
{ {
fs.Impl.Unregister(name); fs.Impl.Unregister(name);
} }
} }

View file

@ -1,7 +1,5 @@
using System; using LibHac.Common;
using LibHac.Common;
using LibHac.Fs.Impl; using LibHac.Fs.Impl;
using LibHac.Ncm;
namespace LibHac.Fs.Fsa; namespace LibHac.Fs.Fsa;
@ -18,7 +16,7 @@ internal struct UserMountTableGlobals
/// <summary> /// <summary>
/// Contains functions for adding, removing and retrieving <see cref="FileSystemAccessor"/>s from the mount table. /// Contains functions for adding, removing and retrieving <see cref="FileSystemAccessor"/>s from the mount table.
/// </summary> /// </summary>
/// <remarks>Based on FS 12.1.0 (nnSdk 12.3.1)</remarks> /// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
internal static class UserMountTable internal static class UserMountTable
{ {
public static Result Register(this FileSystemClientImpl fs, ref UniqueRef<FileSystemAccessor> fileSystem) public static Result Register(this FileSystemClientImpl fs, ref UniqueRef<FileSystemAccessor> fileSystem)
@ -35,15 +33,4 @@ internal static class UserMountTable
{ {
fs.Globals.UserMountTable.MountTable.Unmount(name); fs.Globals.UserMountTable.MountTable.Unmount(name);
} }
}
public static int GetMountedDataIdCount(this FileSystemClientImpl fs)
{
return fs.Globals.UserMountTable.MountTable.GetDataIdCount();
}
public static Result ListMountedDataId(this FileSystemClientImpl fs, out int dataIdCount,
Span<DataId> dataIdBuffer)
{
return fs.Globals.UserMountTable.MountTable.ListDataId(out dataIdCount, dataIdBuffer);
}
}

View file

@ -3,9 +3,13 @@ using LibHac.Fs.Fsa;
namespace LibHac.Fs.Impl; namespace LibHac.Fs.Impl;
/// <summary>
/// Provides access to an <see cref="IFileDataCache"/>.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
internal class FileDataCacheAccessor internal class FileDataCacheAccessor
{ {
private IFileDataCache _cache; private readonly IFileDataCache _cache;
public FileDataCacheAccessor(IFileDataCache cache) public FileDataCacheAccessor(IFileDataCache cache)
{ {
@ -18,8 +22,8 @@ internal class FileDataCacheAccessor
return _cache.Read(file, out bytesRead, offset, destination, in option, ref cacheAccessResult); return _cache.Read(file, out bytesRead, offset, destination, in option, ref cacheAccessResult);
} }
public void Purge(IFileSystem fileSystem) public void Purge(IStorage storage)
{ {
_cache.Purge(fileSystem); _cache.Purge(storage);
} }
} }

View file

@ -1,6 +1,8 @@
namespace LibHac.Fs.Impl; using LibHac.Common.FixedArrays;
namespace LibHac.Fs.Impl;
public struct FilePathHash public struct FilePathHash
{ {
public int Data; public Array4<byte> Data;
} }

View file

@ -7,11 +7,15 @@ using LibHac.Fs.Fsa;
namespace LibHac.Fs.Impl; namespace LibHac.Fs.Impl;
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
/// <summary>
/// Provides a system for caching reads to one or more file systems.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
internal abstract class IFileDataCache : IDisposable internal abstract class IFileDataCache : IDisposable
{ {
public abstract void Dispose(); public abstract void Dispose();
public abstract void Purge(IFileSystem fileSystem); public abstract void Purge(IStorage storage);
protected abstract Result DoRead(IFile file, out long bytesRead, long offset, Span<byte> destination, protected abstract Result DoRead(IFile file, out long bytesRead, long offset, Span<byte> destination,
in ReadOption option, ref FileDataCacheAccessResult cacheAccessResult); in ReadOption option, ref FileDataCacheAccessResult cacheAccessResult);
@ -88,4 +92,4 @@ internal struct FileDataCacheAccessResult
_regionCount++; _regionCount++;
} }
} }
} }

View file

@ -0,0 +1,183 @@
// ReSharper disable UnusedMember.Local
using System;
using LibHac.Common;
using LibHac.Diag;
using LibHac.Fs.Impl;
using LibHac.Os;
namespace LibHac.Fs.Impl
{
/// <summary>
/// Handles getting scoped read access to the global file data cache.
/// </summary>
/// <remarks>Based on FS 13.1.0 (nnSdk 13.4.0)</remarks>
internal struct GlobalFileDataCacheAccessorReadableScopedPointer : IDisposable
{
private FileDataCacheAccessor _accessor;
private ReaderWriterLock _lock;
public void Dispose()
{
_lock?.ReleaseReadLock();
}
public readonly FileDataCacheAccessor Get() => _accessor;
public void Set(FileDataCacheAccessor accessor, ReaderWriterLock rwLock)
{
if (_lock is not null && _lock != rwLock)
_lock.ReleaseReadLock();
_accessor = accessor;
_lock = rwLock;
}
}
}
namespace LibHac.Fs.Shim
{
public static class FileDataCacheShim
{
internal struct Globals : IDisposable
{
public nint FileSystemProxyServiceObjectInitGuard;
public GlobalFileDataCacheAccessorHolder GlobalFileDataCacheAccessorHolder;
public void Dispose()
{
GlobalFileDataCacheAccessorHolder.Dispose();
}
}
internal class GlobalFileDataCacheAccessorHolder
{
private FileDataCacheAccessor _accessor;
private ReaderWriterLock _accessorLock;
private long _cacheSize;
private bool _isDefault;
public GlobalFileDataCacheAccessorHolder(HorizonClient hos)
{
_accessorLock = new ReaderWriterLock(hos.Os);
}
public void Dispose()
{
_accessorLock?.Dispose();
}
public ReaderWriterLock GetLock() => _accessorLock;
public bool HasAccessor()
{
Assert.SdkAssert(_accessorLock.IsReadLockHeld() || _accessorLock.IsWriteLockHeldByCurrentThread());
return _accessor is not null;
}
public void SetAccessor()
{
Assert.SdkAssert(_accessorLock.IsWriteLockHeldByCurrentThread());
_accessor = null;
}
public void SetAccessor(FileDataCacheAccessor accessor, long cacheSize, bool isDefault)
{
Assert.SdkAssert(_accessorLock.IsWriteLockHeldByCurrentThread());
_accessor = accessor;
_cacheSize = cacheSize;
_isDefault = isDefault;
}
public FileDataCacheAccessor GetAccessor()
{
Assert.SdkAssert(_accessorLock.IsReadLockHeld() || _accessorLock.IsWriteLockHeldByCurrentThread());
return _accessor;
}
public long GetCacheSize()
{
Assert.SdkAssert(_accessorLock.IsReadLockHeld() || _accessorLock.IsWriteLockHeldByCurrentThread());
return _cacheSize;
}
public bool IsDefaultGlobalFileDataCache()
{
Assert.SdkAssert(_accessorLock.IsReadLockHeld() || _accessorLock.IsWriteLockHeldByCurrentThread());
Assert.SdkNotNull(_accessor);
return _isDefault;
}
}
private static GlobalFileDataCacheAccessorHolder GetGlobalFileDataCacheAccessorHolder(FileSystemClient fs)
{
ref Globals g = ref fs.Globals.FileDataCache;
using var guard = new InitializationGuard(ref g.FileSystemProxyServiceObjectInitGuard,
fs.Globals.InitMutex);
if (!guard.IsInitialized)
{
g.GlobalFileDataCacheAccessorHolder = new GlobalFileDataCacheAccessorHolder(fs.Hos);
}
return g.GlobalFileDataCacheAccessorHolder;
}
private static Result EnableGlobalFileDataCacheImpl(FileSystemClient fs, Memory<byte> buffer, bool isDefault)
{
throw new NotImplementedException();
}
private static Result DisableGlobalFileDataCacheImpl(FileSystemClient fs)
{
throw new NotImplementedException();
}
private static int PrintDefaultGlobalFileDataCacheAccessLog(Span<byte> textBuffer)
{
throw new NotImplementedException();
}
internal static bool IsGlobalFileDataCacheEnabled(this FileSystemClientImpl fs)
{
return false;
}
internal static bool TryGetGlobalFileDataCacheAccessor(this FileSystemClientImpl fs,
ref GlobalFileDataCacheAccessorReadableScopedPointer scopedPointer)
{
throw new NotImplementedException();
}
internal static void SetGlobalFileDataCacheAccessorForDebug(this FileSystemClientImpl fs,
FileDataCacheAccessor accessor)
{
throw new NotImplementedException();
}
public static void EnableGlobalFileDataCache(this FileSystemClient fs, Memory<byte> buffer)
{
throw new NotImplementedException();
}
public static void DisableGlobalFileDataCache(this FileSystemClient fs)
{
throw new NotImplementedException();
}
public static void EnableDefaultGlobalFileDataCache(this FileSystemClient fs, Memory<byte> buffer)
{
throw new NotImplementedException();
}
public static bool IsDefaultGlobalFileDataCacheEnabled(this FileSystemClient fs)
{
throw new NotImplementedException();
}
}
}

View file

@ -0,0 +1,62 @@
using System;
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.Fs.Impl;
namespace LibHac.Fs.Shim;
public static class PathBasedFileDataCacheShim
{
internal static UniqueLock LockPathBasedFileDataCacheEntries(this FileSystemClientImpl fs)
{
throw new NotImplementedException();
}
internal static void InvalidatePathBasedFileDataCacheEntries(this FileSystemClientImpl fs,
FileSystemAccessor fsAccessor)
{
throw new NotImplementedException();
}
internal static void InvalidatePathBasedFileDataCacheEntry(this FileSystemClientImpl fs,
FileSystemAccessor fsAccessor, in Path path)
{
throw new NotImplementedException();
}
internal static void InvalidatePathBasedFileDataCacheEntry(this FileSystemClientImpl fs,
FileSystemAccessor fsAccessor, in FilePathHash hash, int hashIndex)
{
throw new NotImplementedException();
}
internal static bool FindPathBasedFileDataCacheEntry(this FileSystemClientImpl fs, out FilePathHash outHash,
out int outHashIndex, FileSystemAccessor fsAccessor, in Path path)
{
throw new NotImplementedException();
}
internal static Result ReadViaPathBasedFileDataCache(this FileSystemClientImpl fs, IFile file, int openMode,
FileSystemAccessor fileSystem, in FilePathHash hash, int hashIndex, out long bytesRead, long offset,
Span<byte> buffer, in ReadOption option, ref FileDataCacheAccessResult outCacheAccessResult)
{
throw new NotImplementedException();
}
internal static Result WriteViaPathBasedFileDataCache(this FileSystemClientImpl fs, IFile file, int openMode,
FileSystemAccessor fileSystem, in FilePathHash hash, int hashIndex, long offset, ReadOnlySpan<byte> buffer,
in WriteOption option)
{
throw new NotImplementedException();
}
public static Result EnableIndividualFileDataCache(this FileSystemClient fs, U8Span path, Memory<byte> buffer)
{
throw new NotImplementedException();
}
public static void DisableIndividualFileDataCache(this FileSystemClient fs, U8Span path)
{
throw new NotImplementedException();
}
}

View file

@ -98,7 +98,7 @@ public static class ReaderWriterLockApi
} }
} }
public class ReaderWriterLock : ISharedMutex public class ReaderWriterLock : ISharedMutex, IDisposable
{ {
public const int ReaderWriterLockCountMax = (1 << 15) - 1; public const int ReaderWriterLockCountMax = (1 << 15) - 1;
public const int ReadWriteLockWaiterCountMax = (1 << 8) - 1; public const int ReadWriteLockWaiterCountMax = (1 << 8) - 1;
@ -112,6 +112,11 @@ public class ReaderWriterLock : ISharedMutex
_os.InitializeReaderWriterLock(ref _rwLock); _os.InitializeReaderWriterLock(ref _rwLock);
} }
public void Dispose()
{
_os.FinalizeReaderWriterLock(ref _rwLock);
}
public void AcquireReadLock() public void AcquireReadLock()
{ {
_os.AcquireReadLock(ref _rwLock); _os.AcquireReadLock(ref _rwLock);
@ -191,4 +196,4 @@ public class ReaderWriterLock : ISharedMutex
{ {
return ref _rwLock; return ref _rwLock;
} }
} }