From cf7062788f96b7c5657c4140d01540964bba35bb Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Wed, 22 Dec 2021 16:43:04 -0700 Subject: [PATCH] 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 --- src/LibHac/Common/Box.cs | 13 ++ src/LibHac/Fs/FileSystemClient.cs | 3 +- src/LibHac/Fs/Fsa/DirectoryAccessor.cs | 6 +- src/LibHac/Fs/Fsa/FileAccessor.cs | 100 +++++----- src/LibHac/Fs/Fsa/FileSystemAccessor.cs | 76 ++++++-- src/LibHac/Fs/Fsa/MountTable.cs | 51 +----- src/LibHac/Fs/Fsa/Registrar.cs | 6 +- src/LibHac/Fs/Fsa/UserMountTable.cs | 19 +- src/LibHac/Fs/Impl/FileDataCacheAccessor.cs | 12 +- src/LibHac/Fs/Impl/FilePathHash.cs | 8 +- src/LibHac/Fs/Impl/IFileDataCache.cs | 8 +- src/LibHac/Fs/Shim/FileDataCache.cs | 183 +++++++++++++++++++ src/LibHac/Fs/Shim/PathBasedFileDataCache.cs | 62 +++++++ src/LibHac/Os/ReaderWriterLock.cs | 9 +- 14 files changed, 414 insertions(+), 142 deletions(-) create mode 100644 src/LibHac/Common/Box.cs create mode 100644 src/LibHac/Fs/Shim/FileDataCache.cs create mode 100644 src/LibHac/Fs/Shim/PathBasedFileDataCache.cs diff --git a/src/LibHac/Common/Box.cs b/src/LibHac/Common/Box.cs new file mode 100644 index 00000000..6d31ac62 --- /dev/null +++ b/src/LibHac/Common/Box.cs @@ -0,0 +1,13 @@ +namespace LibHac.Common; + +public class Box where T : struct +{ + private T _value; + + public ref T Value => ref _value; + + public Box() + { + _value = new T(); + } +} \ No newline at end of file diff --git a/src/LibHac/Fs/FileSystemClient.cs b/src/LibHac/Fs/FileSystemClient.cs index 898a1702..f6e285a2 100644 --- a/src/LibHac/Fs/FileSystemClient.cs +++ b/src/LibHac/Fs/FileSystemClient.cs @@ -35,6 +35,7 @@ internal struct FileSystemClientGlobals : IDisposable public FsContextHandlerGlobals FsContextHandler; public ResultHandlingUtilityGlobals ResultHandlingUtility; public DirectorySaveDataFileSystemGlobals DirectorySaveDataFileSystem; + public FileDataCacheShim.Globals FileDataCache; public void Initialize(FileSystemClient fsClient, HorizonClient horizonClient) { @@ -60,4 +61,4 @@ public readonly struct FileSystemClientImpl internal ref FileSystemClientGlobals Globals => ref Fs.Globals; internal FileSystemClientImpl(FileSystemClient parentClient) => Fs = parentClient; -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/Fsa/DirectoryAccessor.cs b/src/LibHac/Fs/Fsa/DirectoryAccessor.cs index bf8962e0..b08a98aa 100644 --- a/src/LibHac/Fs/Fsa/DirectoryAccessor.cs +++ b/src/LibHac/Fs/Fsa/DirectoryAccessor.cs @@ -5,6 +5,10 @@ using LibHac.Fs.Fsa; // ReSharper disable once CheckNamespace namespace LibHac.Fs.Impl; +/// +/// Provides access to a directory in a mounted file system and handles closing the directory. +/// +/// Based on FS 13.1.0 (nnSdk 13.4.0) internal class DirectoryAccessor : IDisposable { private UniqueRef _directory; @@ -35,4 +39,4 @@ internal class DirectoryAccessor : IDisposable { return _directory.Get.GetEntryCount(out entryCount); } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/Fsa/FileAccessor.cs b/src/LibHac/Fs/Fsa/FileAccessor.cs index 6f61137c..5352accb 100644 --- a/src/LibHac/Fs/Fsa/FileAccessor.cs +++ b/src/LibHac/Fs/Fsa/FileAccessor.cs @@ -2,6 +2,7 @@ using LibHac.Common; using LibHac.Diag; using LibHac.Fs.Fsa; +using LibHac.Fs.Shim; using LibHac.Os; using static LibHac.Fs.Impl.AccessLogStrings; @@ -15,6 +16,10 @@ internal enum WriteState Failed, } +/// +/// Provides access to a mount and handles caching it. +/// +/// Based on FS 13.1.0 (nnSdk 13.4.0) internal class FileAccessor : IDisposable { 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 Result _lastResult; private OpenMode _openMode; - private FilePathHash _filePathHash; - // ReSharper disable once NotAccessedField.Local + private Box _filePathHash; private int _pathHashIndex; internal HorizonClient Hos { get; } @@ -59,7 +63,7 @@ internal class FileAccessor : IDisposable public WriteState GetWriteState() => _writeState; public FileSystemAccessor GetParent() => _parentFileSystem; - public void SetFilePathHash(FilePathHash filePathHash, int index) + public void SetFilePathHash(Box filePathHash, int index) { _filePathHash = filePathHash; _pathHashIndex = index; @@ -73,18 +77,18 @@ internal class FileAccessor : IDisposable return result; } - public Result ReadWithoutCacheAccessLog(out long bytesRead, long offset, Span destination, - in ReadOption option) - { - return _file.Get.Read(out bytesRead, offset, destination, in option); - } - private Result ReadWithCacheAccessLog(out long bytesRead, long offset, Span destination, in ReadOption option, bool usePathCache, bool useDataCache) { throw new NotImplementedException(); } + public Result ReadWithoutCacheAccessLog(out long bytesRead, long offset, Span destination, + in ReadOption option) + { + return _file.Get.Read(out bytesRead, offset, destination, in option); + } + public Result Read(out long bytesRead, long offset, Span destination, in ReadOption option) { UnsafeHelpers.SkipParamInit(out bytesRead); @@ -113,42 +117,36 @@ internal class FileAccessor : IDisposable return _lastResult; } - // ReSharper disable ConditionIsAlwaysTrueOrFalse - bool usePathCache = _parentFileSystem is not null && _filePathHash.Data != 0; + bool usePathCache = _parentFileSystem is not null && _filePathHash is not null; + 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) { return ReadWithCacheAccessLog(out bytesRead, offset, destination, in option, usePathCache, 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 { - 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 - { - rc = ReadWithoutCacheAccessLog(out bytesRead, offset, destination, in option); - } - - return rc; + rc = ReadWithoutCacheAccessLog(out bytesRead, offset, destination, in option); } - // ReSharper restore ConditionIsAlwaysTrueOrFalse + + return rc; } public Result Write(long offset, ReadOnlySpan source, in WriteOption option) @@ -159,9 +157,11 @@ internal class FileAccessor : IDisposable using ScopedSetter setter = ScopedSetter.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 { @@ -193,19 +193,27 @@ internal class FileAccessor : IDisposable if (_lastResult.IsFailure()) return _lastResult; - WriteState oldWriteState = _writeState; + WriteState originalWriteState = _writeState; using ScopedSetter setter = ScopedSetter.MakeScopedSetter(ref _writeState, WriteState.Failed); - Result rc = UpdateLastResult(_file.Get.SetSize(size)); - if (rc.IsFailure()) return rc; - - if (_filePathHash.Data != 0) + if (_filePathHash is not null) { - 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; } @@ -224,4 +232,4 @@ internal class FileAccessor : IDisposable { return _file.Get.OperateRange(outBuffer, operationId, offset, size, inBuffer); } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/Fsa/FileSystemAccessor.cs b/src/LibHac/Fs/Fsa/FileSystemAccessor.cs index bd113e67..d24c7b91 100644 --- a/src/LibHac/Fs/Fsa/FileSystemAccessor.cs +++ b/src/LibHac/Fs/Fsa/FileSystemAccessor.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using LibHac.Common; using LibHac.Diag; using LibHac.Fs.Fsa; +using LibHac.Fs.Shim; using LibHac.Os; using LibHac.Util; using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem; @@ -10,6 +11,13 @@ using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem; // ReSharper disable once CheckNamespace namespace LibHac.Fs.Impl; +/// +/// Provides access to a mounted 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 and +/// the target used to include a save file system in a multi-commit operation. +/// +/// Based on FS 13.1.0 (nnSdk 13.4.0) internal class FileSystemAccessor : IDisposable { 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 IMultiCommitTarget _multiCommitTarget; private PathFlags _pathFlags; - private Optional _dataId; + private IStorage _storageForPurgeFileDataCache; internal HorizonClient Hos { get; } @@ -44,7 +52,7 @@ internal class FileSystemAccessor : IDisposable _fileSystem = new UniqueRef(ref fileSystem); _openFiles = new LinkedList(); _openDirectories = new LinkedList(); - _openListLock.Initialize(); + _openListLock = new SdkMutexType(); _mountNameGenerator = new UniqueRef(ref mountNameGenerator); _saveDataAttributeGetter = new UniqueRef(ref saveAttributeGetter); _multiCommitTarget = multiCommitTarget; @@ -90,7 +98,8 @@ internal class FileSystemAccessor : IDisposable 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 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 bool IsEnabledAccessLog() => _isAccessLogEnabled; @@ -126,10 +144,7 @@ internal class FileSystemAccessor : IDisposable _isPathCacheAttached = true; } - public Optional GetDataId() => _dataId; - public void SetDataId(Optional dataId) => _dataId = dataId; - - public Result SetUpPath(ref Path path, U8Span pathBuffer) + private Result SetUpPath(ref Path path, U8Span pathBuffer) { Result rc = PathFormatter.IsNormalized(out bool isNormalized, out _, pathBuffer, _pathFlags); @@ -168,7 +183,12 @@ internal class FileSystemAccessor : IDisposable 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 { @@ -251,7 +271,12 @@ internal class FileSystemAccessor : IDisposable 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 { @@ -274,7 +299,12 @@ internal class FileSystemAccessor : IDisposable 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 { @@ -347,11 +377,17 @@ internal class FileSystemAccessor : IDisposable { if (mode.HasFlag(OpenMode.AllowAppend)) { - throw new NotImplementedException(); + using UniqueLock lk = Hos.Fs.Impl.LockPathBasedFileDataCacheEntries(); + Hos.Fs.Impl.InvalidatePathBasedFileDataCacheEntry(this, in pathNormalized); } else { - throw new NotImplementedException(); + var hash = new Box(); + + 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; } + public void PurgeFileDataCache(FileDataCacheAccessor accessor) + { + Assert.SdkAssert(_storageForPurgeFileDataCache is not null); + + accessor.Purge(_storageForPurgeFileDataCache); + } + public U8Span GetName() { 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) { using ScopedLock scopedLock = ScopedLock.Lock(ref _openListLock); @@ -673,4 +711,4 @@ internal class FileSystemAccessor : IDisposable } } } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/Fsa/MountTable.cs b/src/LibHac/Fs/Fsa/MountTable.cs index 2c2609ad..07fcc727 100644 --- a/src/LibHac/Fs/Fsa/MountTable.cs +++ b/src/LibHac/Fs/Fsa/MountTable.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using LibHac.Common; using LibHac.Diag; -using LibHac.Ncm; using LibHac.Os; using LibHac.Util; @@ -14,7 +13,7 @@ namespace LibHac.Fs.Impl; /// Holds a list of s that are indexed by their name. /// These may be retrieved or removed using their name as a key. /// -/// Based on FS 12.1.0 (nnSdk 12.3.1) +/// Based on FS 13.1.0 (nnSdk 13.4.0) internal class MountTable : IDisposable { private LinkedList _fileSystemList; @@ -76,9 +75,11 @@ internal class MountTable : IDisposable currentNode is not null; currentNode = currentNode.Next) { - if (!Matches(currentNode.Value, name)) continue; - accessor = currentNode.Value; - return Result.Success; + if (Matches(currentNode.Value, name)) + { + accessor = currentNode.Value; + return Result.Success; + } } return ResultFs.NotMounted.Log(); @@ -121,44 +122,4 @@ internal class MountTable : IDisposable return true; } - - public int GetDataIdCount() - { - using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex); - - int count = 0; - - for (LinkedListNode 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 dataIdBuffer) - { - using ScopedLock scopedLock = ScopedLock.Lock(ref _mutex); - - int count = 0; - - for (LinkedListNode currentNode = _fileSystemList.First; - currentNode is not null && count < dataIdBuffer.Length; - currentNode = currentNode.Next) - { - Optional dataId = currentNode.Value.GetDataId(); - - if (dataId.HasValue) - { - dataIdBuffer[count] = dataId.Value; - count++; - } - } - - dataIdCount = count; - return Result.Success; - } } \ No newline at end of file diff --git a/src/LibHac/Fs/Fsa/Registrar.cs b/src/LibHac/Fs/Fsa/Registrar.cs index c523477c..1a2e06f0 100644 --- a/src/LibHac/Fs/Fsa/Registrar.cs +++ b/src/LibHac/Fs/Fsa/Registrar.cs @@ -97,9 +97,9 @@ public static class Registrar if (!accessor.HasValue) return ResultFs.AllocationMemoryFailedInRegisterB.Log(); - accessor.Get.SetFileDataCacheAttachable(useDataCache); + // accessor.Get.SetFileDataCacheAttachable(useDataCache); accessor.Get.SetPathBasedFileDataCacheAttachable(usePathCache); - accessor.Get.SetDataId(dataId); + // accessor.Get.SetDataId(dataId); Result rc = fs.Impl.Register(ref accessor.Ref()); if (rc.IsFailure()) return rc.Miss(); @@ -111,4 +111,4 @@ public static class Registrar { fs.Impl.Unregister(name); } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/Fsa/UserMountTable.cs b/src/LibHac/Fs/Fsa/UserMountTable.cs index 177e4382..dd860cc2 100644 --- a/src/LibHac/Fs/Fsa/UserMountTable.cs +++ b/src/LibHac/Fs/Fsa/UserMountTable.cs @@ -1,7 +1,5 @@ -using System; -using LibHac.Common; +using LibHac.Common; using LibHac.Fs.Impl; -using LibHac.Ncm; namespace LibHac.Fs.Fsa; @@ -18,7 +16,7 @@ internal struct UserMountTableGlobals /// /// Contains functions for adding, removing and retrieving s from the mount table. /// -/// Based on FS 12.1.0 (nnSdk 12.3.1) +/// Based on FS 13.1.0 (nnSdk 13.4.0) internal static class UserMountTable { public static Result Register(this FileSystemClientImpl fs, ref UniqueRef fileSystem) @@ -35,15 +33,4 @@ internal static class UserMountTable { 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 dataIdBuffer) - { - return fs.Globals.UserMountTable.MountTable.ListDataId(out dataIdCount, dataIdBuffer); - } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/Impl/FileDataCacheAccessor.cs b/src/LibHac/Fs/Impl/FileDataCacheAccessor.cs index 7c715c2b..1bab0250 100644 --- a/src/LibHac/Fs/Impl/FileDataCacheAccessor.cs +++ b/src/LibHac/Fs/Impl/FileDataCacheAccessor.cs @@ -3,9 +3,13 @@ using LibHac.Fs.Fsa; namespace LibHac.Fs.Impl; +/// +/// Provides access to an . +/// +/// Based on FS 13.1.0 (nnSdk 13.4.0) internal class FileDataCacheAccessor { - private IFileDataCache _cache; + private readonly 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); } - public void Purge(IFileSystem fileSystem) + public void Purge(IStorage storage) { - _cache.Purge(fileSystem); + _cache.Purge(storage); } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/Impl/FilePathHash.cs b/src/LibHac/Fs/Impl/FilePathHash.cs index 3f1531f8..80caf8ce 100644 --- a/src/LibHac/Fs/Impl/FilePathHash.cs +++ b/src/LibHac/Fs/Impl/FilePathHash.cs @@ -1,6 +1,8 @@ -namespace LibHac.Fs.Impl; +using LibHac.Common.FixedArrays; + +namespace LibHac.Fs.Impl; public struct FilePathHash { - public int Data; -} + public Array4 Data; +} \ No newline at end of file diff --git a/src/LibHac/Fs/Impl/IFileDataCache.cs b/src/LibHac/Fs/Impl/IFileDataCache.cs index c2051423..bfdd01ea 100644 --- a/src/LibHac/Fs/Impl/IFileDataCache.cs +++ b/src/LibHac/Fs/Impl/IFileDataCache.cs @@ -7,11 +7,15 @@ using LibHac.Fs.Fsa; namespace LibHac.Fs.Impl; // ReSharper disable once InconsistentNaming +/// +/// Provides a system for caching reads to one or more file systems. +/// +/// Based on FS 13.1.0 (nnSdk 13.4.0) internal abstract class IFileDataCache : IDisposable { 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 destination, in ReadOption option, ref FileDataCacheAccessResult cacheAccessResult); @@ -88,4 +92,4 @@ internal struct FileDataCacheAccessResult _regionCount++; } } -} +} \ No newline at end of file diff --git a/src/LibHac/Fs/Shim/FileDataCache.cs b/src/LibHac/Fs/Shim/FileDataCache.cs new file mode 100644 index 00000000..35138910 --- /dev/null +++ b/src/LibHac/Fs/Shim/FileDataCache.cs @@ -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 +{ + /// + /// Handles getting scoped read access to the global file data cache. + /// + /// Based on FS 13.1.0 (nnSdk 13.4.0) + 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 buffer, bool isDefault) + { + throw new NotImplementedException(); + } + + private static Result DisableGlobalFileDataCacheImpl(FileSystemClient fs) + { + throw new NotImplementedException(); + } + + private static int PrintDefaultGlobalFileDataCacheAccessLog(Span 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 buffer) + { + throw new NotImplementedException(); + } + + public static void DisableGlobalFileDataCache(this FileSystemClient fs) + { + throw new NotImplementedException(); + } + + public static void EnableDefaultGlobalFileDataCache(this FileSystemClient fs, Memory buffer) + { + throw new NotImplementedException(); + } + + public static bool IsDefaultGlobalFileDataCacheEnabled(this FileSystemClient fs) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/LibHac/Fs/Shim/PathBasedFileDataCache.cs b/src/LibHac/Fs/Shim/PathBasedFileDataCache.cs new file mode 100644 index 00000000..30499df5 --- /dev/null +++ b/src/LibHac/Fs/Shim/PathBasedFileDataCache.cs @@ -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 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 buffer, + in WriteOption option) + { + throw new NotImplementedException(); + } + + public static Result EnableIndividualFileDataCache(this FileSystemClient fs, U8Span path, Memory buffer) + { + throw new NotImplementedException(); + } + + public static void DisableIndividualFileDataCache(this FileSystemClient fs, U8Span path) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/LibHac/Os/ReaderWriterLock.cs b/src/LibHac/Os/ReaderWriterLock.cs index 3a2eb19b..45dfda8b 100644 --- a/src/LibHac/Os/ReaderWriterLock.cs +++ b/src/LibHac/Os/ReaderWriterLock.cs @@ -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 ReadWriteLockWaiterCountMax = (1 << 8) - 1; @@ -112,6 +112,11 @@ public class ReaderWriterLock : ISharedMutex _os.InitializeReaderWriterLock(ref _rwLock); } + public void Dispose() + { + _os.FinalizeReaderWriterLock(ref _rwLock); + } + public void AcquireReadLock() { _os.AcquireReadLock(ref _rwLock); @@ -191,4 +196,4 @@ public class ReaderWriterLock : ISharedMutex { return ref _rwLock; } -} +} \ No newline at end of file