mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add fs_Host shims
This commit is contained in:
parent
a006816a2e
commit
7b4df4671c
8 changed files with 373 additions and 3 deletions
|
@ -12,6 +12,7 @@ namespace LibHac.Common
|
|||
private int _length;
|
||||
|
||||
public bool Overflowed { get; private set; }
|
||||
public int Length => _length;
|
||||
public int Capacity => _buffer.Length - NullTerminatorLength;
|
||||
|
||||
public U8StringBuilder(Span<byte> buffer)
|
||||
|
|
|
@ -4,6 +4,8 @@ namespace LibHac.Fs
|
|||
{
|
||||
internal static class CommonMountNames
|
||||
{
|
||||
public const char ReservedMountNamePrefixCharacter = '@';
|
||||
|
||||
public static readonly U8String GameCardFileSystemMountName = new U8String("@Gc");
|
||||
public static readonly U8String ContentStorageSystemMountName = new U8String("@SystemContent");
|
||||
public static readonly U8String ContentStorageUserMountName = new U8String("@UserContent");
|
||||
|
|
|
@ -150,6 +150,30 @@ namespace LibHac.Fs
|
|||
OutputAccessLogImpl(result, startTime, endTime, handle.GetId(), message, caller);
|
||||
}
|
||||
|
||||
internal void OutputAccessLogUnlessResultSuccess(Result result, TimeSpan startTime, TimeSpan endTime, string message, [CallerMemberName] string caller = "")
|
||||
{
|
||||
if (result.IsFailure())
|
||||
{
|
||||
OutputAccessLogImpl(result, startTime, endTime, 0, message, caller);
|
||||
}
|
||||
}
|
||||
|
||||
internal void OutputAccessLogUnlessResultSuccess(Result result, TimeSpan startTime, TimeSpan endTime, FileHandle handle, string message, [CallerMemberName] string caller = "")
|
||||
{
|
||||
if (result.IsFailure())
|
||||
{
|
||||
OutputAccessLogImpl(result, startTime, endTime, handle.GetId(), message, caller);
|
||||
}
|
||||
}
|
||||
|
||||
internal void OutputAccessLogUnlessResultSuccess(Result result, TimeSpan startTime, TimeSpan endTime, DirectoryHandle handle, string message, [CallerMemberName] string caller = "")
|
||||
{
|
||||
if (result.IsFailure())
|
||||
{
|
||||
OutputAccessLogImpl(result, startTime, endTime, handle.GetId(), message, caller);
|
||||
}
|
||||
}
|
||||
|
||||
internal void OutputAccessLogImpl(Result result, TimeSpan startTime, TimeSpan endTime, int handleId,
|
||||
string message, [CallerMemberName] string caller = "")
|
||||
{
|
||||
|
@ -208,6 +232,27 @@ namespace LibHac.Fs
|
|||
|
||||
return rc;
|
||||
}
|
||||
|
||||
public Result RunOperationWithAccessLogOnFailure(AccessLogTarget logTarget, Func<Result> operation,
|
||||
Func<string> textGenerator, [CallerMemberName] string caller = "")
|
||||
{
|
||||
Result rc;
|
||||
|
||||
if (IsEnabledAccessLog(logTarget))
|
||||
{
|
||||
TimeSpan startTime = Time.GetCurrent();
|
||||
rc = operation();
|
||||
TimeSpan endTime = Time.GetCurrent();
|
||||
|
||||
OutputAccessLogUnlessResultSuccess(rc, startTime, endTime, textGenerator(), caller);
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = operation();
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
|
|
|
@ -187,4 +187,10 @@ namespace LibHac.Fs
|
|||
Nand = 1,
|
||||
SdCard = 2
|
||||
}
|
||||
|
||||
public enum MountHostOption
|
||||
{
|
||||
None = 0,
|
||||
PseudoCaseSensitive = 1
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using LibHac.Common;
|
||||
using static LibHac.Fs.CommonMountNames;
|
||||
|
||||
namespace LibHac.Fs
|
||||
{
|
||||
|
@ -23,6 +24,11 @@ namespace LibHac.Fs
|
|||
return Result.Success;
|
||||
}
|
||||
|
||||
public static bool IsReservedMountName(U8Span name)
|
||||
{
|
||||
return (uint)name.Length > 0 && name[0] == ReservedMountNamePrefixCharacter;
|
||||
}
|
||||
|
||||
// ReSharper disable once UnusedParameter.Local
|
||||
private static bool CheckMountNameImpl(U8Span name)
|
||||
{
|
||||
|
|
304
src/LibHac/Fs/Shim/Host.cs
Normal file
304
src/LibHac/Fs/Shim/Host.cs
Normal file
|
@ -0,0 +1,304 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using LibHac.Common;
|
||||
using LibHac.FsService;
|
||||
using LibHac.FsSystem;
|
||||
using static LibHac.Fs.CommonMountNames;
|
||||
|
||||
namespace LibHac.Fs.Shim
|
||||
{
|
||||
public static class Host
|
||||
{
|
||||
private static ReadOnlySpan<byte> HostRootFileSystemPath => new[]
|
||||
{(byte) '@', (byte) 'H', (byte) 'o', (byte) 's', (byte) 't', (byte) ':', (byte) '/'};
|
||||
|
||||
private const int HostRootFileSystemPathLength = 8;
|
||||
|
||||
private class HostCommonMountNameGenerator : ICommonMountNameGenerator
|
||||
{
|
||||
private FsPath _path;
|
||||
|
||||
public HostCommonMountNameGenerator(U8Span path)
|
||||
{
|
||||
StringUtils.Copy(_path.Str, path);
|
||||
|
||||
int pathLength = StringUtils.GetLength(_path.Str);
|
||||
if (pathLength != 0 && _path.Str[pathLength - 1] == StringTraits.DirectorySeparator)
|
||||
{
|
||||
_path.Str[pathLength - 1] = StringTraits.NullTerminator;
|
||||
}
|
||||
}
|
||||
|
||||
public Result GenerateCommonMountName(Span<byte> nameBuffer)
|
||||
{
|
||||
int requiredNameBufferSize = StringUtils.GetLength(_path.Str, FsPath.MaxLength) + HostRootFileSystemPathLength;
|
||||
|
||||
if (nameBuffer.Length < requiredNameBufferSize)
|
||||
return ResultFs.TooLongPath.Log();
|
||||
|
||||
int size = new U8StringBuilder(nameBuffer).Append(HostRootFileSystemPath).Append(_path.Str).Length;
|
||||
Debug.Assert(size == requiredNameBufferSize - 1);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
private class HostRootCommonMountNameGenerator : ICommonMountNameGenerator
|
||||
{
|
||||
public Result GenerateCommonMountName(Span<byte> nameBuffer)
|
||||
{
|
||||
const int requiredNameBufferSize = HostRootFileSystemPathLength;
|
||||
|
||||
Debug.Assert(nameBuffer.Length >= requiredNameBufferSize);
|
||||
|
||||
int size = StringUtils.Copy(nameBuffer, HostRootFileSystemPath);
|
||||
Debug.Assert(size == requiredNameBufferSize - 1);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
|
||||
public static Result MountHostRoot(this FileSystemClient fs)
|
||||
{
|
||||
IFileSystem hostFileSystem = default;
|
||||
var path = new FsPath();
|
||||
path.Str[0] = 0;
|
||||
|
||||
static string LogMessageGenerator() => $", name: \"{HostRootFileSystemMountName.ToString()}\"";
|
||||
|
||||
Result OpenHostFs() => OpenHostFileSystemImpl(fs, out hostFileSystem, ref path, MountHostOption.None);
|
||||
|
||||
Result MountHostFs() => fs.Register(HostRootFileSystemMountName, hostFileSystem,
|
||||
new HostRootCommonMountNameGenerator());
|
||||
|
||||
// Open the host file system
|
||||
Result result =
|
||||
fs.RunOperationWithAccessLogOnFailure(AccessLogTarget.Application, OpenHostFs, LogMessageGenerator);
|
||||
if (result.IsFailure()) return result;
|
||||
|
||||
// Mount the host file system
|
||||
result = fs.RunOperationWithAccessLog(AccessLogTarget.Application, MountHostFs, LogMessageGenerator);
|
||||
if (result.IsFailure()) return result;
|
||||
|
||||
if (fs.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||
fs.EnableFileSystemAccessorAccessLog(HostRootFileSystemMountName);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static Result MountHostRoot(this FileSystemClient fs, MountHostOption option)
|
||||
{
|
||||
IFileSystem hostFileSystem = default;
|
||||
var path = new FsPath();
|
||||
path.Str[0] = 0;
|
||||
|
||||
string LogMessageGenerator() =>
|
||||
$", name: \"{HostRootFileSystemMountName.ToString()}, mount_host_option: {option}\"";
|
||||
|
||||
Result OpenHostFs() => OpenHostFileSystemImpl(fs, out hostFileSystem, ref path, option);
|
||||
|
||||
Result MountHostFs() => fs.Register(HostRootFileSystemMountName, hostFileSystem,
|
||||
new HostRootCommonMountNameGenerator());
|
||||
|
||||
// Open the host file system
|
||||
Result result =
|
||||
fs.RunOperationWithAccessLogOnFailure(AccessLogTarget.Application, OpenHostFs, LogMessageGenerator);
|
||||
if (result.IsFailure()) return result;
|
||||
|
||||
// Mount the host file system
|
||||
result = fs.RunOperationWithAccessLog(AccessLogTarget.Application, MountHostFs, LogMessageGenerator);
|
||||
if (result.IsFailure()) return result;
|
||||
|
||||
if (fs.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||
fs.EnableFileSystemAccessorAccessLog(HostRootFileSystemMountName);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
public static void UnmountHostRoot(this FileSystemClient fs)
|
||||
{
|
||||
fs.Unmount(HostRootFileSystemMountName);
|
||||
}
|
||||
|
||||
public static Result MountHost(this FileSystemClient fs, U8Span mountName, U8Span path)
|
||||
{
|
||||
return MountHostImpl(fs, mountName, path, null);
|
||||
}
|
||||
|
||||
public static Result MountHost(this FileSystemClient fs, U8Span mountName, U8Span path, MountHostOption option)
|
||||
{
|
||||
return MountHostImpl(fs, mountName, path, option);
|
||||
}
|
||||
|
||||
private static Result MountHostImpl(this FileSystemClient fs, U8Span mountName, U8Span path,
|
||||
MountHostOption? optionalOption, [CallerMemberName] string caller = "")
|
||||
{
|
||||
Result rc;
|
||||
ICommonMountNameGenerator nameGenerator;
|
||||
|
||||
string logMessage = null;
|
||||
var option = MountHostOption.None;
|
||||
|
||||
// Set the mount option if it was specified
|
||||
if (optionalOption.HasValue)
|
||||
{
|
||||
option = optionalOption.Value;
|
||||
}
|
||||
|
||||
if (fs.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||
{
|
||||
if (optionalOption.HasValue)
|
||||
{
|
||||
logMessage = $", name: \"{mountName.ToString()}\", mount_host_option: {option}";
|
||||
}
|
||||
else
|
||||
{
|
||||
logMessage = $", name: \"{mountName.ToString()}\"";
|
||||
}
|
||||
|
||||
TimeSpan startTime = fs.Time.GetCurrent();
|
||||
rc = PreMountHost(out nameGenerator, mountName, path);
|
||||
TimeSpan endTime = fs.Time.GetCurrent();
|
||||
|
||||
fs.OutputAccessLogUnlessResultSuccess(rc, startTime, endTime, logMessage, caller);
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = PreMountHost(out nameGenerator, mountName, path);
|
||||
}
|
||||
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
IFileSystem hostFileSystem;
|
||||
|
||||
if (fs.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||
{
|
||||
TimeSpan startTime = fs.Time.GetCurrent();
|
||||
rc = OpenHostFileSystem(fs, out hostFileSystem, mountName, path, option);
|
||||
TimeSpan endTime = fs.Time.GetCurrent();
|
||||
|
||||
fs.OutputAccessLogUnlessResultSuccess(rc, startTime, endTime, logMessage, caller);
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = OpenHostFileSystem(fs, out hostFileSystem, mountName, path, option);
|
||||
}
|
||||
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (fs.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||
{
|
||||
TimeSpan startTime = fs.Time.GetCurrent();
|
||||
rc = fs.Register(mountName, hostFileSystem, nameGenerator);
|
||||
TimeSpan endTime = fs.Time.GetCurrent();
|
||||
|
||||
fs.OutputAccessLog(rc, startTime, endTime, logMessage, caller);
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = fs.Register(mountName, hostFileSystem, nameGenerator);
|
||||
}
|
||||
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (fs.IsEnabledAccessLog(AccessLogTarget.Application))
|
||||
{
|
||||
fs.EnableFileSystemAccessorAccessLog(mountName);
|
||||
}
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private static Result PreMountHost(out ICommonMountNameGenerator nameGenerator, U8Span mountName, U8Span path)
|
||||
{
|
||||
nameGenerator = default;
|
||||
|
||||
Result rc = MountHelpers.CheckMountName(mountName);
|
||||
if (rc.IsFailure()) return rc;
|
||||
|
||||
if (path.IsNull())
|
||||
return ResultFs.NullptrArgument.Log();
|
||||
|
||||
nameGenerator = new HostCommonMountNameGenerator(path);
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
private static Result OpenHostFileSystem(FileSystemClient fs, out IFileSystem fileSystem, U8Span mountName,
|
||||
U8Span path, MountHostOption option)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
if (mountName.IsNull())
|
||||
return ResultFs.NullptrArgument.Log();
|
||||
|
||||
if (path.IsNull())
|
||||
return ResultFs.NullptrArgument.Log();
|
||||
|
||||
if (PathUtility.IsWindowsDrive(mountName))
|
||||
return ResultFs.InvalidMountName.Log();
|
||||
|
||||
if (MountHelpers.IsReservedMountName(mountName))
|
||||
return ResultFs.InvalidMountName.Log();
|
||||
|
||||
bool needsTrailingSeparator = false;
|
||||
int pathLength = StringUtils.GetLength(path, PathTools.MaxPathLength + 1);
|
||||
|
||||
if (pathLength != 0 && PathTool.IsSeparator(path[pathLength - 1]))
|
||||
{
|
||||
needsTrailingSeparator = true;
|
||||
pathLength++;
|
||||
}
|
||||
|
||||
if (pathLength + 1 > PathTools.MaxPathLength)
|
||||
return ResultFs.TooLongPath.Log();
|
||||
|
||||
FsPath fullPath;
|
||||
unsafe { _ = &fullPath; } // workaround for CS0165
|
||||
|
||||
var sb = new U8StringBuilder(fullPath.Str);
|
||||
sb.Append(StringTraits.DirectorySeparator).Append(path);
|
||||
|
||||
if (needsTrailingSeparator)
|
||||
{
|
||||
sb.Append(StringTraits.DirectorySeparator);
|
||||
}
|
||||
|
||||
if (sb.Overflowed)
|
||||
return ResultFs.TooLongPath.Log();
|
||||
|
||||
// If the input path begins with "//", change any leading '/' characters to '\'
|
||||
if (PathTool.IsSeparator(fullPath.Str[1]) && PathTool.IsSeparator(fullPath.Str[2]))
|
||||
{
|
||||
for (int i = 1; PathTool.IsSeparator(fullPath.Str[i]); i++)
|
||||
{
|
||||
fullPath.Str[i] = StringTraits.AltDirectorySeparator;
|
||||
}
|
||||
}
|
||||
|
||||
return OpenHostFileSystemImpl(fs, out fileSystem, ref fullPath, option);
|
||||
}
|
||||
|
||||
private static Result OpenHostFileSystemImpl(FileSystemClient fs, out IFileSystem fileSystem, ref FsPath path, MountHostOption option)
|
||||
{
|
||||
fileSystem = default;
|
||||
|
||||
IFileSystemProxy fsProxy = fs.GetFileSystemProxyServiceObject();
|
||||
IFileSystem hostFs;
|
||||
|
||||
if (option == MountHostOption.None)
|
||||
{
|
||||
Result rc = fsProxy.OpenHostFileSystem(out hostFs, ref path);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
else
|
||||
{
|
||||
Result rc = fsProxy.OpenHostFileSystemWithOption(out hostFs, ref path, option);
|
||||
if (rc.IsFailure()) return rc;
|
||||
}
|
||||
|
||||
fileSystem = hostFs;
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -42,7 +42,7 @@ namespace LibHac.FsService
|
|||
|
||||
var normalizer = new PathNormalizer(path, GetPathNormalizerOptions(path));
|
||||
if (normalizer.Result.IsFailure()) return normalizer.Result;
|
||||
|
||||
|
||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
||||
return FsProxyCore.OpenFileSystem(out fileSystem, normalizer.Path, type, canMountSystemDataPrivate, titleId);
|
||||
}
|
||||
|
@ -682,7 +682,12 @@ namespace LibHac.FsService
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenHostFileSystem(out IFileSystem fileSystem, ref FsPath subPath)
|
||||
public Result OpenHostFileSystemWithOption(out IFileSystem fileSystem, ref FsPath path, MountHostOption option)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Result OpenHostFileSystem(out IFileSystem fileSystem, ref FsPath path)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
|
|
@ -17,7 +17,8 @@ namespace LibHac.FsService
|
|||
Result OpenBisFileSystem(out IFileSystem fileSystem, ref FsPath rootPath, BisPartitionId partitionId);
|
||||
Result OpenBisStorage(out IStorage storage, BisPartitionId partitionId);
|
||||
Result InvalidateBisCache();
|
||||
Result OpenHostFileSystem(out IFileSystem fileSystem, ref FsPath subPath);
|
||||
Result OpenHostFileSystemWithOption(out IFileSystem fileSystem, ref FsPath path, MountHostOption option);
|
||||
Result OpenHostFileSystem(out IFileSystem fileSystem, ref FsPath path);
|
||||
Result OpenSdCardFileSystem(out IFileSystem fileSystem);
|
||||
Result FormatSdCardFileSystem();
|
||||
Result DeleteSaveDataFileSystem(ulong saveDataId);
|
||||
|
|
Loading…
Reference in a new issue