mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add FileSystemAttribute functionality
This commit is contained in:
parent
809cecd1aa
commit
934f81da67
22 changed files with 509 additions and 17 deletions
34
src/LibHac/Common/FixedArrays/Array100.cs
Normal file
34
src/LibHac/Common/FixedArrays/Array100.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
#pragma warning disable CS0169, CS0649, IDE0051 // Field is never used, Field is never assigned to, Remove unused private members
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace LibHac.Common.FixedArrays;
|
||||
|
||||
public struct Array100<T>
|
||||
{
|
||||
public const int Length = 100;
|
||||
|
||||
private Array64<T> _0;
|
||||
private Array36<T> _64;
|
||||
|
||||
[UnscopedRef] public ref T this[int i] => ref Items[i];
|
||||
|
||||
[UnscopedRef]
|
||||
public Span<T> Items
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.Items), Length);
|
||||
}
|
||||
|
||||
[UnscopedRef]
|
||||
public readonly ReadOnlySpan<T> ItemsRo
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => SpanHelpers.CreateSpan(ref MemoryMarshal.GetReference(_0.ItemsRo), Length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<T>(in Array100<T> value) => value.ItemsRo;
|
||||
}
|
|
@ -31,6 +31,7 @@ internal class FileSystemAccessor : IDisposable
|
|||
private LinkedList<FileAccessor> _openFiles;
|
||||
private LinkedList<DirectoryAccessor> _openDirectories;
|
||||
private SdkMutexType _openListLock;
|
||||
private SdkMutexType _getFsAttributeLock;
|
||||
private UniqueRef<ICommonMountNameGenerator> _mountNameGenerator;
|
||||
private UniqueRef<ISaveDataAttributeGetter> _saveDataAttributeGetter;
|
||||
private bool _isAccessLogEnabled;
|
||||
|
@ -39,6 +40,7 @@ internal class FileSystemAccessor : IDisposable
|
|||
private bool _isPathCacheAttached;
|
||||
private IMultiCommitTarget _multiCommitTarget;
|
||||
private PathFlags _pathFlags;
|
||||
private Optional<FileSystemAttribute> _fsAttribute;
|
||||
private IStorage _storageForPurgeFileDataCache;
|
||||
|
||||
internal HorizonClient Hos { get; }
|
||||
|
@ -53,6 +55,7 @@ internal class FileSystemAccessor : IDisposable
|
|||
_openFiles = new LinkedList<FileAccessor>();
|
||||
_openDirectories = new LinkedList<DirectoryAccessor>();
|
||||
_openListLock = new SdkMutexType();
|
||||
_getFsAttributeLock = new SdkMutexType();
|
||||
_mountNameGenerator = new UniqueRef<ICommonMountNameGenerator>(ref mountNameGenerator);
|
||||
_saveDataAttributeGetter = new UniqueRef<ISaveDataAttributeGetter>(ref saveAttributeGetter);
|
||||
_multiCommitTarget = multiCommitTarget;
|
||||
|
@ -457,6 +460,25 @@ internal class FileSystemAccessor : IDisposable
|
|||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result GetFileSystemAttribute(out FileSystemAttribute outAttribute)
|
||||
{
|
||||
using ScopedLock<SdkMutexType> scopedLock = ScopedLock.Lock(ref _getFsAttributeLock);
|
||||
|
||||
if (_fsAttribute.HasValue)
|
||||
{
|
||||
outAttribute = _fsAttribute.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
Result res = _fileSystem.Get.GetFileSystemAttribute(out outAttribute);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
_fsAttribute.Set(in outAttribute);
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId, U8Span path)
|
||||
{
|
||||
using var pathNormalized = new Path();
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.Common.FixedArrays;
|
||||
using LibHac.FsSystem;
|
||||
|
||||
namespace LibHac.Fs.Fsa;
|
||||
|
@ -297,6 +299,16 @@ public abstract class IFileSystem : IDisposable
|
|||
return DoQueryEntry(outBuffer, inBuffer, queryId, path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets attributes of the <see cref="IFileSystem"/> including info about the maximum path length sizes it supports.
|
||||
/// </summary>
|
||||
/// <param name="outAttribute">If the operation returns successfully, the file system attributes.</param>
|
||||
/// <returns>The <see cref="Result"/> of the requested operation.</returns>
|
||||
public Result GetFileSystemAttribute(out FileSystemAttribute outAttribute)
|
||||
{
|
||||
return DoGetFileSystemAttribute(out outAttribute);
|
||||
}
|
||||
|
||||
protected abstract Result DoCreateFile(in Path path, long size, CreateFileOptions option);
|
||||
protected abstract Result DoDeleteFile(in Path path);
|
||||
protected abstract Result DoCreateDirectory(in Path path);
|
||||
|
@ -320,8 +332,7 @@ public abstract class IFileSystem : IDisposable
|
|||
}
|
||||
|
||||
protected abstract Result DoOpenFile(ref UniqueRef<IFile> outFile, in Path path, OpenMode mode);
|
||||
protected abstract Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path,
|
||||
OpenDirectoryMode mode);
|
||||
protected abstract Result DoOpenDirectory(ref UniqueRef<IDirectory> outDirectory, in Path path, OpenDirectoryMode mode);
|
||||
protected abstract Result DoCommit();
|
||||
|
||||
protected virtual Result DoCommitProvisionally(long counter) => ResultFs.NotImplemented.Log();
|
||||
|
@ -336,6 +347,12 @@ public abstract class IFileSystem : IDisposable
|
|||
|
||||
protected virtual Result DoQueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId,
|
||||
in Path path) => ResultFs.NotImplemented.Log();
|
||||
|
||||
protected virtual Result DoGetFileSystemAttribute(out FileSystemAttribute outAttribute)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out outAttribute);
|
||||
return ResultFs.NotImplemented.Log();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -374,3 +391,36 @@ public enum QueryId
|
|||
IsSignedSystemPartition = 2,
|
||||
QueryUnpreparedFileInformation = 3
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct FileSystemAttribute
|
||||
{
|
||||
public bool DirectoryNameLengthMaxHasValue;
|
||||
public bool FileNameLengthMaxHasValue;
|
||||
public bool DirectoryPathLengthMaxHasValue;
|
||||
public bool FilePathLengthMaxHasValue;
|
||||
public bool Utf16CreateDirectoryPathLengthMaxHasValue;
|
||||
public bool Utf16DeleteDirectoryPathLengthMaxHasValue;
|
||||
public bool Utf16RenameSourceDirectoryPathLengthMaxHasValue;
|
||||
public bool Utf16RenameDestinationDirectoryPathLengthMaxHasValue;
|
||||
public bool Utf16OpenDirectoryPathLengthMaxHasValue;
|
||||
public bool Utf16DirectoryNameLengthMaxHasValue;
|
||||
public bool Utf16FileNameLengthMaxHasValue;
|
||||
public bool Utf16DirectoryPathLengthMaxHasValue;
|
||||
public bool Utf16FilePathLengthMaxHasValue;
|
||||
public Array27<byte> Reserved1;
|
||||
public int DirectoryNameLengthMax;
|
||||
public int FileNameLengthMax;
|
||||
public int DirectoryPathLengthMax;
|
||||
public int FilePathLengthMax;
|
||||
public int Utf16CreateDirectoryPathLengthMax;
|
||||
public int Utf16DeleteDirectoryPathLengthMax;
|
||||
public int Utf16RenameSourceDirectoryPathLengthMax;
|
||||
public int Utf16RenameDestinationDirectoryPathLengthMax;
|
||||
public int Utf16OpenDirectoryPathLengthMax;
|
||||
public int Utf16DirectoryNameLengthMax;
|
||||
public int Utf16FileNameLengthMax;
|
||||
public int Utf16DirectoryPathLengthMax;
|
||||
public int Utf16FilePathLengthMax;
|
||||
public Array100<byte> Reserved2;
|
||||
}
|
|
@ -76,6 +76,9 @@ public static class Registrar
|
|||
protected override Result DoGetFreeSpaceSize(out long freeSpace, in Path path) =>
|
||||
_fileSystem.Get.GetFreeSpaceSize(out freeSpace, in path);
|
||||
|
||||
protected override Result DoGetFileSystemAttribute(out FileSystemAttribute outAttribute) =>
|
||||
_fileSystem.Get.GetFileSystemAttribute(out outAttribute);
|
||||
|
||||
protected override Result DoGetTotalSpaceSize(out long totalSpace, in Path path) =>
|
||||
_fileSystem.Get.GetTotalSpaceSize(out totalSpace, in path);
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Fs.Impl;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.Os;
|
||||
using static LibHac.Fs.Impl.AccessLogStrings;
|
||||
|
||||
|
@ -9,7 +11,7 @@ namespace LibHac.Fs.Fsa;
|
|||
/// <summary>
|
||||
/// Contains functions meant for internal use for interacting with mounted file systems.
|
||||
/// </summary>
|
||||
/// <remarks>Based on nnSdk 13.4.0 (FS 13.1.0)</remarks>
|
||||
/// <remarks>Based on nnSdk 16.2.0</remarks>
|
||||
public static class UserFileSystemPrivate
|
||||
{
|
||||
public static Result CreateFile(this FileSystemClient fs, U8Span path, long size, CreateFileOptions option)
|
||||
|
@ -26,7 +28,7 @@ public static class UserFileSystemPrivate
|
|||
Tick end = fs.Hos.Os.GetSystemTick();
|
||||
|
||||
var sb = new U8StringBuilder(logBuffer, true);
|
||||
sb.Append(LogPath).Append(path).Append((byte)'"');
|
||||
sb.Append(LogPath).Append(path).Append(LogQuote);
|
||||
logBuffer = sb.Buffer;
|
||||
|
||||
fs.Impl.OutputAccessLogUnlessResultSuccess(res, start, end, null, new U8Span(logBuffer));
|
||||
|
@ -45,7 +47,7 @@ public static class UserFileSystemPrivate
|
|||
Tick end = fs.Hos.Os.GetSystemTick();
|
||||
|
||||
var sb = new U8StringBuilder(logBuffer, true);
|
||||
sb.Append(LogPath).Append(path).Append((byte)'"').Append(LogSize).AppendFormat(size);
|
||||
sb.Append(LogPath).Append(path).Append(LogQuote).Append(LogSize).AppendFormat(size);
|
||||
logBuffer = sb.Buffer;
|
||||
|
||||
fs.Impl.OutputAccessLog(res, start, end, null, new U8Span(logBuffer));
|
||||
|
@ -97,4 +99,134 @@ public static class UserFileSystemPrivate
|
|||
fs.Impl.AbortIfNeeded(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
private static void SetOrChangeMin(ref int minValue, ref bool hasMinValue, int value, bool hasValue)
|
||||
{
|
||||
if (!hasValue)
|
||||
return;
|
||||
|
||||
if (!hasMinValue)
|
||||
{
|
||||
minValue = value;
|
||||
hasMinValue = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (minValue > value)
|
||||
{
|
||||
minValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
public static Result GetPathLengthMax(this FileSystemClient fs, out long outLength, U8Span path)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out outLength);
|
||||
|
||||
Result res = fs.Impl.FindFileSystem(out FileSystemAccessor fileSystem, out _, path);
|
||||
fs.Impl.AbortIfNeeded(res);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
res = fileSystem.GetFileSystemAttribute(out FileSystemAttribute attribute);
|
||||
fs.Impl.AbortIfNeeded(res);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
int retValue = 0;
|
||||
bool hasRetValue = false;
|
||||
SetOrChangeMin(ref retValue, ref hasRetValue, attribute.DirectoryPathLengthMax, attribute.DirectoryPathLengthMaxHasValue);
|
||||
SetOrChangeMin(ref retValue, ref hasRetValue, attribute.FilePathLengthMax, attribute.FileNameLengthMaxHasValue);
|
||||
SetOrChangeMin(ref retValue, ref hasRetValue, FspPath.MaxLength, true);
|
||||
|
||||
Assert.SdkAssert(hasRetValue);
|
||||
|
||||
outLength = retValue;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result GetEntryNameLengthMax(this FileSystemClient fs, out long outLength, U8Span path)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out outLength);
|
||||
|
||||
Result res = fs.Impl.FindFileSystem(out FileSystemAccessor fileSystem, out _, path);
|
||||
fs.Impl.AbortIfNeeded(res);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
res = fileSystem.GetFileSystemAttribute(out FileSystemAttribute attribute);
|
||||
fs.Impl.AbortIfNeeded(res);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
int retValue = 0;
|
||||
bool hasRetValue = false;
|
||||
SetOrChangeMin(ref retValue, ref hasRetValue, attribute.DirectoryNameLengthMax, attribute.DirectoryNameLengthMaxHasValue);
|
||||
SetOrChangeMin(ref retValue, ref hasRetValue, attribute.FileNameLengthMax, attribute.FileNameLengthMaxHasValue);
|
||||
SetOrChangeMin(ref retValue, ref hasRetValue, FspPath.MaxLength, true);
|
||||
|
||||
Assert.SdkAssert(hasRetValue);
|
||||
|
||||
outLength = retValue;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result GetUtf16PathLengthMax(this FileSystemClient fs, out bool outHasValue, out long outLength, U8Span path)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out outHasValue, out outLength);
|
||||
|
||||
Result res = fs.Impl.FindFileSystem(out FileSystemAccessor fileSystem, out _, path);
|
||||
fs.Impl.AbortIfNeeded(res);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
res = fileSystem.GetFileSystemAttribute(out FileSystemAttribute attribute);
|
||||
fs.Impl.AbortIfNeeded(res);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
int retValue = 0;
|
||||
bool hasRetValue = false;
|
||||
SetOrChangeMin(ref retValue, ref hasRetValue, attribute.Utf16CreateDirectoryPathLengthMax, attribute.Utf16CreateDirectoryPathLengthMaxHasValue);
|
||||
SetOrChangeMin(ref retValue, ref hasRetValue, attribute.Utf16DeleteDirectoryPathLengthMax, attribute.Utf16DeleteDirectoryPathLengthMaxHasValue);
|
||||
SetOrChangeMin(ref retValue, ref hasRetValue, attribute.Utf16RenameSourceDirectoryPathLengthMax, attribute.Utf16RenameSourceDirectoryPathLengthMaxHasValue);
|
||||
SetOrChangeMin(ref retValue, ref hasRetValue, attribute.Utf16RenameDestinationDirectoryPathLengthMax, attribute.Utf16RenameDestinationDirectoryPathLengthMaxHasValue);
|
||||
SetOrChangeMin(ref retValue, ref hasRetValue, attribute.Utf16OpenDirectoryPathLengthMax, attribute.Utf16OpenDirectoryPathLengthMaxHasValue);
|
||||
SetOrChangeMin(ref retValue, ref hasRetValue, attribute.Utf16FilePathLengthMax, attribute.Utf16FilePathLengthMaxHasValue);
|
||||
SetOrChangeMin(ref retValue, ref hasRetValue, attribute.Utf16DirectoryPathLengthMax, attribute.Utf16DirectoryPathLengthMaxHasValue);
|
||||
|
||||
outHasValue = hasRetValue;
|
||||
outLength = retValue;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result GetUtf16EntryNameLengthMax(this FileSystemClient fs, out bool outHasValue, out long outLength, U8Span path)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out outHasValue, out outLength);
|
||||
|
||||
Result res = fs.Impl.FindFileSystem(out FileSystemAccessor fileSystem, out _, path);
|
||||
fs.Impl.AbortIfNeeded(res);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
res = fileSystem.GetFileSystemAttribute(out FileSystemAttribute attribute);
|
||||
fs.Impl.AbortIfNeeded(res);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
int retValue = 0;
|
||||
bool hasRetValue = false;
|
||||
SetOrChangeMin(ref retValue, ref hasRetValue, attribute.Utf16FileNameLengthMax, attribute.Utf16FileNameLengthMaxHasValue);
|
||||
SetOrChangeMin(ref retValue, ref hasRetValue, attribute.Utf16DirectoryNameLengthMax, attribute.Utf16DirectoryNameLengthMaxHasValue);
|
||||
|
||||
outHasValue = hasRetValue;
|
||||
outLength = retValue;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result GetFileSystemAttributeForDebug(this FileSystemClient fs, out FileSystemAttribute outAttribute, U8Span path)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out outAttribute);
|
||||
|
||||
Result res = fs.Impl.FindFileSystem(out FileSystemAccessor fileSystem, out _, path);
|
||||
fs.Impl.AbortIfNeeded(res);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
res = fileSystem.GetFileSystemAttribute(out outAttribute);
|
||||
fs.Impl.AbortIfNeeded(res);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ namespace LibHac.Fs;
|
|||
/// Wraps an <see cref="IFile"/> and only allows read operations on it.
|
||||
/// </summary>
|
||||
/// <remarks>Based on nnSdk 13.4.0 (FS 13.1.0)</remarks>
|
||||
internal class ReadOnlyFile : IFile
|
||||
file class ReadOnlyFile : IFile
|
||||
{
|
||||
private UniqueRef<IFile> _baseFile;
|
||||
|
||||
|
@ -156,4 +156,9 @@ public class ReadOnlyFileSystem : IFileSystem
|
|||
Unsafe.SkipInit(out totalSpace);
|
||||
return ResultFs.UnsupportedGetTotalSpaceSizeForReadOnlyFileSystem.Log();
|
||||
}
|
||||
|
||||
protected override Result DoGetFileSystemAttribute(out FileSystemAttribute outAttribute)
|
||||
{
|
||||
return _baseFileSystem.Get.GetFileSystemAttribute(out outAttribute);
|
||||
}
|
||||
}
|
|
@ -293,6 +293,11 @@ internal class FileSystemServiceObjectAdapter : IFileSystem, IMultiCommitTarget
|
|||
return _baseFs.Get.GetFileTimeStampRaw(out timeStamp, in sfPath);
|
||||
}
|
||||
|
||||
protected override Result DoGetFileSystemAttribute(out FileSystemAttribute outAttribute)
|
||||
{
|
||||
return _baseFs.Get.GetFileSystemAttribute(out outAttribute);
|
||||
}
|
||||
|
||||
protected override Result DoQueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId,
|
||||
in Path path)
|
||||
{
|
||||
|
|
|
@ -592,6 +592,17 @@ public class FileSystemInterfaceAdapter : IFileSystemSf
|
|||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result GetFileSystemAttribute(out FileSystemAttribute outAttribute)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out outAttribute);
|
||||
|
||||
Result res = _baseFileSystem.Get.GetFileSystemAttribute(out FileSystemAttribute attribute);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
outAttribute = attribute;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public Result GetImpl(ref SharedRef<IFileSystem> fileSystem)
|
||||
{
|
||||
fileSystem.SetByCopy(in _baseFileSystem);
|
||||
|
|
|
@ -114,4 +114,9 @@ public class SaveDataFileSystemCacheRegister : IFileSystem
|
|||
{
|
||||
return _baseFileSystem.Get.GetTotalSpaceSize(out totalSpace, in path);
|
||||
}
|
||||
|
||||
protected override Result DoGetFileSystemAttribute(out FileSystemAttribute outAttribute)
|
||||
{
|
||||
return _baseFileSystem.Get.GetFileSystemAttribute(out outAttribute);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Sf;
|
||||
using IFileSf = LibHac.FsSrv.Sf.IFile;
|
||||
using IDirectorySf = LibHac.FsSrv.Sf.IDirectory;
|
||||
|
@ -26,4 +27,5 @@ public interface IFileSystem : IDisposable
|
|||
Result CleanDirectoryRecursively(in Path path);
|
||||
Result GetFileTimeStampRaw(out FileTimeStampRaw timeStamp, in Path path);
|
||||
Result QueryEntry(OutBuffer outBuffer, InBuffer inBuffer, int queryId, in Path path);
|
||||
Result GetFileSystemAttribute(out FileSystemAttribute outAttribute);
|
||||
}
|
|
@ -502,8 +502,7 @@ public class ConcatenationFileSystem : IFileSystem
|
|||
/// <param name="baseFileSystem">The base <see cref="IAttributeFileSystem"/> for the
|
||||
/// new <see cref="ConcatenationFileSystem"/>.</param>
|
||||
public ConcatenationFileSystem(ref UniqueRef<IAttributeFileSystem> baseFileSystem) : this(ref baseFileSystem,
|
||||
DefaultInternalFileSize)
|
||||
{ }
|
||||
DefaultInternalFileSize) { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="ConcatenationFileSystem"/>.
|
||||
|
@ -1011,4 +1010,11 @@ public class ConcatenationFileSystem : IFileSystem
|
|||
|
||||
return _baseFileSystem.Get.CommitProvisionally(counter).Ret();
|
||||
}
|
||||
|
||||
protected override Result DoGetFileSystemAttribute(out FileSystemAttribute outAttribute)
|
||||
{
|
||||
using var scopedLock = new ScopedLock<SdkMutexType>(ref _mutex);
|
||||
|
||||
return _baseFileSystem.Get.GetFileSystemAttribute(out outAttribute).Ret();
|
||||
}
|
||||
}
|
|
@ -682,6 +682,18 @@ public class DirectorySaveDataFileSystem : ISaveDataFileSystem
|
|||
return Result.Success;
|
||||
}
|
||||
|
||||
protected override Result DoGetFileSystemAttribute(out FileSystemAttribute outAttribute)
|
||||
{
|
||||
const int baseDirectoryNameLength = 2;
|
||||
|
||||
Result res = _baseFs.GetFileSystemAttribute(out outAttribute);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
Utility.SubtractAllPathLengthMax(ref outAttribute, baseDirectoryNameLength);
|
||||
Utility.SubtractAllUtf16CountMax(ref outAttribute, baseDirectoryNameLength);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public override bool IsSaveDataFileSystemCacheEnabled()
|
||||
{
|
||||
return false;
|
||||
|
|
|
@ -139,6 +139,9 @@ public class ForwardingFileSystem : IFileSystem
|
|||
protected override Result DoGetFileTimeStampRaw(out FileTimeStampRaw timeStamp, in Path path) =>
|
||||
BaseFileSystem.Get.GetFileTimeStampRaw(out timeStamp, in path);
|
||||
|
||||
protected override Result DoGetFileSystemAttribute(out FileSystemAttribute outAttribute) =>
|
||||
BaseFileSystem.Get.GetFileSystemAttribute(out outAttribute);
|
||||
|
||||
protected override Result DoQueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId,
|
||||
in Path path) => BaseFileSystem.Get.QueryEntry(outBuffer, inBuffer, queryId, in path);
|
||||
}
|
|
@ -207,4 +207,9 @@ public abstract class IResultConvertFileSystem<T> : ISaveDataFileSystem where T
|
|||
{
|
||||
return ConvertResult(_baseFileSystem.Get.GetTotalSpaceSize(out totalSpace, in path)).Ret();
|
||||
}
|
||||
|
||||
protected override Result DoGetFileSystemAttribute(out FileSystemAttribute outAttribute)
|
||||
{
|
||||
return ConvertResult(_baseFileSystem.Get.GetFileSystemAttribute(out outAttribute)).Ret();
|
||||
}
|
||||
}
|
|
@ -523,6 +523,44 @@ public class LocalFileSystem : IAttributeFileSystem
|
|||
return Result.Success;
|
||||
}
|
||||
|
||||
protected override Result DoGetFileSystemAttribute(out FileSystemAttribute outAttribute)
|
||||
{
|
||||
const int winMaxPathComponentLength = 255;
|
||||
const int winMaxPathLength = 259;
|
||||
const int winMaxDirectoryPathLength = 247;
|
||||
|
||||
outAttribute = default;
|
||||
|
||||
outAttribute.Utf16DirectoryNameLengthMax = winMaxPathComponentLength;
|
||||
outAttribute.Utf16DirectoryNameLengthMaxHasValue = true;
|
||||
outAttribute.Utf16FileNameLengthMax = winMaxPathComponentLength;
|
||||
outAttribute.Utf16FileNameLengthMaxHasValue = true;
|
||||
outAttribute.Utf16DirectoryPathLengthMax = winMaxPathLength;
|
||||
outAttribute.Utf16DirectoryPathLengthMaxHasValue = true;
|
||||
outAttribute.Utf16FilePathLengthMax = winMaxPathLength;
|
||||
outAttribute.Utf16FilePathLengthMaxHasValue = true;
|
||||
outAttribute.Utf16CreateDirectoryPathLengthMax = winMaxDirectoryPathLength;
|
||||
outAttribute.Utf16CreateDirectoryPathLengthMaxHasValue = true;
|
||||
outAttribute.Utf16DeleteDirectoryPathLengthMax = winMaxPathLength;
|
||||
outAttribute.Utf16DeleteDirectoryPathLengthMaxHasValue = true;
|
||||
outAttribute.Utf16RenameSourceDirectoryPathLengthMax = winMaxDirectoryPathLength;
|
||||
outAttribute.Utf16RenameSourceDirectoryPathLengthMaxHasValue = true;
|
||||
outAttribute.Utf16RenameDestinationDirectoryPathLengthMax = winMaxDirectoryPathLength;
|
||||
outAttribute.Utf16RenameDestinationDirectoryPathLengthMaxHasValue = true;
|
||||
outAttribute.Utf16OpenDirectoryPathLengthMax = winMaxPathLength;
|
||||
outAttribute.Utf16OpenDirectoryPathLengthMaxHasValue = true;
|
||||
|
||||
int rootPathCount = _rootPath.GetLength();
|
||||
|
||||
Result res = Utility.CountUtf16CharacterForUtf8String(out ulong rootPathUtf16Count, _rootPath.GetString());
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
Utility.SubtractAllPathLengthMax(ref outAttribute, rootPathCount);
|
||||
Utility.SubtractAllUtf16CountMax(ref outAttribute, (int)rootPathUtf16Count);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
protected override Result DoCommit()
|
||||
{
|
||||
return Result.Success;
|
||||
|
|
|
@ -38,10 +38,7 @@ internal struct ScopedStorageLayoutTypeSetter : IDisposable
|
|||
// Todo: Implement
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
public void Dispose() { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -377,6 +374,12 @@ internal class StorageLayoutTypeSetFileSystem : IFileSystem
|
|||
return _baseFileSystem.Get.GetFileTimeStampRaw(out timeStamp, path);
|
||||
}
|
||||
|
||||
protected override Result DoGetFileSystemAttribute(out FileSystemAttribute outAttribute)
|
||||
{
|
||||
using var scopedContext = new ScopedStorageLayoutTypeSetter(_storageFlag);
|
||||
return _baseFileSystem.Get.GetFileSystemAttribute(out outAttribute);
|
||||
}
|
||||
|
||||
protected override Result DoQueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId,
|
||||
in Path path)
|
||||
{
|
||||
|
|
|
@ -255,4 +255,20 @@ public class SubdirectoryFileSystem : IFileSystem
|
|||
{
|
||||
return _baseFileSystem.Rollback();
|
||||
}
|
||||
|
||||
protected override Result DoGetFileSystemAttribute(out FileSystemAttribute outAttribute)
|
||||
{
|
||||
int rootPathCount = _rootPath.GetLength();
|
||||
|
||||
Result res = _baseFileSystem.GetFileSystemAttribute(out outAttribute);
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
res = Utility.CountUtf16CharacterForUtf8String(out ulong rootPathUtf16Count, _rootPath.GetString());
|
||||
if (res.IsFailure()) return res.Miss();
|
||||
|
||||
Utility.SubtractAllPathLengthMax(ref outAttribute, rootPathCount);
|
||||
Utility.SubtractAllUtf16CountMax(ref outAttribute, (int)rootPathUtf16Count);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
using System;
|
||||
using LibHac.Common;
|
||||
using LibHac.Diag;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.FsSrv.Sf;
|
||||
using LibHac.Os;
|
||||
using LibHac.Sf;
|
||||
using LibHac.Util;
|
||||
using IDirectory = LibHac.Fs.Fsa.IDirectory;
|
||||
using IFile = LibHac.Fs.Fsa.IFile;
|
||||
using IFileSystem = LibHac.Fs.Fsa.IFileSystem;
|
||||
|
@ -438,4 +440,73 @@ internal static class Utility
|
|||
outUniqueLock.Set(ref uniqueLock.Ref);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private static void SubtractIfHasValue(ref int inOutValue, int count, bool hasValue)
|
||||
{
|
||||
if (hasValue)
|
||||
{
|
||||
inOutValue -= count;
|
||||
Assert.SdkAssert(inOutValue >= 0);
|
||||
inOutValue = Math.Max(inOutValue, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private static ulong GetCodePointByteLength(byte firstUtf8CodeUnit)
|
||||
{
|
||||
if ((firstUtf8CodeUnit & 0x80) == 0)
|
||||
return 1;
|
||||
|
||||
if ((firstUtf8CodeUnit & 0xE0) == 0xC0)
|
||||
return 2;
|
||||
|
||||
if ((firstUtf8CodeUnit & 0xF0) == 0xE0)
|
||||
return 3;
|
||||
|
||||
if ((firstUtf8CodeUnit & 0xF8) == 0xF0)
|
||||
return 4;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void SubtractAllPathLengthMax(ref FileSystemAttribute attribute, int count)
|
||||
{
|
||||
SubtractIfHasValue(ref attribute.DirectoryPathLengthMax, count, attribute.DirectoryPathLengthMaxHasValue);
|
||||
SubtractIfHasValue(ref attribute.FilePathLengthMax, count, attribute.FilePathLengthMaxHasValue);
|
||||
}
|
||||
|
||||
public static Result CountUtf16CharacterForUtf8String(out ulong outCount, ReadOnlySpan<byte> utf8String)
|
||||
{
|
||||
UnsafeHelpers.SkipParamInit(out outCount);
|
||||
|
||||
Span<byte> buffer = stackalloc byte[4];
|
||||
ReadOnlySpan<byte> curString = utf8String;
|
||||
ulong utf16CodeUnitTotalCount = 0;
|
||||
|
||||
while (curString.Length > 0 && curString[0] != 0)
|
||||
{
|
||||
int utf16CodeUnitCount = GetCodePointByteLength(curString[0]) >= 4 ? 2 : 1;
|
||||
buffer.Clear();
|
||||
|
||||
if (CharacterEncoding.PickOutCharacterFromUtf8String(buffer, ref curString) != CharacterEncodingResult.Success)
|
||||
{
|
||||
return ResultFs.InvalidPathFormat.Log();
|
||||
}
|
||||
|
||||
utf16CodeUnitTotalCount += (ulong)utf16CodeUnitCount;
|
||||
}
|
||||
|
||||
outCount = utf16CodeUnitTotalCount;
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static void SubtractAllUtf16CountMax(ref FileSystemAttribute attribute, int count)
|
||||
{
|
||||
SubtractIfHasValue(ref attribute.Utf16DirectoryPathLengthMax, count, attribute.Utf16DirectoryPathLengthMaxHasValue);
|
||||
SubtractIfHasValue(ref attribute.Utf16FilePathLengthMax, count, attribute.Utf16FilePathLengthMaxHasValue);
|
||||
SubtractIfHasValue(ref attribute.Utf16CreateDirectoryPathLengthMax, count, attribute.Utf16CreateDirectoryPathLengthMaxHasValue);
|
||||
SubtractIfHasValue(ref attribute.Utf16DeleteDirectoryPathLengthMax, count, attribute.Utf16DeleteDirectoryPathLengthMaxHasValue);
|
||||
SubtractIfHasValue(ref attribute.Utf16RenameSourceDirectoryPathLengthMax, count, attribute.Utf16RenameSourceDirectoryPathLengthMaxHasValue);
|
||||
SubtractIfHasValue(ref attribute.Utf16RenameDestinationDirectoryPathLengthMax, count, attribute.Utf16RenameDestinationDirectoryPathLengthMaxHasValue);
|
||||
SubtractIfHasValue(ref attribute.Utf16OpenDirectoryPathLengthMax, count, attribute.Utf16OpenDirectoryPathLengthMaxHasValue);
|
||||
}
|
||||
}
|
|
@ -117,6 +117,17 @@ public class RomFsFileSystem : IFileSystem
|
|||
return ResultFs.UnsupportedGetTotalSpaceSizeForRomFsFileSystem.Log();
|
||||
}
|
||||
|
||||
protected override Result DoGetFileSystemAttribute(out FileSystemAttribute outAttribute)
|
||||
{
|
||||
outAttribute = default;
|
||||
outAttribute.DirectoryNameLengthMax = PathTool.EntryNameLengthMax;
|
||||
outAttribute.DirectoryNameLengthMaxHasValue = true;
|
||||
outAttribute.FileNameLengthMax = PathTool.EntryNameLengthMax;
|
||||
outAttribute.FileNameLengthMaxHasValue = true;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
internal static Result ConvertRomFsDriverPrivateResult(Result result)
|
||||
{
|
||||
if (result.IsSuccess())
|
||||
|
|
|
@ -247,6 +247,13 @@ public class SaveDataFileSystem : IFileSystem
|
|||
return SaveResults.ConvertToExternalResult(result).LogConverted(result);
|
||||
}
|
||||
|
||||
protected override Result DoGetFileSystemAttribute(out FileSystemAttribute outAttribute)
|
||||
{
|
||||
Result result = SaveDataFileSystemCore.GetFileSystemAttribute(out outAttribute);
|
||||
|
||||
return SaveResults.ConvertToExternalResult(result).LogConverted(result);
|
||||
}
|
||||
|
||||
protected override Result DoCommit()
|
||||
{
|
||||
Result result = Commit(KeySet);
|
||||
|
|
|
@ -229,6 +229,19 @@ public class SaveDataFileSystemCore : IFileSystem
|
|||
return Result.Success;
|
||||
}
|
||||
|
||||
protected override Result DoGetFileSystemAttribute(out FileSystemAttribute outAttribute)
|
||||
{
|
||||
const int maxSaveNameLength = 0x40;
|
||||
|
||||
outAttribute = default;
|
||||
outAttribute.DirectoryNameLengthMax = maxSaveNameLength;
|
||||
outAttribute.DirectoryNameLengthMaxHasValue = true;
|
||||
outAttribute.FileNameLengthMax = maxSaveNameLength;
|
||||
outAttribute.FileNameLengthMaxHasValue = true;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
protected override Result DoCommit()
|
||||
{
|
||||
return Result.Success;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Fs;
|
||||
using LibHac.Fs.Fsa;
|
||||
using LibHac.Fs.Impl;
|
||||
using Xunit;
|
||||
using static LibHac.Tests.Common.Layout;
|
||||
|
@ -258,6 +259,43 @@ public class TypeLayoutTests
|
|||
Assert.Equal(0xC, GetOffset(in s, in s.NumReadWriteErrorCorrections));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void FileSystemAttribute_Layout()
|
||||
{
|
||||
var s = new FileSystemAttribute();
|
||||
|
||||
Assert.Equal(0xC0, Unsafe.SizeOf<FileSystemAttribute>());
|
||||
|
||||
Assert.Equal(0x00, GetOffset(in s, in s.DirectoryNameLengthMaxHasValue));
|
||||
Assert.Equal(0x01, GetOffset(in s, in s.FileNameLengthMaxHasValue));
|
||||
Assert.Equal(0x02, GetOffset(in s, in s.DirectoryPathLengthMaxHasValue));
|
||||
Assert.Equal(0x03, GetOffset(in s, in s.FilePathLengthMaxHasValue));
|
||||
Assert.Equal(0x04, GetOffset(in s, in s.Utf16CreateDirectoryPathLengthMaxHasValue));
|
||||
Assert.Equal(0x05, GetOffset(in s, in s.Utf16DeleteDirectoryPathLengthMaxHasValue));
|
||||
Assert.Equal(0x06, GetOffset(in s, in s.Utf16RenameSourceDirectoryPathLengthMaxHasValue));
|
||||
Assert.Equal(0x07, GetOffset(in s, in s.Utf16RenameDestinationDirectoryPathLengthMaxHasValue));
|
||||
Assert.Equal(0x08, GetOffset(in s, in s.Utf16OpenDirectoryPathLengthMaxHasValue));
|
||||
Assert.Equal(0x09, GetOffset(in s, in s.Utf16DirectoryNameLengthMaxHasValue));
|
||||
Assert.Equal(0x0A, GetOffset(in s, in s.Utf16FileNameLengthMaxHasValue));
|
||||
Assert.Equal(0x0B, GetOffset(in s, in s.Utf16DirectoryPathLengthMaxHasValue));
|
||||
Assert.Equal(0x0C, GetOffset(in s, in s.Utf16FilePathLengthMaxHasValue));
|
||||
Assert.Equal(0x0D, GetOffset(in s, in s.Reserved1));
|
||||
Assert.Equal(0x28, GetOffset(in s, in s.DirectoryNameLengthMax));
|
||||
Assert.Equal(0x2C, GetOffset(in s, in s.FileNameLengthMax));
|
||||
Assert.Equal(0x30, GetOffset(in s, in s.DirectoryPathLengthMax));
|
||||
Assert.Equal(0x34, GetOffset(in s, in s.FilePathLengthMax));
|
||||
Assert.Equal(0x38, GetOffset(in s, in s.Utf16CreateDirectoryPathLengthMax));
|
||||
Assert.Equal(0x3C, GetOffset(in s, in s.Utf16DeleteDirectoryPathLengthMax));
|
||||
Assert.Equal(0x40, GetOffset(in s, in s.Utf16RenameSourceDirectoryPathLengthMax));
|
||||
Assert.Equal(0x44, GetOffset(in s, in s.Utf16RenameDestinationDirectoryPathLengthMax));
|
||||
Assert.Equal(0x48, GetOffset(in s, in s.Utf16OpenDirectoryPathLengthMax));
|
||||
Assert.Equal(0x4C, GetOffset(in s, in s.Utf16DirectoryNameLengthMax));
|
||||
Assert.Equal(0x50, GetOffset(in s, in s.Utf16FileNameLengthMax));
|
||||
Assert.Equal(0x54, GetOffset(in s, in s.Utf16DirectoryPathLengthMax));
|
||||
Assert.Equal(0x58, GetOffset(in s, in s.Utf16FilePathLengthMax));
|
||||
Assert.Equal(0x5C, GetOffset(in s, in s.Reserved2));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void FileTimeStamp_Layout()
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue