Rewrite file system accessors

This commit is contained in:
Alex Barney 2021-02-11 00:11:14 -07:00
parent 5eb063f4ec
commit 596a8bef7c
18 changed files with 957 additions and 20 deletions

View file

@ -0,0 +1,29 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace LibHac.Common.FixedArrays
{
[StructLayout(LayoutKind.Sequential)]
public struct Array8<T>
{
public const int Length = 8;
private T _item01;
private T _item02;
private T _item03;
private T _item04;
private T _item05;
private T _item06;
private T _item07;
private T _item08;
public ref T this[int i] => ref Items[i];
public Span<T> Items => SpanHelpers.CreateSpan(ref _item01, Length);
public readonly ReadOnlySpan<T> ItemsRo => SpanHelpers.CreateReadOnlySpan(in _item01, Length);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ReadOnlySpan<T>(in Array8<T> value) => value.ItemsRo;
}
}

View file

@ -6,11 +6,11 @@ namespace LibHac.Diag
public static class Abort
{
[DoesNotReturn]
public static void DoAbort(string message = null)
public static void DoAbort(Result result, string message = null)
{
if (string.IsNullOrWhiteSpace(message))
{
throw new LibHacException("Abort.");
throw new HorizonResultException(result, "Abort.");
}
throw new LibHacException($"Abort: {message}");
@ -21,7 +21,15 @@ namespace LibHac.Diag
if (condition)
return;
DoAbort(message);
DoAbort(default, message);
}
public static void DoAbortUnless([DoesNotReturnIf(false)] bool condition, Result result, string message = null)
{
if (condition)
return;
DoAbort(result, message);
}
[DoesNotReturn]

View file

@ -12,6 +12,23 @@ using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
namespace LibHac.Fs
{
// Functions in the nn::fssrv::detail namespace use this struct.
public readonly struct FileSystemClientImpl
{
internal readonly FileSystemClient Fs;
internal HorizonClient Hos => Fs.Hos;
internal ref FileSystemClientGlobals Globals => ref Fs.Globals;
internal FileSystemClientImpl(FileSystemClient parentClient) => Fs = parentClient;
}
internal struct FileSystemClientGlobals
{
public HorizonClient Hos;
public object InitMutex;
public FileSystemProxyServiceObjectGlobals FileSystemProxyServiceObject;
}
public partial class FileSystemClient
{
internal FileSystemClientGlobals Globals;
@ -38,13 +55,6 @@ namespace LibHac.Fs
Assert.NotNull(Time);
}
internal struct FileSystemClientGlobals
{
public HorizonClient Hos;
public object InitMutex;
public FileSystemProxyServiceObjectGlobals FileSystemProxyServiceObject;
}
public bool HasFileSystemServer()
{
return Hos != null;

View file

@ -0,0 +1,39 @@
using System;
using LibHac.Common;
using LibHac.Fs.Fsa;
// ReSharper disable once CheckNamespace
namespace LibHac.Fs.Impl
{
internal class DirectoryAccessor : IDisposable
{
private IDirectory _directory;
private FileSystemAccessor _parentFileSystem;
public DirectoryAccessor(ref IDirectory directory, FileSystemAccessor parentFileSystem)
{
_directory = Shared.Move(ref directory);
_parentFileSystem = parentFileSystem;
}
public void Dispose()
{
_directory?.Dispose();
_directory = null;
_parentFileSystem.NotifyCloseDirectory(this);
}
public FileSystemAccessor GetParent() => _parentFileSystem;
public Result Read(out long entriesRead, Span<DirectoryEntry> entryBuffer)
{
return _directory.Read(out entriesRead, entryBuffer);
}
public Result GetEntryCount(out long entryCount)
{
return _directory.GetEntryCount(out entryCount);
}
}
}

View file

@ -0,0 +1,181 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Diag;
using LibHac.Fs.Fsa;
// ReSharper disable once CheckNamespace
namespace LibHac.Fs.Impl
{
internal enum WriteState
{
None,
NeedsFlush,
Failed,
}
internal class FileAccessor : IDisposable
{
private IFile _file;
private FileSystemAccessor _parentFileSystem;
private WriteState _writeState;
private Result _lastResult;
private OpenMode _openMode;
private FilePathHash _filePathHash;
// ReSharper disable once NotAccessedField.Local
private int _pathHashIndex;
public FileAccessor(ref IFile file, FileSystemAccessor parentFileSystem, OpenMode mode)
{
_file = Shared.Move(ref file);
_parentFileSystem = parentFileSystem;
_openMode = mode;
}
public void Dispose()
{
if (_lastResult.IsSuccess() && _writeState == WriteState.NeedsFlush)
{
Abort.DoAbort(ResultFs.NeedFlush.Log(), "File needs flush before closing.");
}
_parentFileSystem?.NotifyCloseFile(this);
_file?.Dispose();
_file = null;
}
public OpenMode GetOpenMode() => _openMode;
public WriteState GetWriteState() => _writeState;
public FileSystemAccessor GetParent() => _parentFileSystem;
public void SetFilePathHash(FilePathHash filePathHash, int index)
{
_filePathHash = filePathHash;
_pathHashIndex = index;
}
private Result UpdateLastResult(Result result)
{
if (!ResultFs.UsableSpaceNotEnough.Includes(result))
_lastResult = result;
return result;
}
public Result ReadWithoutCacheAccessLog(out long bytesRead, long offset, Span<byte> destination,
in ReadOption option)
{
return _file.Read(out bytesRead, offset, destination, in option);
}
private Result ReadWithCacheAccessLog(out long bytesRead, long offset, Span<byte> destination,
in ReadOption option, bool usePathCache, bool useDataCache)
{
throw new NotImplementedException();
}
public Result Read(out long bytesRead, long offset, Span<byte> destination, in ReadOption option)
{
Unsafe.SkipInit(out bytesRead);
if (_lastResult.IsFailure())
{
// Todo: Access log
return _lastResult;
}
// ReSharper disable ConditionIsAlwaysTrueOrFalse
bool usePathCache = _parentFileSystem is not null && _filePathHash.Data != 0;
// 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);
}
else
{
return ReadWithoutCacheAccessLog(out bytesRead, offset, destination, in option);
}
// ReSharper restore ConditionIsAlwaysTrueOrFalse
}
public Result Write(long offset, ReadOnlySpan<byte> source, in WriteOption option)
{
if (_lastResult.IsFailure())
return _lastResult;
using ScopedSetter<WriteState> setter =
ScopedSetter<WriteState>.MakeScopedSetter(ref _writeState, WriteState.Failed);
if (_filePathHash.Data != 0)
{
throw new NotImplementedException();
}
else
{
Result rc = UpdateLastResult(_file.Write(offset, source, in option));
if (rc.IsFailure()) return rc;
}
setter.Set(option.HasFlushFlag() ? WriteState.None : WriteState.NeedsFlush);
return Result.Success;
}
public Result Flush()
{
if (_lastResult.IsFailure())
return _lastResult;
using ScopedSetter<WriteState> setter =
ScopedSetter<WriteState>.MakeScopedSetter(ref _writeState, WriteState.Failed);
Result rc = UpdateLastResult(_file.Flush());
if (rc.IsFailure()) return rc;
setter.Set(WriteState.None);
return Result.Success;
}
public Result SetSize(long size)
{
if (_lastResult.IsFailure())
return _lastResult;
WriteState oldWriteState = _writeState;
using ScopedSetter<WriteState> setter =
ScopedSetter<WriteState>.MakeScopedSetter(ref _writeState, WriteState.Failed);
Result rc = UpdateLastResult(_file.SetSize(size));
if (rc.IsFailure()) return rc;
if (_filePathHash.Data != 0)
{
throw new NotImplementedException();
}
setter.Set(oldWriteState);
return Result.Success;
}
public Result GetSize(out long size)
{
Unsafe.SkipInit(out size);
if (_lastResult.IsFailure())
return _lastResult;
return _file.GetSize(out size);
}
public Result OperateRange(Span<byte> outBuffer, OperationId operationId, long offset, long size,
ReadOnlySpan<byte> inBuffer)
{
return _file.OperateRange(outBuffer, operationId, offset, size, inBuffer);
}
}
}

View file

@ -0,0 +1,403 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Diag;
using LibHac.Fs.Fsa;
using LibHac.Os;
using LibHac.Util;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
// ReSharper disable once CheckNamespace
namespace LibHac.Fs.Impl
{
internal class FileSystemAccessor : IDisposable
{
private MountName _mountName;
private IFileSystem _fileSystem;
private LinkedList<FileAccessor> _openFiles;
private LinkedList<DirectoryAccessor> _openDirectories;
private SdkMutexType _openListLock;
private ICommonMountNameGenerator _mountNameGenerator;
private ISaveDataAttributeGetter _saveDataAttributeGetter;
private bool _isAccessLogEnabled;
private bool _isDataCacheAttachable;
private bool _isPathCacheAttachable;
private bool _isPathCacheAttached;
private IMultiCommitTarget _multiCommitTarget;
public FileSystemAccessor(U8Span name, IMultiCommitTarget multiCommitTarget, IFileSystem fileSystem,
ICommonMountNameGenerator mountNameGenerator, ISaveDataAttributeGetter saveAttributeGetter)
{
_fileSystem = fileSystem;
_openFiles = new LinkedList<FileAccessor>();
_openDirectories = new LinkedList<DirectoryAccessor>();
_openListLock.Initialize();
_mountNameGenerator = mountNameGenerator;
_saveDataAttributeGetter = saveAttributeGetter;
_multiCommitTarget = multiCommitTarget;
if (name.IsEmpty())
Abort.DoAbort(ResultFs.InvalidMountName.Log());
if (StringUtils.GetLength(name, PathTool.MountNameLengthMax + 1) > PathTool.MountNameLengthMax)
Abort.DoAbort(ResultFs.InvalidMountName.Log());
StringUtils.Copy(_mountName.Name.Slice(0, PathTool.MountNameLengthMax), name);
_mountName.Name[PathTool.MountNameLengthMax] = 0;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposing) return;
using (ScopedLock.Lock(ref _openListLock))
{
Abort.DoAbortUnless(_openFiles.Count == 0, ResultFs.FileNotClosed.Log(),
"All files must be closed before unmounting.");
Abort.DoAbortUnless(_openDirectories.Count == 0, ResultFs.DirectoryNotClosed.Log(),
"All directories must be closed before unmounting.");
if (_isPathCacheAttached)
{
throw new NotImplementedException();
}
}
_saveDataAttributeGetter?.Dispose();
_saveDataAttributeGetter = null;
_mountNameGenerator?.Dispose();
_mountNameGenerator = null;
_fileSystem?.Dispose();
_fileSystem = null;
}
private static void Remove<T>(LinkedList<T> list, T item)
{
LinkedListNode<T> node = list.Find(item);
Abort.DoAbortUnless(node is not null, "Invalid file or directory object.");
list.Remove(node);
}
private static Result CheckPath(U8Span mountName, U8Span path)
{
int mountNameLength = StringUtils.GetLength(mountName, PathTool.MountNameLengthMax);
int pathLength = StringUtils.GetLength(path, PathTool.EntryNameLengthMax);
if (mountNameLength + 1 + pathLength > PathTool.EntryNameLengthMax)
return ResultFs.TooLongPath.Log();
return Result.Success;
}
private static bool HasOpenWriteModeFiles(LinkedList<FileAccessor> list)
{
for (LinkedListNode<FileAccessor> file = list.First; file is not null; file = file.Next)
{
if (file.Value.GetOpenMode().HasFlag(OpenMode.Write))
{
return true;
}
}
return false;
}
public void SetAccessLog(bool isEnabled) => _isAccessLogEnabled = isEnabled;
public void SetFileDataCacheAttachable(bool isAttachable) => _isDataCacheAttachable = isAttachable;
public void SetPathBasedFileDataCacheAttachable(bool isAttachable) => _isPathCacheAttachable = isAttachable;
public bool IsEnabledAccessLog() => _isAccessLogEnabled;
public bool IsFileDataCacheAttachable() => _isDataCacheAttachable;
public bool IsPathBasedFileDataCacheAttachable() => _isPathCacheAttachable;
public void AttachPathBasedFileDataCache()
{
if (_isPathCacheAttachable)
_isPathCacheAttached = true;
}
public Result CreateFile(U8Span path, long size, CreateFileOptions option)
{
Result rc = CheckPath(new U8Span(_mountName.Name), path);
if (rc.IsFailure()) return rc;
if (_isPathCacheAttached)
{
throw new NotImplementedException();
}
else
{
rc = _fileSystem.CreateFile(path, size, option);
if (rc.IsFailure()) return rc;
}
return Result.Success;
}
public Result DeleteFile(U8Span path)
{
Result rc = CheckPath(new U8Span(_mountName.Name), path);
if (rc.IsFailure()) return rc;
return _fileSystem.DeleteDirectory(path);
}
public Result CreateDirectory(U8Span path)
{
Result rc = CheckPath(new U8Span(_mountName.Name), path);
if (rc.IsFailure()) return rc;
return _fileSystem.CreateDirectory(path);
}
public Result DeleteDirectory(U8Span path)
{
Result rc = CheckPath(new U8Span(_mountName.Name), path);
if (rc.IsFailure()) return rc;
return _fileSystem.DeleteDirectory(path);
}
public Result DeleteDirectoryRecursively(U8Span path)
{
Result rc = CheckPath(new U8Span(_mountName.Name), path);
if (rc.IsFailure()) return rc;
return _fileSystem.DeleteDirectoryRecursively(path);
}
public Result CleanDirectoryRecursively(U8Span path)
{
Result rc = CheckPath(new U8Span(_mountName.Name), path);
if (rc.IsFailure()) return rc;
return _fileSystem.CleanDirectoryRecursively(path);
}
public Result RenameFile(U8Span oldPath, U8Span newPath)
{
Result rc = CheckPath(new U8Span(_mountName.Name), oldPath);
if (rc.IsFailure()) return rc;
rc = CheckPath(new U8Span(_mountName.Name), newPath);
if (rc.IsFailure()) return rc;
if (_isPathCacheAttached)
{
throw new NotImplementedException();
}
else
{
rc = _fileSystem.RenameFile(oldPath, newPath);
if (rc.IsFailure()) return rc;
}
return Result.Success;
}
public Result RenameDirectory(U8Span oldPath, U8Span newPath)
{
Result rc = CheckPath(new U8Span(_mountName.Name), oldPath);
if (rc.IsFailure()) return rc;
rc = CheckPath(new U8Span(_mountName.Name), newPath);
if (rc.IsFailure()) return rc;
if (_isPathCacheAttached)
{
throw new NotImplementedException();
}
else
{
rc = _fileSystem.RenameDirectory(oldPath, newPath);
if (rc.IsFailure()) return rc;
}
return Result.Success;
}
public Result GetEntryType(out DirectoryEntryType entryType, U8Span path)
{
Unsafe.SkipInit(out entryType);
Result rc = CheckPath(new U8Span(_mountName.Name), path);
if (rc.IsFailure()) return rc;
return _fileSystem.GetEntryType(out entryType, path);
}
public Result GetFreeSpaceSize(out long freeSpace, U8Span path)
{
Unsafe.SkipInit(out freeSpace);
Result rc = CheckPath(new U8Span(_mountName.Name), path);
if (rc.IsFailure()) return rc;
return _fileSystem.GetFreeSpaceSize(out freeSpace, path);
}
public Result GetTotalSpaceSize(out long totalSpace, U8Span path)
{
Unsafe.SkipInit(out totalSpace);
Result rc = CheckPath(new U8Span(_mountName.Name), path);
if (rc.IsFailure()) return rc;
return _fileSystem.GetTotalSpaceSize(out totalSpace, path);
}
public Result OpenFile(out FileAccessor file, U8Span path, OpenMode mode)
{
file = default;
Result rc = CheckPath(new U8Span(_mountName.Name), path);
if (rc.IsFailure()) return rc;
IFile iFile = null;
try
{
rc = _fileSystem.OpenFile(out iFile, path, mode);
if (rc.IsFailure()) return rc;
var fileAccessor = new FileAccessor(ref iFile, this, mode);
using (ScopedLock.Lock(ref _openListLock))
{
_openFiles.AddLast(fileAccessor);
}
if (_isPathCacheAttached)
{
if (mode.HasFlag(OpenMode.AllowAppend))
{
throw new NotImplementedException();
}
else
{
throw new NotImplementedException();
}
}
file = Shared.Move(ref fileAccessor);
return Result.Success;
}
finally
{
iFile?.Dispose();
}
}
public Result OpenDirectory(out DirectoryAccessor directory, U8Span path, OpenDirectoryMode mode)
{
directory = default;
Result rc = CheckPath(new U8Span(_mountName.Name), path);
if (rc.IsFailure()) return rc;
IDirectory iDirectory = null;
try
{
rc = _fileSystem.OpenDirectory(out iDirectory, path, mode);
if (rc.IsFailure()) return rc;
var directoryAccessor = new DirectoryAccessor(ref iDirectory, this);
using (ScopedLock.Lock(ref _openListLock))
{
_openDirectories.AddLast(directoryAccessor);
}
directory = Shared.Move(ref directoryAccessor);
return Result.Success;
}
finally
{
iDirectory?.Dispose();
}
}
public Result Commit()
{
using (ScopedLock.Lock(ref _openListLock))
{
DumpUnclosedAccessorList(OpenMode.Write, 0);
if (HasOpenWriteModeFiles(_openFiles))
return ResultFs.WriteModeFileNotClosed.Log();
}
return _fileSystem.Commit();
}
public Result GetFileTimeStampRaw(out FileTimeStampRaw timeStamp, U8Span path)
{
return _fileSystem.GetFileTimeStampRaw(out timeStamp, path);
}
public Result QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId, U8Span path)
{
return _fileSystem.QueryEntry(outBuffer, inBuffer, queryId, path);
}
public U8Span GetName()
{
return new U8Span(_mountName.Name);
}
public Result GetCommonMountName(Span<byte> nameBuffer)
{
if (_mountNameGenerator is null)
return ResultFs.PreconditionViolation.Log();
return _mountNameGenerator.GenerateCommonMountName(nameBuffer);
}
public Result GetSaveDataAttribute(out SaveDataAttribute attribute)
{
Unsafe.SkipInit(out attribute);
if (_saveDataAttributeGetter is null)
return ResultFs.PreconditionViolation.Log();
return _saveDataAttributeGetter.GetSaveDataAttribute(out attribute);
}
public ReferenceCountedDisposable<IFileSystemSf> GetMultiCommitTarget()
{
return _multiCommitTarget?.GetMultiCommitTarget();
}
public void PurgeFileDataCache(FileDataCacheAccessor cacheAccessor)
{
cacheAccessor.Purge(_fileSystem);
}
public void NotifyCloseFile(FileAccessor file)
{
using ScopedLock<SdkMutexType> lk = ScopedLock.Lock(ref _openListLock);
Remove(_openFiles, file);
}
public void NotifyCloseDirectory(DirectoryAccessor directory)
{
using ScopedLock<SdkMutexType> lk = ScopedLock.Lock(ref _openListLock);
Remove(_openDirectories, directory);
}
private void DumpUnclosedAccessorList(OpenMode fileOpenModeMask, OpenDirectoryMode directoryOpenModeMask)
{
// Todo: Implement
}
}
}

