mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
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:
parent
375b5b9220
commit
cf7062788f
14 changed files with 414 additions and 142 deletions
13
src/LibHac/Common/Box.cs
Normal file
13
src/LibHac/Common/Box.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,20 +117,16 @@ 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);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
if (Hos.Fs.Impl.IsEnabledAccessLog() && Hos.Fs.Impl.IsEnabledHandleAccessLog(handle))
|
if (Hos.Fs.Impl.IsEnabledAccessLog() && Hos.Fs.Impl.IsEnabledHandleAccessLog(handle))
|
||||||
{
|
{
|
||||||
Tick start = Hos.Os.GetSystemTick();
|
Tick start = Hos.Os.GetSystemTick();
|
||||||
|
@ -148,8 +148,6 @@ internal class FileAccessor : IDisposable
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
// ReSharper restore ConditionIsAlwaysTrueOrFalse
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
if (_filePathHash is not null)
|
||||||
|
{
|
||||||
|
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));
|
Result rc = UpdateLastResult(_file.Get.SetSize(size));
|
||||||
if (rc.IsFailure()) return rc;
|
if (rc.IsFailure()) return rc;
|
||||||
|
|
||||||
if (_filePathHash.Data != 0)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setter.Set(oldWriteState);
|
setter.Set(originalWriteState);
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,10 +75,12 @@ 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;
|
accessor = currentNode.Value;
|
||||||
return Result.Success;
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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;
|
||||||
}
|
}
|
|
@ -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);
|
||||||
|
|
183
src/LibHac/Fs/Shim/FileDataCache.cs
Normal file
183
src/LibHac/Fs/Shim/FileDataCache.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
62
src/LibHac/Fs/Shim/PathBasedFileDataCache.cs
Normal file
62
src/LibHac/Fs/Shim/PathBasedFileDataCache.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue