Implement UserFileSystem and mount registration

This commit is contained in:
Alex Barney 2021-02-20 22:10:12 -07:00
parent a11e84cc81
commit 0dc433d8a2
13 changed files with 1304 additions and 212 deletions

View file

@ -533,6 +533,7 @@ Module,DescriptionStart,DescriptionEnd,Flags,Namespace,Name,Summary
2,5307,,,,UnexpectedErrorInHostFileFlush,
2,5308,,,,UnexpectedErrorInHostFileGetSize,
2,5309,,,,UnknownHostFileSystemError,
2,5319,,,,UnexpectedInMountUtilityA,
2,5320,,,,InvalidNcaMountPoint,
2,6000,6499,,,PreconditionViolation,

1 Module DescriptionStart DescriptionEnd Flags Namespace Name Summary
533 2 6321 6320 UnsupportedSetSizeForBlockCacheBufferedStorage UnsupportedOperateRangeForIntegrityVerificationStorage
534 2 6322 6321 UnsupportedOperateRangeForNonSaveDataBlockCacheBufferedStorage UnsupportedSetSizeForBlockCacheBufferedStorage
535 2 6323 6322 UnsupportedOperateRangeForBlockCacheBufferedStorage UnsupportedOperateRangeForNonSaveDataBlockCacheBufferedStorage
536 2 6323 UnsupportedOperateRangeForBlockCacheBufferedStorage
537 2 6324 UnsupportedWriteForIndirectStorage
538 2 6325 UnsupportedSetSizeForIndirectStorage
539 2 6326 UnsupportedOperateRangeForIndirectStorage

View file

@ -435,5 +435,19 @@ namespace LibHac.Fs.Impl
{
throw new NotImplementedException();
}
public static ReadOnlySpan<byte> ConvertFromBoolToAccessLogBooleanValue(bool value)
{
return value ? LogTrue : LogFalse;
}
private static ReadOnlySpan<byte> LogTrue => // "true"
new[] { (byte)'t', (byte)'r', (byte)'u', (byte)'e' };
private static ReadOnlySpan<byte> LogFalse => // "false"
new[]
{
(byte)'f', (byte)'a', (byte)'l', (byte)'s', (byte)'e'
};
}
}

View file

@ -88,7 +88,7 @@ namespace LibHac.Fs
filter.SetProgramId(applicationId);
filter.SetSaveDataType(SaveDataType.Temporary);
Result rc = fs.FindSaveDataWithFilter(out _, SaveDataSpaceId.Temporary, ref filter);
Result rc = fs.FindSaveDataWithFilter(out _, SaveDataSpaceId.Temporary, in filter);
if (rc.IsFailure())
{
@ -176,7 +176,7 @@ namespace LibHac.Fs
private static Result EnsureAndExtendSaveData(FileSystemClient fs, Func<Result> createFunc,
ref long requiredSize, ref SaveDataFilter filter, long baseSize, long dataSize, long journalSize)
{
Result rc = fs.FindSaveDataWithFilter(out SaveDataInfo info, SaveDataSpaceId.User, ref filter);
Result rc = fs.FindSaveDataWithFilter(out SaveDataInfo info, SaveDataSpaceId.User, in filter);
if (rc.IsFailure())
{
@ -346,7 +346,7 @@ namespace LibHac.Fs
filter.SetIndex(index);
filter.SetSaveDataType(SaveDataType.Cache);
Result rc = fs.FindSaveDataWithFilter(out SaveDataInfo info, spaceId, ref filter);
Result rc = fs.FindSaveDataWithFilter(out SaveDataInfo info, spaceId, in filter);
if (rc.IsFailure())
{
@ -401,7 +401,7 @@ namespace LibHac.Fs
if (fs.IsSdCardAccessible())
{
Result rc = fs.FindSaveDataWithFilter(out _, SaveDataSpaceId.SdCache, ref filter);
Result rc = fs.FindSaveDataWithFilter(out _, SaveDataSpaceId.SdCache, in filter);
if (rc.IsFailure() && !ResultFs.TargetNotFound.Includes(rc)) return rc;
if (rc.IsSuccess())
@ -413,7 +413,7 @@ namespace LibHac.Fs
// Not on the SD card. Check it it's in NAND
if (target == CacheStorageTargetMedia.None)
{
Result rc = fs.FindSaveDataWithFilter(out _, SaveDataSpaceId.User, ref filter);
Result rc = fs.FindSaveDataWithFilter(out _, SaveDataSpaceId.User, in filter);
if (rc.IsFailure() && !ResultFs.TargetNotFound.Includes(rc)) return rc;
if (rc.IsSuccess())
@ -434,7 +434,7 @@ namespace LibHac.Fs
while (true)
{
rc = fs.FindSaveDataWithFilter(out SaveDataInfo saveInfo, SaveDataSpaceId.Temporary, ref filter);
rc = fs.FindSaveDataWithFilter(out SaveDataInfo saveInfo, SaveDataSpaceId.Temporary, in filter);
if (rc.IsFailure()) break;

View file

@ -177,9 +177,11 @@ namespace LibHac.Fs
Restore = 1 << 4
}
[Flags]
public enum CommitOptionFlag
{
None = 1,
None = 0,
ClearRestoreFlag = 1,
SetRestoreFlag = 2
}

View file

@ -1,6 +1,11 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Diag;
using LibHac.Fs.Impl;
using LibHac.FsSystem;
using LibHac.Os;
using LibHac.Util;
namespace LibHac.Fs.Fsa
{
@ -8,59 +13,260 @@ namespace LibHac.Fs.Fsa
{
internal static Result GetMountNameAndSubPath(out MountName mountName, out U8Span subPath, U8Span path)
{
throw new NotImplementedException();
Unsafe.SkipInit(out mountName);
subPath = default;
int mountLen = 0;
int maxMountLen = Math.Min(path.Length, PathTools.MountNameLengthMax);
if (PathUtility.IsWindowsDrive(path) || PathUtility.IsUnc(path))
{
StringUtils.Copy(mountName.Name, CommonPaths.HostRootFileSystemMountName);
mountName.Name[PathTools.MountNameLengthMax] = StringTraits.NullTerminator;
subPath = path;
return Result.Success;
}
for (int i = 0; i <= maxMountLen; i++)
{
if (path[i] == PathTools.MountSeparator)
{
mountLen = i;
break;
}
}
if (mountLen == 0)
return ResultFs.InvalidMountName.Log();
if (mountLen > maxMountLen)
return ResultFs.InvalidMountName.Log();
if (mountLen <= 0)
return ResultFs.InvalidMountName.Log();
U8Span subPathTemp = path.Slice(mountLen + 1);
if (subPathTemp.Length == 0 || !PathTool.IsAnySeparator(subPathTemp[0]))
return ResultFs.InvalidPathFormat.Log();
path.Value.Slice(0, mountLen).CopyTo(mountName.Name);
mountName.Name[mountLen] = StringTraits.NullTerminator;
subPath = subPathTemp;
return Result.Success;
}
public static bool IsValidMountName(this FileSystemClientImpl fs, U8Span name)
{
throw new NotImplementedException();
if (name.IsEmpty())
return false;
// Check for a single-letter mount name
if ((name.Length <= 1 || name[1] == 0) &&
('a' <= name[0] && name[0] <= 'z' || 'A' <= name[0] && name[0] <= 'Z'))
{
return false;
}
// Check for mount or directory separators
int length = 0;
for (int i = 0; i < name.Length && name[i] != 0; i++)
{
if (PathTool.IsDriveSeparator(name[i]) || PathTool.IsSeparator(name[i]))
return false;
if (++length > PathTools.MountNameLengthMax)
return false;
}
// Todo: VerifyUtf8String
return true;
}
public static bool IsUsedReservedMountName(this FileSystemClientImpl fs, U8Span name)
{
throw new NotImplementedException();
return name.Length > 0 && name[0] == CommonPaths.ReservedMountNamePrefixCharacter;
}
internal static Result FindFileSystem(this FileSystemClientImpl fs, out FileSystemAccessor fileSystem,
out U8Span subPath, U8Span path)
{
throw new NotImplementedException();
fileSystem = default;
subPath = default;
if (path.IsNull())
return ResultFs.NullptrArgument.Log();
int hostMountNameLen = StringUtils.GetLength(CommonPaths.HostRootFileSystemMountName);
if (StringUtils.Compare(path, CommonPaths.HostRootFileSystemMountName, hostMountNameLen) == 0)
{
return ResultFs.NotMounted.Log();
}
Result rc = GetMountNameAndSubPath(out MountName mountName, out subPath, path);
if (rc.IsFailure()) return rc;
return fs.Find(out fileSystem, new U8Span(mountName.Name));
}
public static Result CheckMountName(this FileSystemClientImpl fs, U8Span name)
{
throw new NotImplementedException();
if (name.IsNull())
return ResultFs.NullptrArgument.Log();
if (fs.IsUsedReservedMountName(name))
return ResultFs.InvalidMountName.Log();
if (fs.IsValidMountName(name))
return ResultFs.InvalidMountName.Log();
return Result.Success;
}
public static Result CheckMountNameAcceptingReservedMountName(this FileSystemClientImpl fs, U8Span name)
{
throw new NotImplementedException();
if (name.IsNull())
return ResultFs.NullptrArgument.Log();
if (fs.IsValidMountName(name))
return ResultFs.InvalidMountName.Log();
return Result.Success;
}
public static Result Unmount(this FileSystemClientImpl fs, U8Span mountName)
{
throw new NotImplementedException();
Result rc = fs.Find(out FileSystemAccessor fileSystem, mountName);
if (rc.IsFailure()) return rc;
if (fileSystem.IsFileDataCacheAttachable())
{
// Todo: Data cache purge
}
fs.Unregister(mountName);
return Result.Success;
}
public static Result IsMounted(this FileSystemClientImpl fs, out bool isMounted, U8Span mountName)
{
throw new NotImplementedException();
Unsafe.SkipInit(out isMounted);
Result rc = fs.Find(out _, mountName);
if (rc.IsFailure())
{
if (!ResultFs.NotMounted.Includes(rc))
return rc;
isMounted = false;
}
else
{
isMounted = true;
}
public static Result Unmount(this FileSystemClient fs, U8Span mountName)
{
throw new NotImplementedException();
return Result.Success;
}
public static Result IsMounted(this FileSystemClient fs, out bool isMounted, U8Span mountName)
public static void Unmount(this FileSystemClient fs, U8Span mountName)
{
throw new NotImplementedException();
Result rc;
Span<byte> logBuffer = stackalloc byte[0x30];
if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledFileSystemAccessorAccessLog(mountName))
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fs.Impl.Unmount(mountName);
Tick end = fs.Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
sb.Append(LogName).Append(mountName).Append((byte)'"');
logBuffer = sb.Buffer;
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = fs.Impl.Unmount(mountName);
}
fs.Impl.LogErrorMessage(rc);
Abort.DoAbortUnless(rc.IsSuccess());
}
public static bool IsMounted(this FileSystemClient fs, U8Span mountName)
{
Result rc;
bool isMounted;
Span<byte> logBuffer = stackalloc byte[0x30];
if (fs.Impl.IsEnabledAccessLog() && fs.Impl.IsEnabledFileSystemAccessorAccessLog(mountName))
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fs.Impl.IsMounted(out isMounted, mountName);
Tick end = fs.Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
ReadOnlySpan<byte> boolString = AccessLogImpl.ConvertFromBoolToAccessLogBooleanValue(isMounted);
sb.Append(LogName).Append(mountName).Append(LogIsMounted).Append(boolString).Append((byte)'"');
logBuffer = sb.Buffer;
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = fs.Impl.IsMounted(out isMounted, mountName);
}
fs.Impl.LogErrorMessage(rc);
Abort.DoAbortUnless(rc.IsSuccess());
return isMounted;
}
public static Result ConvertToFsCommonPath(this FileSystemClient fs, U8SpanMutable commonPathBuffer,
U8Span path)
{
throw new NotImplementedException();
if (commonPathBuffer.IsNull())
return ResultFs.NullptrArgument.Log();
if (path.IsNull())
return ResultFs.NullptrArgument.Log();
Result rc = GetMountNameAndSubPath(out MountName mountName, out U8Span subPath, path);
fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc;
rc = fs.Impl.Find(out FileSystemAccessor fileSystem, new U8Span(mountName.Name));
fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc;
rc = fileSystem.GetCommonMountName(commonPathBuffer.Value);
fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc;
int mountNameLength = StringUtils.GetLength(commonPathBuffer);
int commonPathLength = StringUtils.GetLength(subPath);
if (mountNameLength + commonPathLength > commonPathBuffer.Length)
return ResultFs.TooLongPath.Log();
StringUtils.Copy(commonPathBuffer.Slice(commonPathLength), subPath);
return Result.Success;
}
private static ReadOnlySpan<byte> LogName => // ", name: ""
new[]
{
(byte)',', (byte)' ', (byte)'n', (byte)'a', (byte)'m', (byte)'e', (byte)':', (byte)' ',
(byte)'"'
};
private static ReadOnlySpan<byte> LogIsMounted => // "", is_mounted: ""
new[]
{
(byte)'"', (byte)',', (byte)' ', (byte)'i', (byte)'s', (byte)'_', (byte)'m', (byte)'o',
(byte)'u', (byte)'n', (byte)'t', (byte)'e', (byte)'d', (byte)':', (byte)' ', (byte)'"'
};
}
}

View file

@ -1,5 +1,6 @@
using System;
using LibHac.Common;
using LibHac.Fs.Impl;
namespace LibHac.Fs.Fsa
{
@ -15,32 +16,47 @@ namespace LibHac.Fs.Fsa
internal static class Registrar
{
public static Result Register(U8Span name, IFileSystem fileSystem)
public static Result Register(this FileSystemClient fs, U8Span name, IFileSystem fileSystem)
{
throw new NotImplementedException();
var accessor = new FileSystemAccessor(name, null, fileSystem, null, null);
fs.Impl.Register(accessor);
return Result.Success;
}
public static Result Register(U8Span name, IFileSystem fileSystem, ICommonMountNameGenerator mountNameGenerator)
public static Result Register(this FileSystemClient fs, U8Span name, IFileSystem fileSystem,
ICommonMountNameGenerator mountNameGenerator)
{
throw new NotImplementedException();
var accessor = new FileSystemAccessor(name, null, fileSystem, mountNameGenerator, null);
fs.Impl.Register(accessor);
return Result.Success;
}
public static Result Register(U8Span name, IMultiCommitTarget multiCommitTarget, IFileSystem fileSystem,
ICommonMountNameGenerator mountNameGenerator, bool useDataCache, bool usePathCache)
public static Result Register(this FileSystemClient fs, U8Span name, IMultiCommitTarget multiCommitTarget,
IFileSystem fileSystem, ICommonMountNameGenerator mountNameGenerator, bool useDataCache, bool usePathCache)
{
throw new NotImplementedException();
return fs.Register(name, multiCommitTarget, fileSystem, mountNameGenerator, null, useDataCache,
usePathCache);
}
public static Result Register(U8Span name, IMultiCommitTarget multiCommitTarget, IFileSystem fileSystem,
ICommonMountNameGenerator mountNameGenerator, ISaveDataAttributeGetter saveAttributeGetter,
bool useDataCache, bool usePathCache)
public static Result Register(this FileSystemClient fs, U8Span name, IMultiCommitTarget multiCommitTarget,
IFileSystem fileSystem, ICommonMountNameGenerator mountNameGenerator,
ISaveDataAttributeGetter saveAttributeGetter, bool useDataCache, bool usePathCache)
{
throw new NotImplementedException();
var accessor = new FileSystemAccessor(name, multiCommitTarget, fileSystem, mountNameGenerator,
saveAttributeGetter);
accessor.SetFileDataCacheAttachable(useDataCache);
accessor.SetPathBasedFileDataCacheAttachable(usePathCache);
fs.Impl.Register(accessor);
return Result.Success;
}
public static void Unregister(U8Span name)
public static void Unregister(this FileSystemClient fs, U8Span name)
{
fs.Impl.Unregister(name);
}
}
}

View file

@ -210,7 +210,7 @@ namespace LibHac.Fs.Fsa
Result rc = Get(handle).OperateRange(SpanHelpers.AsByteSpan(ref rangeInfo), OperationId.QueryRange, offset,
size, ReadOnlySpan<byte>.Empty);
fs.Impl.IsAbortNeeded(rc);
fs.Impl.AbortIfNeeded(rc);
return rc;
}
@ -219,7 +219,7 @@ namespace LibHac.Fs.Fsa
Result rc = Get(handle).OperateRange(Span<byte>.Empty, OperationId.InvalidateCache, offset, size,
ReadOnlySpan<byte>.Empty);
fs.Impl.IsAbortNeeded(rc);
fs.Impl.AbortIfNeeded(rc);
return rc;
}

View file

@ -1,101 +1,805 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Fs.Impl;
using LibHac.Fs.Shim;
using LibHac.FsSrv.Sf;
using LibHac.Os;
using IFileSystemSf = LibHac.FsSrv.Sf.IFileSystem;
namespace LibHac.Fs.Fsa
{
[SkipLocalsInit]
public static class UserFileSystem
{
public static Result CreateFile(this FileSystemClient fs, U8Span path, long size)
{
throw new NotImplementedException();
return fs.CreateFile(path, size, CreateFileOptions.None);
}
public static Result DeleteFile(this FileSystemClient fs, U8Span path)
{
throw new NotImplementedException();
Result rc;
U8Span subPath;
FileSystemAccessor fileSystem;
Span<byte> logBuffer = stackalloc byte[0x300];
if (fs.Impl.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fs.Impl.FindFileSystem(out fileSystem, out subPath, path);
Tick end = fs.Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
sb.Append(LogPath).Append(path).Append((byte)'"');
logBuffer = sb.Buffer;
fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = fs.Impl.FindFileSystem(out fileSystem, out subPath, path);
}
fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc;
if (fs.Impl.IsEnabledAccessLog() && fileSystem.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fileSystem.DeleteFile(subPath);
Tick end = fs.Hos.Os.GetSystemTick();
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = fileSystem.DeleteFile(subPath);
}
fs.Impl.AbortIfNeeded(rc);
return rc;
}
public static Result CreateDirectory(this FileSystemClient fs, U8Span path)
{
throw new NotImplementedException();
Result rc;
U8Span subPath;
FileSystemAccessor fileSystem;
Span<byte> logBuffer = stackalloc byte[0x300];
if (fs.Impl.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fs.Impl.FindFileSystem(out fileSystem, out subPath, path);
Tick end = fs.Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
sb.Append(LogPath).Append(path).Append((byte)'"');
logBuffer = sb.Buffer;
fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = fs.Impl.FindFileSystem(out fileSystem, out subPath, path);
}
fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc;
if (fs.Impl.IsEnabledAccessLog() && fileSystem.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fileSystem.CreateDirectory(subPath);
Tick end = fs.Hos.Os.GetSystemTick();
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = fileSystem.CreateDirectory(subPath);
}
fs.Impl.AbortIfNeeded(rc);
return rc;
}
public static Result DeleteDirectory(this FileSystemClient fs, U8Span path)
{
throw new NotImplementedException();
Result rc;
U8Span subPath;
FileSystemAccessor fileSystem;
Span<byte> logBuffer = stackalloc byte[0x300];
if (fs.Impl.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fs.Impl.FindFileSystem(out fileSystem, out subPath, path);
Tick end = fs.Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
sb.Append(LogPath).Append(path).Append((byte)'"');
logBuffer = sb.Buffer;
fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = fs.Impl.FindFileSystem(out fileSystem, out subPath, path);
}
fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc;
if (fs.Impl.IsEnabledAccessLog() && fileSystem.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fileSystem.DeleteDirectory(subPath);
Tick end = fs.Hos.Os.GetSystemTick();
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = fileSystem.DeleteDirectory(subPath);
}
fs.Impl.AbortIfNeeded(rc);
return rc;
}
public static Result DeleteDirectoryRecursively(this FileSystemClient fs, U8Span path)
{
throw new NotImplementedException();
Result rc;
U8Span subPath;
FileSystemAccessor fileSystem;
Span<byte> logBuffer = stackalloc byte[0x300];
if (fs.Impl.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fs.Impl.FindFileSystem(out fileSystem, out subPath, path);
Tick end = fs.Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
sb.Append(LogPath).Append(path).Append((byte)'"');
logBuffer = sb.Buffer;
fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = fs.Impl.FindFileSystem(out fileSystem, out subPath, path);
}
fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc;
if (fs.Impl.IsEnabledAccessLog() && fileSystem.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fileSystem.DeleteDirectoryRecursively(subPath);
Tick end = fs.Hos.Os.GetSystemTick();
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = fileSystem.DeleteDirectoryRecursively(subPath);
}
fs.Impl.AbortIfNeeded(rc);
return rc;
}
public static Result CleanDirectoryRecursively(this FileSystemClient fs, U8Span path)
{
throw new NotImplementedException();
Result rc;
U8Span subPath;
FileSystemAccessor fileSystem;
Span<byte> logBuffer = stackalloc byte[0x300];
if (fs.Impl.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fs.Impl.FindFileSystem(out fileSystem, out subPath, path);
Tick end = fs.Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
sb.Append(LogPath).Append(path).Append((byte)'"');
logBuffer = sb.Buffer;
fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = fs.Impl.FindFileSystem(out fileSystem, out subPath, path);
}
fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc;
if (fs.Impl.IsEnabledAccessLog() && fileSystem.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fileSystem.CleanDirectoryRecursively(subPath);
Tick end = fs.Hos.Os.GetSystemTick();
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = fileSystem.CleanDirectoryRecursively(subPath);
}
fs.Impl.AbortIfNeeded(rc);
return rc;
}
public static Result RenameFile(this FileSystemClient fs, U8Span oldPath, U8Span newPath)
{
throw new NotImplementedException();
Result rc;
U8Span currentSubPath, newSubPath;
FileSystemAccessor currentFileSystem, newFileSystem;
Span<byte> logBuffer = stackalloc byte[0x300];
// Get the file system accessor for the current path
if (fs.Impl.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fs.Impl.FindFileSystem(out currentFileSystem, out currentSubPath, oldPath);
Tick end = fs.Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
sb.Append(LogPath).Append(oldPath).Append(LogNewPath).Append(newPath).Append((byte)'"');
logBuffer = sb.Buffer;
fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = fs.Impl.FindFileSystem(out currentFileSystem, out currentSubPath, oldPath);
}
fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc;
// Get the file system accessor for the new path
if (fs.Impl.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fs.Impl.FindFileSystem(out newFileSystem, out newSubPath, newPath);
Tick end = fs.Hos.Os.GetSystemTick();
fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = fs.Impl.FindFileSystem(out newFileSystem, out newSubPath, newPath);
}
fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc;
// Rename the file
if (fs.Impl.IsEnabledAccessLog() && currentFileSystem.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = currentFileSystem != newFileSystem
? ResultFs.RenameToOtherFileSystem.Log()
: currentFileSystem.RenameFile(currentSubPath, newSubPath);
Tick end = fs.Hos.Os.GetSystemTick();
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = currentFileSystem != newFileSystem
? ResultFs.RenameToOtherFileSystem.Log()
: currentFileSystem.RenameFile(currentSubPath, newSubPath);
}
fs.Impl.AbortIfNeeded(rc);
return rc;
}
public static Result RenameDirectory(this FileSystemClient fs, U8Span oldPath, U8Span newPath)
{
throw new NotImplementedException();
Result rc;
U8Span currentSubPath, newSubPath;
FileSystemAccessor currentFileSystem, newFileSystem;
Span<byte> logBuffer = stackalloc byte[0x300];
// Get the file system accessor for the current path
if (fs.Impl.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fs.Impl.FindFileSystem(out currentFileSystem, out currentSubPath, oldPath);
Tick end = fs.Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
sb.Append(LogPath).Append(oldPath).Append(LogNewPath).Append(newPath).Append((byte)'"');
logBuffer = sb.Buffer;
fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = fs.Impl.FindFileSystem(out currentFileSystem, out currentSubPath, oldPath);
}
fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc;
// Get the file system accessor for the new path
if (fs.Impl.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fs.Impl.FindFileSystem(out newFileSystem, out newSubPath, newPath);
Tick end = fs.Hos.Os.GetSystemTick();
fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = fs.Impl.FindFileSystem(out newFileSystem, out newSubPath, newPath);
}
fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc;
// Rename the directory
if (fs.Impl.IsEnabledAccessLog() && currentFileSystem.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = currentFileSystem != newFileSystem
? ResultFs.RenameToOtherFileSystem.Log()
: currentFileSystem.RenameDirectory(currentSubPath, newSubPath);
Tick end = fs.Hos.Os.GetSystemTick();
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = currentFileSystem != newFileSystem
? ResultFs.RenameToOtherFileSystem.Log()
: currentFileSystem.RenameDirectory(currentSubPath, newSubPath);
}
fs.Impl.AbortIfNeeded(rc);
return rc;
}
public static Result GetEntryType(this FileSystemClient fs, out DirectoryEntryType type, U8Span path)
{
throw new NotImplementedException();
Unsafe.SkipInit(out type);
Result rc;
U8Span subPath;
FileSystemAccessor fileSystem;
Span<byte> logBuffer = stackalloc byte[0x300];
if (fs.Impl.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fs.Impl.FindFileSystem(out fileSystem, out subPath, path);
Tick end = fs.Hos.Os.GetSystemTick();
var idString = new IdString();
var sb = new U8StringBuilder(logBuffer, true);
sb.Append(LogPath).Append(path).Append(LogEntryType)
.Append(idString.ToString(AccessLogImpl.DereferenceOutValue(in type, rc)));
logBuffer = sb.Buffer;
fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = fs.Impl.FindFileSystem(out fileSystem, out subPath, path);
}
fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc;
if (fs.Impl.IsEnabledAccessLog() && fileSystem.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fileSystem.GetEntryType(out type, subPath);
Tick end = fs.Hos.Os.GetSystemTick();
var idString = new IdString();
var sb = new U8StringBuilder(logBuffer, true);
sb.Append(LogPath).Append(path).Append(LogEntryType)
.Append(idString.ToString(AccessLogImpl.DereferenceOutValue(in type, rc)));
logBuffer = sb.Buffer;
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = fileSystem.GetEntryType(out type, subPath);
}
fs.Impl.AbortIfNeeded(rc);
return rc;
}
public static Result GetFreeSpaceSize(this FileSystemClient fs, out long freeSpace, U8Span path)
{
throw new NotImplementedException();
Unsafe.SkipInit(out freeSpace);
Result rc;
var subPath = U8Span.Empty;
FileSystemAccessor fileSystem;
Span<byte> logBuffer = stackalloc byte[0x300];
if (fs.Impl.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
if (fs.Impl.IsValidMountName(path))
{
rc = fs.Impl.Find(out fileSystem, path);
if (rc.IsFailure()) return rc;
}
else
{
rc = fs.Impl.FindFileSystem(out fileSystem, out subPath, path);
if (rc.IsFailure()) return rc;
}
Tick end = fs.Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
sb.Append(LogPath).Append(path).Append(LogSize)
.AppendFormat(AccessLogImpl.DereferenceOutValue(in freeSpace, rc));
logBuffer = sb.Buffer;
fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer));
}
else
{
if (fs.Impl.IsValidMountName(path))
{
rc = fs.Impl.Find(out fileSystem, path);
if (rc.IsFailure()) return rc;
}
else
{
rc = fs.Impl.FindFileSystem(out fileSystem, out subPath, path);
if (rc.IsFailure()) return rc;
}
}
fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc;
if (fs.Impl.IsEnabledAccessLog() && fileSystem.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fileSystem.GetFreeSpaceSize(out freeSpace, subPath);
Tick end = fs.Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
sb.Append(LogPath).Append(path).Append(LogSize)
.AppendFormat(AccessLogImpl.DereferenceOutValue(in freeSpace, rc));
logBuffer = sb.Buffer;
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = fileSystem.GetFreeSpaceSize(out freeSpace, subPath);
}
fs.Impl.AbortIfNeeded(rc);
return rc;
}
public static Result OpenFile(this FileSystemClient fs, out FileHandle2 handle, U8Span path, OpenMode mode)
{
throw new NotImplementedException();
handle = default;
Result rc;
U8Span subPath;
FileSystemAccessor fileSystem;
Span<byte> logBuffer = stackalloc byte[0x300];
if (fs.Impl.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fs.Impl.FindFileSystem(out fileSystem, out subPath, path);
Tick end = fs.Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
sb.Append(LogPath).Append(path).Append(LogOpenMode).AppendFormat((int)mode, 'X');
logBuffer = sb.Buffer;
fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = fs.Impl.FindFileSystem(out fileSystem, out subPath, path);
}
fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc;
FileAccessor accessor;
if (fs.Impl.IsEnabledAccessLog() && fileSystem.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fileSystem.OpenFile(out accessor, subPath, mode);
Tick end = fs.Hos.Os.GetSystemTick();
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = fileSystem.OpenFile(out accessor, subPath, mode);
}
fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc;
handle = new FileHandle2(accessor);
return Result.Success;
}
public static Result OpenFile(this FileSystemClient fs, out FileHandle2 handle, IFile file, OpenMode mode)
{
throw new NotImplementedException();
var accessor = new FileAccessor(ref file, null, mode);
handle = new FileHandle2(accessor);
return Result.Success;
}
public static Result OpenDirectory(this FileSystemClient fs, out DirectoryHandle2 handle, U8Span path,
OpenDirectoryMode mode)
{
throw new NotImplementedException();
handle = default;
Result rc;
U8Span subPath;
FileSystemAccessor fileSystem;
Span<byte> logBuffer = stackalloc byte[0x300];
if (fs.Impl.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fs.Impl.FindFileSystem(out fileSystem, out subPath, path);
Tick end = fs.Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
sb.Append(LogPath).Append(path).Append(LogOpenMode).AppendFormat((int)mode, 'X');
logBuffer = sb.Buffer;
fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = fs.Impl.FindFileSystem(out fileSystem, out subPath, path);
}
fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc;
DirectoryAccessor accessor;
if (fs.Impl.IsEnabledAccessLog() && fileSystem.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fileSystem.OpenDirectory(out accessor, subPath, mode);
Tick end = fs.Hos.Os.GetSystemTick();
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = fileSystem.OpenDirectory(out accessor, subPath, mode);
}
fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc;
handle = new DirectoryHandle2(accessor);
return Result.Success;
}
private static Result CommitImpl(FileSystemClient fs, U8Span mountName,
[CallerMemberName] string functionName = "")
{
throw new NotImplementedException();
Result rc;
FileSystemAccessor fileSystem;
Span<byte> logBuffer = stackalloc byte[0x30];
if (fs.Impl.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fs.Impl.Find(out fileSystem, mountName);
Tick end = fs.Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
sb.Append(LogName).Append(mountName).Append((byte)'"');
logBuffer = sb.Buffer;
fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer), functionName);
}
else
{
rc = fs.Impl.Find(out fileSystem, mountName);
}
fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc;
if (fs.Impl.IsEnabledAccessLog() && fileSystem.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fileSystem.Commit();
Tick end = fs.Hos.Os.GetSystemTick();
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer), functionName);
}
else
{
rc = fileSystem.Commit();
}
fs.Impl.AbortIfNeeded(rc);
return rc;
}
public static Result Commit(this FileSystemClient fs, ReadOnlySpan<U8String> mountNames)
{
throw new NotImplementedException();
// Todo: Add access log
if (mountNames.Length > 10)
return ResultFs.InvalidCommitNameCount.Log();
if (mountNames.Length == 0)
return Result.Success;
ReferenceCountedDisposable<IMultiCommitManager> commitManager = null;
ReferenceCountedDisposable<IFileSystemSf> fileSystem = null;
try
{
using ReferenceCountedDisposable<IFileSystemProxy> fsProxy = fs.Impl.GetFileSystemProxyServiceObject();
Result rc = fsProxy.Target.OpenMultiCommitManager(out commitManager);
if (rc.IsFailure()) return rc;
for (int i = 0; i < mountNames.Length; i++)
{
rc = fs.Impl.Find(out FileSystemAccessor accessor, mountNames[i]);
if (rc.IsFailure()) return rc;
fileSystem = accessor.GetMultiCommitTarget();
if (fileSystem is null)
return ResultFs.UnsupportedCommitTarget.Log();
rc = commitManager.Target.Add(fileSystem);
if (rc.IsFailure()) return rc;
}
rc = commitManager.Target.Commit();
if (rc.IsFailure()) return rc;
return Result.Success;
}
finally
{
commitManager?.Dispose();
fileSystem?.Dispose();
}
}
public static Result Commit(this FileSystemClient fs, U8Span mountName, CommitOption option)
{
throw new NotImplementedException();
Result rc;
FileSystemAccessor fileSystem;
Span<byte> logBuffer = stackalloc byte[0x40];
if (fs.Impl.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fs.Impl.Find(out fileSystem, mountName);
Tick end = fs.Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
sb.Append(LogName).Append(mountName).Append(LogCommitOption).AppendFormat((int)option.Flags, 'X');
logBuffer = sb.Buffer;
fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = fs.Impl.Find(out fileSystem, mountName);
}
fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc;
if (fs.Impl.IsEnabledAccessLog() && fileSystem.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = RunCommit(fs, option, fileSystem);
Tick end = fs.Hos.Os.GetSystemTick();
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = RunCommit(fs, option, fileSystem);
}
fs.Impl.AbortIfNeeded(rc);
return rc;
static Result RunCommit(FileSystemClient fs, CommitOption option, FileSystemAccessor fileSystem)
{
if ((option.Flags & (CommitOptionFlag.ClearRestoreFlag | CommitOptionFlag.SetRestoreFlag)) == 0)
{
return fileSystem.Commit();
}
if (option.Flags != CommitOptionFlag.ClearRestoreFlag &&
option.Flags != CommitOptionFlag.SetRestoreFlag)
{
return ResultFs.InvalidCommitOption.Log();
}
Result rc = fileSystem.GetSaveDataAttribute(out SaveDataAttribute attribute);
if (rc.IsFailure()) return rc;
if (attribute.ProgramId == SaveData.InvalidProgramId)
attribute.ProgramId = SaveData.AutoResolveCallerProgramId;
var extraDataMask = new SaveDataExtraData();
extraDataMask.Flags = SaveDataFlags.Restore;
var extraData = new SaveDataExtraData();
extraDataMask.Flags = option.Flags == CommitOptionFlag.SetRestoreFlag
? SaveDataFlags.Restore
: SaveDataFlags.None;
return fs.Impl.WriteSaveDataFileSystemExtraData(SaveDataSpaceId.User, in attribute, in extraData,
in extraDataMask);
}
}
public static Result Commit(this FileSystemClient fs, U8Span mountName)
{
throw new NotImplementedException();
return CommitImpl(fs, mountName);
}
public static Result CommitSaveData(this FileSystemClient fs, U8Span mountName)
{
throw new NotImplementedException();
return CommitImpl(fs, mountName);
}
private static ReadOnlySpan<byte> LogPath => // ", path: ""
new[]
{
(byte)',', (byte)' ', (byte)'p', (byte)'a', (byte)'t', (byte)'h', (byte)':', (byte)' ',
(byte)'"'
};
private static ReadOnlySpan<byte> LogNewPath => // "", new_path: ""
new[]
{
(byte)'"', (byte)',', (byte)' ', (byte)'n', (byte)'e', (byte)'w', (byte)'_', (byte)'p',
(byte)'a', (byte)'t', (byte)'h', (byte)':', (byte)' ', (byte)'"'
};
private static ReadOnlySpan<byte> LogEntryType => // "", entry_type: "
new[]
{
(byte)'"', (byte)',', (byte)' ', (byte)'e', (byte)'n', (byte)'t', (byte)'r', (byte)'y',
(byte)'_', (byte)'t', (byte)'y', (byte)'p', (byte)'e', (byte)':', (byte)' '
};
private static ReadOnlySpan<byte> LogSize => // "", size: "
new[]
{
(byte)'"', (byte)',', (byte)' ', (byte)'s', (byte)'i', (byte)'z', (byte)'e', (byte)':',
(byte)' '
};
private static ReadOnlySpan<byte> LogOpenMode => // "", open_mode: 0x"
new[]
{
(byte)'"', (byte)',', (byte)' ', (byte)'o', (byte)'p', (byte)'e', (byte)'n', (byte)'_',
(byte)'m', (byte)'o', (byte)'d', (byte)'e', (byte)':', (byte)' ', (byte)'0', (byte)'x'
};
private static ReadOnlySpan<byte> LogName => // ", name: ""
new[]
{
(byte)',', (byte)' ', (byte)'n', (byte)'a', (byte)'m', (byte)'e', (byte)':', (byte)' ',
(byte)'"'
};
private static ReadOnlySpan<byte> LogCommitOption => // "", commit_option: 0x"
new[]
{
(byte)'"', (byte)',', (byte)' ', (byte)'c', (byte)'o', (byte)'m', (byte)'m', (byte)'i',
(byte)'t', (byte)'_', (byte)'o', (byte)'p', (byte)'t', (byte)'i', (byte)'o', (byte)'n',
(byte)':', (byte)' ', (byte)'0', (byte)'x'
};
}
}

View file

@ -0,0 +1,95 @@
using System;
using System.Runtime.CompilerServices;
using LibHac.Common;
using LibHac.Fs.Impl;
using LibHac.Os;
namespace LibHac.Fs.Fsa
{
public static class UserFileSystemPrivate
{
public static Result CreateFile(this FileSystemClient fs, U8Span path, long size, CreateFileOptions options)
{
Result rc;
U8Span subPath;
FileSystemAccessor fileSystem;
Span<byte> logBuffer = stackalloc byte[0x300];
if (fs.Impl.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fs.Impl.FindFileSystem(out fileSystem, out subPath, path);
Tick end = fs.Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
sb.Append(LogPath).Append(path).Append((byte)'"');
logBuffer = sb.Buffer;
fs.Impl.OutputAccessLogUnlessResultSuccess(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = fs.Impl.FindFileSystem(out fileSystem, out subPath, path);
}
fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc;
if (fs.Impl.IsEnabledAccessLog() && fileSystem.IsEnabledAccessLog())
{
Tick start = fs.Hos.Os.GetSystemTick();
rc = fileSystem.CreateFile(subPath, size, options);
Tick end = fs.Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
sb.Append(LogPath).Append(path).Append(LogSize).AppendFormat(size);
logBuffer = sb.Buffer;
fs.Impl.OutputAccessLog(rc, start, end, null, new U8Span(logBuffer));
}
else
{
rc = fileSystem.CreateFile(subPath, size, options);
}
fs.Impl.AbortIfNeeded(rc);
return rc;
}
public static Result GetTotalSpaceSize(this FileSystemClient fs, out long totalSpace, U8Span path)
{
Unsafe.SkipInit(out totalSpace);
Result rc = fs.Impl.FindFileSystem(out FileSystemAccessor fileSystem, out U8Span subPath, path);
fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc;
rc = fileSystem.GetFreeSpaceSize(out totalSpace, subPath);
fs.Impl.AbortIfNeeded(rc);
return rc;
}
public static Result SetConcatenationFileAttribute(this FileSystemClient fs, U8Span path)
{
Result rc = fs.Impl.FindFileSystem(out FileSystemAccessor fileSystem, out U8Span subPath, path);
fs.Impl.AbortIfNeeded(rc);
if (rc.IsFailure()) return rc;
rc = fileSystem.QueryEntry(Span<byte>.Empty, ReadOnlySpan<byte>.Empty, QueryId.MakeConcatFile, subPath);
fs.Impl.AbortIfNeeded(rc);
return rc;
}
private static ReadOnlySpan<byte> LogPath => // ", path: ""
new[]
{
(byte)',', (byte)' ', (byte)'p', (byte)'a', (byte)'t', (byte)'h', (byte)':', (byte)' ',
(byte)'"'
};
private static ReadOnlySpan<byte> LogSize => // "", size: "
new[]
{
(byte)'"', (byte)',', (byte)' ', (byte)'s', (byte)'i', (byte)'z', (byte)'e', (byte)':',
(byte)' '
};
}
}

View file

@ -1,5 +1,4 @@
using System;
using LibHac.Common;
using LibHac.Common;
using LibHac.Fs.Impl;
namespace LibHac.Fs.Fsa
@ -18,17 +17,17 @@ namespace LibHac.Fs.Fsa
{
public static Result Register(this FileSystemClientImpl fs, FileSystemAccessor fileSystem)
{
throw new NotImplementedException();
return fs.Globals.UserMountTable.MountTable.Mount(fileSystem);
}
public static Result Find(this FileSystemClientImpl fs, out FileSystemAccessor fileSystem, U8Span name)
{
throw new NotImplementedException();
return fs.Globals.UserMountTable.MountTable.Find(out fileSystem, name);
}
public static void Unregister(this FileSystemClientImpl fs, U8Span name)
{
throw new NotImplementedException();
fs.Globals.UserMountTable.MountTable.Unmount(name);
}
}
}