View file

@ -0,0 +1,15 @@
using System;
namespace LibHac.Fs.Fsa
{
public interface ICommonMountNameGenerator : IDisposable
{
Result GenerateCommonMountName(Span<byte> nameBuffer);
}
public interface ISaveDataAttributeGetter : IDisposable
{
Result GetSaveDataAttribute(out SaveDataAttribute attribute);
}
}

View file

@ -1,9 +0,0 @@
using System;
namespace LibHac.Fs
{
public interface ICommonMountNameGenerator
{
Result GenerateCommonMountName(Span<byte> nameBuffer);
}
}

View file

@ -0,0 +1,26 @@
using System;
using LibHac.Fs.Fsa;
namespace LibHac.Fs.Impl
{
internal class FileDataCacheAccessor
{
private IFileDataCache _cache;
public FileDataCacheAccessor(IFileDataCache cache)
{
_cache = cache;
}
public Result Read(IFile file, out long bytesRead, long offset, Span<byte> destination, in ReadOption option,
ref FileDataCacheAccessResult cacheAccessResult)
{
return _cache.Read(file, out bytesRead, offset, destination, in option, ref cacheAccessResult);
}
public void Purge(IFileSystem fileSystem)
{
_cache.Purge(fileSystem);
}
}
}

View file

@ -0,0 +1,7 @@
namespace LibHac.Fs.Impl
{
public struct FilePathHash
{
public int Data;
}
}

View file

@ -0,0 +1,93 @@
using System;
using LibHac.Diag;
using LibHac.Util;
namespace LibHac.Fs.Impl
{
public readonly struct FileRegion
{
public readonly long Offset;
public readonly long Size;
public FileRegion(long offset, long size)
{
Offset = offset;
Size = size;
Abort.DoAbortUnless(size >= 0);
}
public long GetEndOffset()
{
return Offset + Size;
}
public bool Includes(FileRegion other)
{
return Offset <= other.Offset && other.GetEndOffset() <= GetEndOffset();
}
public bool Intersects(FileRegion other)
{
return HasIntersection(this, other);
}
public FileRegion GetIntersection(FileRegion other)
{
return GetIntersection(this, other);
}
public FileRegion ExpandAndAlign(uint alignment)
{
long alignedStartOffset = Alignment.AlignDownPow2(Offset, alignment);
long alignedEndOffset = Alignment.AlignUpPow2(GetEndOffset(), alignment);
long alignedSize = alignedEndOffset - alignedStartOffset;
return new FileRegion(alignedStartOffset, alignedSize);
}
public FileRegion ShrinkAndAlign(uint alignment)
{
long alignedStartOffset = Alignment.AlignUpPow2(Offset, alignment);
long alignedEndOffset = Alignment.AlignDownPow2(GetEndOffset(), alignment);
long alignedSize = alignedEndOffset - alignedStartOffset;
return new FileRegion(alignedStartOffset, alignedSize);
}
public FileRegion GetEndRegionWithSizeLimit(long size)
{
if (size >= Size)
return this;
return new FileRegion(GetEndOffset() - size, size);
}
public static bool HasIntersection(FileRegion region1, FileRegion region2)
{
return region1.GetEndOffset() >= region2.Offset &&
region2.GetEndOffset() >= region1.Offset;
}
public static FileRegion GetIntersection(FileRegion region1, FileRegion region2)
{
if (!region1.Intersects(region2))
return new FileRegion();
long intersectionStartOffset = Math.Max(region1.Offset, region2.Offset);
long intersectionEndOffset = Math.Min(region1.GetEndOffset(), region2.GetEndOffset());
long intersectionSize = intersectionEndOffset - intersectionStartOffset;
return new FileRegion(intersectionStartOffset, intersectionSize);
}
public static FileRegion GetInclusion(FileRegion region1, FileRegion region2)
{
long inclusionStartOffset = Math.Min(region1.Offset, region2.Offset);
long inclusionEndOffset = Math.Max(region1.GetEndOffset(), region2.GetEndOffset());
long inclusionSize = inclusionEndOffset - inclusionStartOffset;
return new FileRegion(inclusionStartOffset, inclusionSize);
}
}
}