View file

@ -934,6 +934,8 @@ namespace LibHac.Fs
public static Result.Base UnexpectedErrorInHostFileGetSize => new Result.Base(ModuleFs, 5308);
/// <summary>Error code: 2002-5309; Inner value: 0x297a02</summary>
public static Result.Base UnknownHostFileSystemError => new Result.Base(ModuleFs, 5309);
/// <summary>Error code: 2002-5319; Inner value: 0x298e02</summary>
public static Result.Base UnexpectedInMountUtilityA => new Result.Base(ModuleFs, 5319);
/// <summary>Error code: 2002-5320; Inner value: 0x299002</summary>
public static Result.Base InvalidNcaMountPoint => new Result.Base(ModuleFs, 5320);

View file

@ -1,7 +1,11 @@
namespace LibHac.Fs
using LibHac.Ncm;
namespace LibHac.Fs
{
public static class SaveData
{
public const ulong SaveIndexerId = 0x8000000000000000;
public static ProgramId InvalidProgramId => ProgramId.InvalidId;
public static ProgramId AutoResolveCallerProgramId => new ProgramId(ulong.MaxValue - 1);
}
}

View file

@ -10,6 +10,55 @@ namespace LibHac.Fs.Shim
{
public static class SaveDataManagement
{
internal static Result ReadSaveDataFileSystemExtraData(this FileSystemClientImpl fs,
out SaveDataExtraData extraData, ulong saveDataId)
{
throw new NotImplementedException();
}
internal static Result ReadSaveDataFileSystemExtraData(this FileSystemClientImpl fs,
out SaveDataExtraData extraData, SaveDataSpaceId spaceId, ulong saveDataId)
{
throw new NotImplementedException();
}
internal static Result ReadSaveDataFileSystemExtraData(this FileSystemClientImpl fs,
out SaveDataExtraData extraData, SaveDataSpaceId spaceId, in SaveDataAttribute attribute)
{
throw new NotImplementedException();
}
internal static Result ReadSaveDataFileSystemExtraData(this FileSystemClientImpl fs,
out SaveDataExtraData extraData, SaveDataSpaceId spaceId, in SaveDataAttribute attribute,
in SaveDataExtraData extraDataMask)
{
throw new NotImplementedException();
}
internal static Result WriteSaveDataFileSystemExtraData(this FileSystemClientImpl fs, SaveDataSpaceId spaceId,
ulong saveDataId, in SaveDataExtraData extraData)
{
throw new NotImplementedException();
}
internal static Result WriteSaveDataFileSystemExtraData(this FileSystemClientImpl fs, SaveDataSpaceId spaceId,
ulong saveDataId, in SaveDataExtraData extraData, in SaveDataExtraData extraDataMask)
{
throw new NotImplementedException();
}
internal static Result WriteSaveDataFileSystemExtraData(this FileSystemClientImpl fs, SaveDataSpaceId spaceId,
in SaveDataAttribute attribute, in SaveDataExtraData extraData, in SaveDataExtraData extraDataMask)
{
throw new NotImplementedException();
}
internal static Result FindSaveDataWithFilter(this FileSystemClientImpl fs, out SaveDataInfo saveInfo,
SaveDataSpaceId spaceId, in SaveDataFilter filter)
{
throw new NotImplementedException();
}
public static Result CreateSaveData(this FileSystemClient fs, Ncm.ApplicationId applicationId, UserId userId,
ulong ownerId, long size, long journalSize, SaveDataFlags flags)
{
@ -42,39 +91,6 @@ namespace LibHac.Fs.Shim
$", applicationid: 0x{applicationId.Value:X}, userid: 0x{userId}, save_data_owner_id: 0x{ownerId:X}, save_data_size: {size}, save_data_journal_size: {journalSize}, save_data_flags: 0x{(int)flags:X8}");
}
public static Result CreateSaveData(this FileSystemClient fs, Ncm.ApplicationId applicationId, UserId userId,
ulong ownerId, long size, long journalSize, HashSalt hashSalt, SaveDataFlags flags)
{
return fs.RunOperationWithAccessLog(AccessLogTarget.System,
() =>
{
using ReferenceCountedDisposable<IFileSystemProxy> fsProxy = fs.Impl.GetFileSystemProxyServiceObject();
var attribute = new SaveDataAttribute(applicationId, SaveDataType.Account, userId, 0);
var createInfo = new SaveDataCreationInfo
{
Size = size,
JournalSize = journalSize,
BlockSize = 0x4000,
OwnerId = ownerId,
Flags = flags,
SpaceId = SaveDataSpaceId.User
};
var metaInfo = new SaveDataMetaInfo
{
Type = SaveDataMetaType.Thumbnail,
Size = 0x40060
};
return fsProxy.Target.CreateSaveDataFileSystemWithHashSalt(in attribute, in createInfo, in metaInfo,
in hashSalt);
},
() =>
$", applicationid: 0x{applicationId.Value:X}, userid: 0x{userId}, save_data_owner_id: 0x{ownerId:X}, save_data_size: {size}, save_data_journal_size: {journalSize}, save_data_flags: 0x{(int)flags:X8}");
}
public static Result CreateBcatSaveData(this FileSystemClient fs, Ncm.ApplicationId applicationId, long size)
{
return fs.RunOperationWithAccessLog(AccessLogTarget.System,
@ -128,6 +144,159 @@ namespace LibHac.Fs.Shim
() => $", applicationid: 0x{applicationId.Value:X}, save_data_owner_id: 0x{ownerId:X}, save_data_size: {size}, save_data_journal_size: {journalSize}, save_data_flags: 0x{(int)flags:X8}");
}
public static Result DeleteSaveData(this FileSystemClient fs, ulong saveDataId)
{
return fs.RunOperationWithAccessLog(AccessLogTarget.System,
() =>
{
using ReferenceCountedDisposable<IFileSystemProxy> fsProxy = fs.Impl.GetFileSystemProxyServiceObject();
return fsProxy.Target.DeleteSaveDataFileSystem(saveDataId);
},
() => $", savedataid: 0x{saveDataId:X}");
}
public static Result DeleteSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId)
{
return fs.RunOperationWithAccessLog(AccessLogTarget.System,
() =>
{
using ReferenceCountedDisposable<IFileSystemProxy> fsProxy = fs.Impl.GetFileSystemProxyServiceObject();
return fsProxy.Target.DeleteSaveDataFileSystemBySaveDataSpaceId(spaceId, saveDataId);
},
() => $", savedataspaceid: {spaceId}, savedataid: 0x{saveDataId:X}");
}
public static Result OpenSaveDataIterator(this FileSystemClient fs, out SaveDataIterator iterator, SaveDataSpaceId spaceId)
{
var tempIterator = new SaveDataIterator();
try
{
Result result = fs.RunOperationWithAccessLog(AccessLogTarget.System,
() =>
{
using ReferenceCountedDisposable<IFileSystemProxy> fsProxy = fs.Impl.GetFileSystemProxyServiceObject();
Result rc = fsProxy.Target.OpenSaveDataInfoReaderBySaveDataSpaceId(
out ReferenceCountedDisposable<ISaveDataInfoReader> reader, spaceId);
if (rc.IsFailure()) return rc;
tempIterator = new SaveDataIterator(fs, reader);
return Result.Success;
},
() => $", savedataspaceid: {spaceId}");
iterator = result.IsSuccess() ? tempIterator : default;
tempIterator = default;
return result;
}
finally
{
tempIterator.Dispose();
}
}
public static Result OpenSaveDataIterator(this FileSystemClient fs, out SaveDataIterator iterator, SaveDataSpaceId spaceId, in SaveDataFilter filter)
{
ReferenceCountedDisposable<ISaveDataInfoReader> reader = null;
var tempIterator = new SaveDataIterator();
SaveDataFilter tempFilter = filter;
try
{
Result result = fs.RunOperationWithAccessLog(AccessLogTarget.System,
() =>
{
using ReferenceCountedDisposable<IFileSystemProxy> fsProxy = fs.Impl.GetFileSystemProxyServiceObject();
Result rc = fsProxy.Target.OpenSaveDataInfoReaderWithFilter(out reader, spaceId, in tempFilter);
if (rc.IsFailure()) return rc;
tempIterator = new SaveDataIterator(fs, reader);
return Result.Success;
},
() => $", savedataspaceid: {spaceId}");
iterator = result.IsSuccess() ? tempIterator : default;
return result;
}
finally
{
reader?.Dispose();
}
}
public static Result FindSaveDataWithFilter(this FileSystemClient fs, out SaveDataInfo info, SaveDataSpaceId spaceId,
in SaveDataFilter filter)
{
info = default;
SaveDataFilter tempFilter = filter;
var tempInfo = new SaveDataInfo();
Result result = fs.RunOperationWithAccessLog(AccessLogTarget.System,
() =>
{
using ReferenceCountedDisposable<IFileSystemProxy> fsProxy = fs.Impl.GetFileSystemProxyServiceObject();
tempInfo = new SaveDataInfo();
Result rc = fsProxy.Target.FindSaveDataWithFilter(out long count,
OutBuffer.FromStruct(ref tempInfo), spaceId, in tempFilter);
if (rc.IsFailure()) return rc;
if (count == 0)
return ResultFs.TargetNotFound.Log();
return Result.Success;
},
() => $", savedataspaceid: {spaceId}");
if (result.IsSuccess())
{
info = tempInfo;
}
return result;
}
public static Result CreateSaveData(this FileSystemClient fs, Ncm.ApplicationId applicationId, UserId userId,
ulong ownerId, long size, long journalSize, HashSalt hashSalt, SaveDataFlags flags)
{
return fs.RunOperationWithAccessLog(AccessLogTarget.System,
() =>
{
using ReferenceCountedDisposable<IFileSystemProxy> fsProxy = fs.Impl.GetFileSystemProxyServiceObject();
var attribute = new SaveDataAttribute(applicationId, SaveDataType.Account, userId, 0);
var createInfo = new SaveDataCreationInfo
{
Size = size,
JournalSize = journalSize,
BlockSize = 0x4000,
OwnerId = ownerId,
Flags = flags,
SpaceId = SaveDataSpaceId.User
};
var metaInfo = new SaveDataMetaInfo
{
Type = SaveDataMetaType.Thumbnail,
Size = 0x40060
};
return fsProxy.Target.CreateSaveDataFileSystemWithHashSalt(in attribute, in createInfo, in metaInfo,
in hashSalt);
},
() =>
$", applicationid: 0x{applicationId.Value:X}, userid: 0x{userId}, save_data_owner_id: 0x{ownerId:X}, save_data_size: {size}, save_data_journal_size: {journalSize}, save_data_flags: 0x{(int)flags:X8}");
}
public static Result CreateTemporaryStorage(this FileSystemClient fs, Ncm.ApplicationId applicationId, ulong ownerId, long size, SaveDataFlags flags)
{
return fs.RunOperationWithAccessLog(AccessLogTarget.System,
@ -247,62 +416,6 @@ namespace LibHac.Fs.Shim
return CreateSystemSaveData(fs, spaceId, saveDataId, UserId.InvalidId, ownerId, size, journalSize, flags);
}
public static Result DeleteSaveData(this FileSystemClient fs, ulong saveDataId)
{
return fs.RunOperationWithAccessLog(AccessLogTarget.System,
() =>
{
using ReferenceCountedDisposable<IFileSystemProxy> fsProxy = fs.Impl.GetFileSystemProxyServiceObject();
return fsProxy.Target.DeleteSaveDataFileSystem(saveDataId);
},
() => $", savedataid: 0x{saveDataId:X}");
}
public static Result DeleteSaveData(this FileSystemClient fs, SaveDataSpaceId spaceId, ulong saveDataId)
{
return fs.RunOperationWithAccessLog(AccessLogTarget.System,
() =>
{
using ReferenceCountedDisposable<IFileSystemProxy> fsProxy = fs.Impl.GetFileSystemProxyServiceObject();
return fsProxy.Target.DeleteSaveDataFileSystemBySaveDataSpaceId(spaceId, saveDataId);
},
() => $", savedataspaceid: {spaceId}, savedataid: 0x{saveDataId:X}");
}
public static Result FindSaveDataWithFilter(this FileSystemClient fs, out SaveDataInfo info, SaveDataSpaceId spaceId,
ref SaveDataFilter filter)
{
info = default;
SaveDataFilter tempFilter = filter;
var tempInfo = new SaveDataInfo();
Result result = fs.RunOperationWithAccessLog(AccessLogTarget.System,
() =>
{
using ReferenceCountedDisposable<IFileSystemProxy> fsProxy = fs.Impl.GetFileSystemProxyServiceObject();
tempInfo = new SaveDataInfo();
Result rc = fsProxy.Target.FindSaveDataWithFilter(out long count,
OutBuffer.FromStruct(ref tempInfo), spaceId, in tempFilter);
if (rc.IsFailure()) return rc;
if (count == 0)
return ResultFs.TargetNotFound.Log();
return Result.Success;
},
() => $", savedataspaceid: {spaceId}");
if (result.IsSuccess())
{
info = tempInfo;
}
return result;
}
public static Result QuerySaveDataTotalSize(this FileSystemClient fs, out long totalSize, long size, long journalSize)
{
totalSize = default;
@ -326,70 +439,6 @@ namespace LibHac.Fs.Shim
return result;
}
public static Result OpenSaveDataIterator(this FileSystemClient fs, out SaveDataIterator iterator, SaveDataSpaceId spaceId)
{
var tempIterator = new SaveDataIterator();
try
{
Result result = fs.RunOperationWithAccessLog(AccessLogTarget.System,
() =>
{
using ReferenceCountedDisposable<IFileSystemProxy> fsProxy = fs.Impl.GetFileSystemProxyServiceObject();
Result rc = fsProxy.Target.OpenSaveDataInfoReaderBySaveDataSpaceId(
out ReferenceCountedDisposable<ISaveDataInfoReader> reader, spaceId);
if (rc.IsFailure()) return rc;
tempIterator = new SaveDataIterator(fs, reader);
return Result.Success;
},
() => $", savedataspaceid: {spaceId}");
iterator = result.IsSuccess() ? tempIterator : default;
tempIterator = default;
return result;
}
finally
{
tempIterator.Dispose();
}
}
public static Result OpenSaveDataIterator(this FileSystemClient fs, out SaveDataIterator iterator, SaveDataSpaceId spaceId, ref SaveDataFilter filter)
{
ReferenceCountedDisposable<ISaveDataInfoReader> reader = null;
var tempIterator = new SaveDataIterator();
SaveDataFilter tempFilter = filter;
try
{
Result result = fs.RunOperationWithAccessLog(AccessLogTarget.System,
() =>
{
using ReferenceCountedDisposable<IFileSystemProxy> fsProxy = fs.Impl.GetFileSystemProxyServiceObject();
Result rc = fsProxy.Target.OpenSaveDataInfoReaderWithFilter(out reader, spaceId, in tempFilter);
if (rc.IsFailure()) return rc;
tempIterator = new SaveDataIterator(fs, reader);
return Result.Success;
},
() => $", savedataspaceid: {spaceId}");
iterator = result.IsSuccess() ? tempIterator : default;
return result;
}
finally
{
reader?.Dispose();
}
}
public static void DisableAutoSaveDataCreation(this FileSystemClient fsClient)
{
using ReferenceCountedDisposable<IFileSystemProxy> fsProxy = fsClient.Impl.GetFileSystemProxyServiceObject();