View file

@ -0,0 +1,92 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common.FixedArrays;
using LibHac.Diag;
using LibHac.Fs.Fsa;
namespace LibHac.Fs.Impl
{
// ReSharper disable once InconsistentNaming
internal abstract class IFileDataCache : IDisposable
{
public abstract void Dispose();
public abstract void Purge(IFileSystem fileSystem);
protected abstract Result DoRead(IFile file, out long bytesRead, long offset, Span<byte> destination,
in ReadOption option, ref FileDataCacheAccessResult cacheAccessResult);
public Result Read(IFile file, out long bytesRead, long offset, Span<byte> destination, in ReadOption option,
ref FileDataCacheAccessResult cacheAccessResult)
{
Unsafe.SkipInit(out bytesRead);
if (destination.Length == 0)
{
bytesRead = 0;
cacheAccessResult.SetFileDataCacheUsed(true);
return Result.Success;
}
if (offset < 0)
return ResultFs.OutOfRange.Log();
if (destination.Length < 0)
return ResultFs.OutOfRange.Log();
if (long.MaxValue - offset < destination.Length)
return ResultFs.OutOfRange.Log();
return DoRead(file, out bytesRead, offset, destination, in option, ref cacheAccessResult);
}
}
internal struct FileDataCacheAccessResult
{
private const int MaxRegionCount = 8;
private int _regionCount;
private Array8<FileRegion> _regions;
private bool _isFileDataCacheUsed;
private bool _exceededMaxRegionCount;
public bool IsFileDataCacheUsed() => _isFileDataCacheUsed;
public bool SetFileDataCacheUsed(bool useFileDataCache) => _isFileDataCacheUsed = useFileDataCache;
public int GetCacheFetchedRegionCount()
{
Assert.True(_isFileDataCacheUsed);
return _regionCount;
}
public bool ExceededMaxCacheFetchedRegionCount() => _exceededMaxRegionCount;
public FileRegion GetCacheFetchedRegion(int index)
{
Assert.True(IsFileDataCacheUsed());
Assert.True(index >= 0);
Assert.True(index < _regionCount);
return _regions[index];
}
public void AddCacheFetchedRegion(FileRegion region)
{
_isFileDataCacheUsed = true;
if (region.Size == 0)
return;
if (_regionCount >= MaxRegionCount)
{
_regions[MaxRegionCount - 1] = region;
_exceededMaxRegionCount = true;
}
else
{
_regions[_regionCount] = region;
_regionCount++;
}
}
}
}

View file

@ -0,0 +1,29 @@
using LibHac.Common;
namespace LibHac.Fs
{
public ref struct ScopedSetter<T>
{
private Ref<T> _ref;
private T _value;
public ScopedSetter(ref T reference, T value)
{
_ref = new Ref<T>(ref reference);
_value = value;
}
public void Dispose()
{
if (!_ref.IsNull)
_ref.Value = _value;
}
public void Set(T value) => _value = value;
public static ScopedSetter<T> MakeScopedSetter(ref T reference, T value)
{
return new ScopedSetter<T>(ref reference, value);
}
}
}

View file

@ -2,6 +2,7 @@
using System.Diagnostics;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.Fs.Impl;
using LibHac.FsSrv.Sf;
using LibHac.FsSystem;
@ -23,6 +24,8 @@ namespace LibHac.Fs.Shim
PartitionId = partitionId;
}
public void Dispose() { }
public Result GenerateCommonMountName(Span<byte> nameBuffer)
{
U8Span mountName = GetBisMountName(PartitionId);

View file

@ -1,5 +1,6 @@
using System;
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.Fs.Impl;
using LibHac.FsSrv.Sf;
using LibHac.Util;
@ -59,6 +60,8 @@ namespace LibHac.Fs.Shim
StorageId = storageId;
}
public void Dispose() { }
public Result GenerateCommonMountName(Span<byte> nameBuffer)
{
U8String mountName = GetContentStorageMountName(StorageId);

View file

@ -1,5 +1,6 @@
using System;
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.Fs.Impl;
using LibHac.FsSrv;
using LibHac.FsSrv.Sf;
@ -105,6 +106,8 @@ namespace LibHac.Fs.Shim
PartitionId = partitionId;
}
public void Dispose() { }
public Result GenerateCommonMountName(Span<byte> nameBuffer)
{
char letter = GetGameCardMountNameSuffix(PartitionId);

View file

@ -2,6 +2,7 @@
using System.Diagnostics;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Fs.Fsa;
using LibHac.Fs.Impl;
using LibHac.FsSrv.Sf;
using LibHac.FsSystem;
@ -38,6 +39,8 @@ namespace LibHac.Fs.Shim
}
}
public void Dispose() { }
public Result GenerateCommonMountName(Span<byte> nameBuffer)
{
int requiredNameBufferSize = StringUtils.GetLength(_path.Str, FsPath.MaxLength) + HostRootFileSystemPathLength;
@ -56,6 +59,8 @@ namespace LibHac.Fs.Shim
private class HostRootCommonMountNameGenerator : ICommonMountNameGenerator
{
public void Dispose() { }
public Result GenerateCommonMountName(Span<byte> nameBuffer)
{
const int requiredNameBufferSize = HostRootFileSystemPathLength;

View file

@ -397,7 +397,7 @@ namespace LibHac.Fs.Shim
if (rc.IsFailure())
{
Abort.DoAbort();
Abort.DoAbort(rc);
}
}
}