Update file system accessor classes

This commit is contained in:
Alex Barney 2021-07-20 22:56:50 -07:00
parent 77aef9166f
commit 4efe313281
18 changed files with 465 additions and 140 deletions

View File

@ -14,6 +14,10 @@ namespace LibHac.Common
private const int NullTerminatorLength = 1;
public Span<byte> Buffer { get; private set; }
/// <summary>
/// The current length of the string not including the null terminator.
/// </summary>
public int Length { get; private set; }
public bool Overflowed { get; private set; }
public bool AutoExpand { get; }

View File

@ -635,7 +635,7 @@ namespace LibHac.Fs.Impl
else
{
Result rc = fs.Fs.GetGlobalAccessLogMode(out g.GlobalAccessLogMode);
fs.LogErrorMessage(rc);
fs.LogResultErrorMessage(rc);
if (rc.IsFailure()) Abort.DoAbort(rc);
if (g.GlobalAccessLogMode != GlobalAccessLogMode.None)
@ -703,7 +703,7 @@ namespace LibHac.Fs.Impl
public static void EnableFileSystemAccessorAccessLog(this FileSystemClientImpl fs, U8Span mountName)
{
Result rc = fs.Find(out FileSystemAccessor fileSystem, mountName);
fs.LogErrorMessage(rc);
fs.LogResultErrorMessage(rc);
Abort.DoAbortUnless(rc.IsSuccess());
fileSystem.SetAccessLog(true);

View File

@ -60,7 +60,7 @@ namespace LibHac.Fs
return _writeBufferLength;
}
private readonly int GetLength()
public readonly int GetLength()
{
return StringUtils.GetLength(GetString());
}

View File

@ -18,7 +18,7 @@ namespace LibHac.Fs
{
if (IsValid)
{
Directory.GetParent().FsClient.CloseDirectory(this);
Directory.GetParent().Hos.Fs.CloseDirectory(this);
}
}
}

View File

@ -18,7 +18,7 @@ namespace LibHac.Fs
{
if (IsValid)
{
File.FsClient.CloseFile(this);
File.Hos.Fs.CloseFile(this);
}
}
}

View File

@ -18,8 +18,8 @@ namespace LibHac.Fs.Impl
public void Dispose()
{
_directory?.Dispose();
_directory = null;
IDirectory directory = Shared.Move(ref _directory);
directory?.Dispose();
_parentFileSystem.NotifyCloseDirectory(this);
}

View File

@ -17,6 +17,8 @@ namespace LibHac.Fs.Impl
internal class FileAccessor : IDisposable
{
private const string NeedFlushMessage = "Error: nn::fs::CloseFile() failed because the file was not flushed.\n";
private IFile _file;
private FileSystemAccessor _parentFileSystem;
private WriteState _writeState;
@ -26,15 +28,17 @@ namespace LibHac.Fs.Impl
// ReSharper disable once NotAccessedField.Local
private int _pathHashIndex;
internal FileSystemClient FsClient { get; }
internal HorizonClient Hos { get; }
public FileAccessor(FileSystemClient fsClient, ref IFile file, FileSystemAccessor parentFileSystem,
public FileAccessor(HorizonClient hosClient, ref IFile file, FileSystemAccessor parentFileSystem,
OpenMode mode)
{
FsClient = fsClient;
Hos = hosClient;
_file = Shared.Move(ref file);
_parentFileSystem = parentFileSystem;
_writeState = WriteState.None;
_lastResult = Result.Success;
_openMode = mode;
}
@ -42,13 +46,14 @@ namespace LibHac.Fs.Impl
{
if (_lastResult.IsSuccess() && _writeState == WriteState.NeedsFlush)
{
Abort.DoAbort(ResultFs.NeedFlush.Log(), "File needs flush before closing.");
Hos.Fs.Impl.LogErrorMessage(ResultFs.NeedFlush.Value, NeedFlushMessage);
Abort.DoAbort(ResultFs.NeedFlush.Value);
}
_parentFileSystem?.NotifyCloseFile(this);
_file?.Dispose();
_file = null;
IFile file = Shared.Move(ref _file);
file?.Dispose();
}
public OpenMode GetOpenMode() => _openMode;
@ -91,18 +96,18 @@ namespace LibHac.Fs.Impl
if (_lastResult.IsFailure())
{
if (FsClient.Impl.IsEnabledAccessLog() && FsClient.Impl.IsEnabledHandleAccessLog(handle))
if (Hos.Fs.Impl.IsEnabledAccessLog() && Hos.Fs.Impl.IsEnabledHandleAccessLog(handle))
{
Tick start = FsClient.Hos.Os.GetSystemTick();
Tick start = Hos.Os.GetSystemTick();
rc = _lastResult;
Tick end = FsClient.Hos.Os.GetSystemTick();
Tick end = Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
sb.Append(LogOffset).AppendFormat(offset)
.Append(LogSize).AppendFormat(destination.Length)
.Append(LogReadSize).AppendFormat(AccessLogImpl.DereferenceOutValue(in bytesRead, rc));
FsClient.Impl.OutputAccessLog(rc, start, end, handle, new U8Span(logBuffer),
Hos.Fs.Impl.OutputAccessLog(rc, start, end, handle, new U8Span(logBuffer),
nameof(UserFile.ReadFile));
}
@ -123,18 +128,18 @@ namespace LibHac.Fs.Impl
}
else
{
if (FsClient.Impl.IsEnabledAccessLog() && FsClient.Impl.IsEnabledHandleAccessLog(handle))
if (Hos.Fs.Impl.IsEnabledAccessLog() && Hos.Fs.Impl.IsEnabledHandleAccessLog(handle))
{
Tick start = FsClient.Hos.Os.GetSystemTick();
Tick start = Hos.Os.GetSystemTick();
rc = ReadWithoutCacheAccessLog(out bytesRead, offset, destination, in option);
Tick end = FsClient.Hos.Os.GetSystemTick();
Tick end = Hos.Os.GetSystemTick();
var sb = new U8StringBuilder(logBuffer, true);
sb.Append(LogOffset).AppendFormat(offset)
.Append(LogSize).AppendFormat(destination.Length)
.Append(LogReadSize).AppendFormat(AccessLogImpl.DereferenceOutValue(in bytesRead, rc));
FsClient.Impl.OutputAccessLog(rc, start, end, handle, new U8Span(logBuffer),
Hos.Fs.Impl.OutputAccessLog(rc, start, end, handle, new U8Span(logBuffer),
nameof(UserFile.ReadFile));
}
else

View File

@ -12,6 +12,12 @@ namespace LibHac.Fs.Impl
{
internal class FileSystemAccessor : IDisposable
{
private const string EmptyMountNameMessage = "Error: Mount failed because the mount name was empty.\n";
private const string TooLongMountNameMessage = "Error: Mount failed because the mount name was too long. The mount name was \"{0}\".\n";
private const string FileNotClosedMessage = "Error: Unmount failed because not all files were closed.\n";
private const string DirectoryNotClosedMessage = "Error: Unmount failed because not all directories were closed.\n";
private const string InvalidFsEntryObjectMessage = "Invalid file or directory object.";
private MountName _mountName;
private IFileSystem _fileSystem;
private LinkedList<FileAccessor> _openFiles;
@ -24,14 +30,16 @@ namespace LibHac.Fs.Impl
private bool _isPathCacheAttachable;
private bool _isPathCacheAttached;
private IMultiCommitTarget _multiCommitTarget;
private PathFlags _pathFlags;
private Optional<Ncm.DataId> _dataId;
internal FileSystemClient FsClient { get; }
internal HorizonClient Hos { get; }
public FileSystemAccessor(FileSystemClient fsClient, U8Span name, IMultiCommitTarget multiCommitTarget,
public FileSystemAccessor(HorizonClient hosClient, U8Span name, IMultiCommitTarget multiCommitTarget,
IFileSystem fileSystem, ICommonMountNameGenerator mountNameGenerator,
ISaveDataAttributeGetter saveAttributeGetter)
{
FsClient = fsClient;
Hos = hosClient;
_fileSystem = fileSystem;
_openFiles = new LinkedList<FileAccessor>();
@ -42,32 +50,43 @@ namespace LibHac.Fs.Impl
_multiCommitTarget = multiCommitTarget;
if (name.IsEmpty())
Abort.DoAbort(ResultFs.InvalidMountName.Log());
{
Hos.Fs.Impl.LogErrorMessage(ResultFs.InvalidMountName.Value, EmptyMountNameMessage);
Abort.DoAbort(ResultFs.InvalidMountName.Value);
}
if (StringUtils.GetLength(name, PathTool.MountNameLengthMax + 1) > PathTool.MountNameLengthMax)
Abort.DoAbort(ResultFs.InvalidMountName.Log());
int mountLength = StringUtils.Copy(_mountName.Name, name, PathTool.MountNameLengthMax + 1);
StringUtils.Copy(_mountName.Name.Slice(0, PathTool.MountNameLengthMax), name);
_mountName.Name[PathTool.MountNameLengthMax] = 0;
if (mountLength > PathTool.MountNameLengthMax)
{
Hos.Fs.Impl.LogErrorMessage(ResultFs.InvalidMountName.Value, TooLongMountNameMessage,
name.ToString());
Abort.DoAbort(ResultFs.InvalidMountName.Value);
}
if (StringUtils.Compare(_mountName.Name, CommonMountNames.HostRootFileSystemMountName) == 0)
{
_pathFlags.AllowWindowsPath();
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposing) return;
using (ScopedLock.Lock(ref _openListLock))
{
Abort.DoAbortUnless(_openFiles.Count == 0, ResultFs.FileNotClosed.Value,
"All files must be closed before unmounting.");
DumpUnclosedAccessorList(OpenMode.All, OpenDirectoryMode.All);
Abort.DoAbortUnless(_openDirectories.Count == 0, ResultFs.DirectoryNotClosed.Value,
"All directories must be closed before unmounting.");
if (_openFiles.Count != 0)
{
Hos.Fs.Impl.LogErrorMessage(ResultFs.FileNotClosed.Value, FileNotClosedMessage);
Abort.DoAbort(ResultFs.FileNotClosed.Value);
}
if (_openDirectories.Count != 0)
{
Hos.Fs.Impl.LogErrorMessage(ResultFs.DirectoryNotClosed.Value, DirectoryNotClosedMessage);
Abort.DoAbort(ResultFs.DirectoryNotClosed.Value);
}
if (_isPathCacheAttached)
{
@ -75,46 +94,27 @@ namespace LibHac.Fs.Impl
}
}
_saveDataAttributeGetter?.Dispose();
_saveDataAttributeGetter = null;
ISaveDataAttributeGetter saveDataAttributeGetter = Shared.Move(ref _saveDataAttributeGetter);
saveDataAttributeGetter?.Dispose();
_mountNameGenerator?.Dispose();
_mountNameGenerator = null;
ICommonMountNameGenerator mountNameGenerator = Shared.Move(ref _mountNameGenerator);
mountNameGenerator?.Dispose();
_fileSystem?.Dispose();
_fileSystem = null;
IFileSystem fileSystem = Shared.Move(ref _fileSystem);
fileSystem?.Dispose();
}
private static void Remove<T>(LinkedList<T> list, T item)
{
LinkedListNode<T> node = list.Find(item);
Abort.DoAbortUnless(node is not null, "Invalid file or directory object.");
list.Remove(node);
}
private static Result CheckPath(U8Span mountName, U8Span path)
{
int mountNameLength = StringUtils.GetLength(mountName, PathTool.MountNameLengthMax);
int pathLength = StringUtils.GetLength(path, PathTool.EntryNameLengthMax);
if (mountNameLength + 1 + pathLength > PathTool.EntryNameLengthMax)
return ResultFs.TooLongPath.Log();
return Result.Success;
}
private static bool HasOpenWriteModeFiles(LinkedList<FileAccessor> list)
{
for (LinkedListNode<FileAccessor> file = list.First; file is not null; file = file.Next)
if (node is not null)
{
if (file.Value.GetOpenMode().HasFlag(OpenMode.Write))
{
return true;
}
list.Remove(node);
return;
}
return false;
Assert.SdkAssert(false, InvalidFsEntryObjectMessage);
}
public void SetAccessLog(bool isEnabled) => _isAccessLogEnabled = isEnabled;
@ -131,9 +131,44 @@ namespace LibHac.Fs.Impl
_isPathCacheAttached = true;
}
public Optional<Ncm.DataId> GetDataId() => _dataId;
public void SetDataId(Ncm.DataId dataId) => _dataId.Set(dataId);
public Result SetUpPath(ref Path path, U8Span pathBuffer)
{
Result rc = PathFormatter.IsNormalized(out bool isNormalized, out _, pathBuffer, _pathFlags);
if (rc.IsSuccess() && isNormalized)
{
path.SetShallowBuffer(pathBuffer);
}
else
{
if (_pathFlags.IsWindowsPathAllowed())
{
rc = path.InitializeWithReplaceForwardSlashes(pathBuffer);
if (rc.IsFailure()) return rc;
}
else
{
rc = path.InitializeWithReplaceBackslash(pathBuffer);
if (rc.IsFailure()) return rc;
}
rc = path.Normalize(_pathFlags);
if (rc.IsFailure()) return rc;
}
if (path.GetLength() > PathTool.EntryNameLengthMax)
return ResultFs.TooLongPath.Log();
return Result.Success;
}
public Result CreateFile(U8Span path, long size, CreateFileOptions option)
{
Result rc = CheckPath(new U8Span(_mountName.Name), path);
var pathNormalized = new Path();
Result rc = SetUpPath(ref pathNormalized, path);
if (rc.IsFailure()) return rc;
if (_isPathCacheAttached)
@ -142,80 +177,87 @@ namespace LibHac.Fs.Impl
}
else
{
rc = _fileSystem.CreateFile(path, size, option);
rc = _fileSystem.CreateFile(in pathNormalized, size, option);
if (rc.IsFailure()) return rc;
}
pathNormalized.Dispose();
return Result.Success;
}
public Result DeleteFile(U8Span path)
{
Result rc = CheckPath(new U8Span(_mountName.Name), path);
var pathNormalized = new Path();
Result rc = SetUpPath(ref pathNormalized, path);
if (rc.IsFailure()) return rc;
return _fileSystem.DeleteFile(path);
rc = _fileSystem.DeleteFile(in pathNormalized);
if (rc.IsFailure()) return rc;
pathNormalized.Dispose();
return Result.Success;
}
public Result CreateDirectory(U8Span path)
{
Result rc = CheckPath(new U8Span(_mountName.Name), path);
var pathNormalized = new Path();
Result rc = SetUpPath(ref pathNormalized, path);
if (rc.IsFailure()) return rc;
return _fileSystem.CreateDirectory(path);
rc = _fileSystem.CreateDirectory(in pathNormalized);
if (rc.IsFailure()) return rc;
pathNormalized.Dispose();
return Result.Success;
}
public Result DeleteDirectory(U8Span path)
{
Result rc = CheckPath(new U8Span(_mountName.Name), path);
var pathNormalized = new Path();
Result rc = SetUpPath(ref pathNormalized, path);
if (rc.IsFailure()) return rc;
return _fileSystem.DeleteDirectory(path);
rc = _fileSystem.CreateDirectory(in pathNormalized);
if (rc.IsFailure()) return rc;
pathNormalized.Dispose();
return Result.Success;
}
public Result DeleteDirectoryRecursively(U8Span path)
{
Result rc = CheckPath(new U8Span(_mountName.Name), path);
var pathNormalized = new Path();
Result rc = SetUpPath(ref pathNormalized, path);
if (rc.IsFailure()) return rc;
return _fileSystem.DeleteDirectoryRecursively(path);
rc = _fileSystem.DeleteDirectoryRecursively(in pathNormalized);
if (rc.IsFailure()) return rc;
pathNormalized.Dispose();
return Result.Success;
}
public Result CleanDirectoryRecursively(U8Span path)
{
Result rc = CheckPath(new U8Span(_mountName.Name), path);
var pathNormalized = new Path();
Result rc = SetUpPath(ref pathNormalized, path);
if (rc.IsFailure()) return rc;
return _fileSystem.CleanDirectoryRecursively(path);
}
public Result RenameFile(U8Span oldPath, U8Span newPath)
{
Result rc = CheckPath(new U8Span(_mountName.Name), oldPath);
rc = _fileSystem.CleanDirectoryRecursively(in pathNormalized);
if (rc.IsFailure()) return rc;
rc = CheckPath(new U8Span(_mountName.Name), newPath);
if (rc.IsFailure()) return rc;
if (_isPathCacheAttached)
{
throw new NotImplementedException();
}
else
{
rc = _fileSystem.RenameFile(oldPath, newPath);
if (rc.IsFailure()) return rc;
}
pathNormalized.Dispose();
return Result.Success;
}
public Result RenameDirectory(U8Span oldPath, U8Span newPath)
public Result RenameFile(U8Span currentPath, U8Span newPath)
{
Result rc = CheckPath(new U8Span(_mountName.Name), oldPath);
var currentPathNormalized = new Path();
Result rc = SetUpPath(ref currentPathNormalized, currentPath);
if (rc.IsFailure()) return rc;
rc = CheckPath(new U8Span(_mountName.Name), newPath);
var newPathNormalized = new Path();
rc = SetUpPath(ref newPathNormalized, newPath);
if (rc.IsFailure()) return rc;
if (_isPathCacheAttached)
@ -224,10 +266,37 @@ namespace LibHac.Fs.Impl
}
else
{
rc = _fileSystem.RenameDirectory(oldPath, newPath);
rc = _fileSystem.RenameFile(in currentPathNormalized, in newPathNormalized);
if (rc.IsFailure()) return rc;
}
currentPathNormalized.Dispose();
newPathNormalized.Dispose();
return Result.Success;
}
public Result RenameDirectory(U8Span currentPath, U8Span newPath)
{
var currentPathNormalized = new Path();
Result rc = SetUpPath(ref currentPathNormalized, currentPath);
if (rc.IsFailure()) return rc;
var newPathNormalized = new Path();
rc = SetUpPath(ref newPathNormalized, newPath);
if (rc.IsFailure()) return rc;
if (_isPathCacheAttached)
{
throw new NotImplementedException();
}
else
{
rc = _fileSystem.RenameDirectory(in currentPathNormalized, in newPathNormalized);
if (rc.IsFailure()) return rc;
}
currentPathNormalized.Dispose();
newPathNormalized.Dispose();
return Result.Success;
}
@ -235,46 +304,62 @@ namespace LibHac.Fs.Impl
{
UnsafeHelpers.SkipParamInit(out entryType);
Result rc = CheckPath(new U8Span(_mountName.Name), path);
var pathNormalized = new Path();
Result rc = SetUpPath(ref pathNormalized, path);
if (rc.IsFailure()) return rc;
return _fileSystem.GetEntryType(out entryType, path);
rc = _fileSystem.GetEntryType(out entryType, in pathNormalized);
if (rc.IsFailure()) return rc;
pathNormalized.Dispose();
return Result.Success;
}
public Result GetFreeSpaceSize(out long freeSpace, U8Span path)
{
UnsafeHelpers.SkipParamInit(out freeSpace);
Result rc = CheckPath(new U8Span(_mountName.Name), path);
var pathNormalized = new Path();
Result rc = SetUpPath(ref pathNormalized, path);
if (rc.IsFailure()) return rc;
return _fileSystem.GetFreeSpaceSize(out freeSpace, path);
rc = _fileSystem.GetFreeSpaceSize(out freeSpace, in pathNormalized);
if (rc.IsFailure()) return rc;
pathNormalized.Dispose();
return Result.Success;
}
public Result GetTotalSpaceSize(out long totalSpace, U8Span path)
{
UnsafeHelpers.SkipParamInit(out totalSpace);
Result rc = CheckPath(new U8Span(_mountName.Name), path);
var pathNormalized = new Path();
Result rc = SetUpPath(ref pathNormalized, path);
if (rc.IsFailure()) return rc;
return _fileSystem.GetTotalSpaceSize(out totalSpace, path);
rc = _fileSystem.GetTotalSpaceSize(out totalSpace, in pathNormalized);
if (rc.IsFailure()) return rc;
pathNormalized.Dispose();
return Result.Success;
}
public Result OpenFile(out FileAccessor file, U8Span path, OpenMode mode)
{
UnsafeHelpers.SkipParamInit(out file);
Result rc = CheckPath(new U8Span(_mountName.Name), path);
var pathNormalized = new Path();
Result rc = SetUpPath(ref pathNormalized, path);
if (rc.IsFailure()) return rc;
IFile iFile = null;
try
{
rc = _fileSystem.OpenFile(out iFile, path, mode);
rc = _fileSystem.OpenFile(out iFile, in pathNormalized, mode);
if (rc.IsFailure()) return rc;
var fileAccessor = new FileAccessor(FsClient, ref iFile, this, mode);
var fileAccessor = new FileAccessor(Hos, ref iFile, this, mode);
using (ScopedLock.Lock(ref _openListLock))
{
@ -299,6 +384,7 @@ namespace LibHac.Fs.Impl
finally
{
iFile?.Dispose();
pathNormalized.Dispose();
}
}
@ -306,13 +392,14 @@ namespace LibHac.Fs.Impl
{
UnsafeHelpers.SkipParamInit(out directory);
Result rc = CheckPath(new U8Span(_mountName.Name), path);
var pathNormalized = new Path();
Result rc = SetUpPath(ref pathNormalized, path);
if (rc.IsFailure()) return rc;
IDirectory iDirectory = null;
try
{
rc = _fileSystem.OpenDirectory(out iDirectory, path, mode);
rc = _fileSystem.OpenDirectory(out iDirectory, in pathNormalized, mode);
if (rc.IsFailure()) return rc;
var directoryAccessor = new DirectoryAccessor(ref iDirectory, this);
@ -328,11 +415,25 @@ namespace LibHac.Fs.Impl
finally
{
iDirectory?.Dispose();
pathNormalized.Dispose();
}
}
public Result Commit()
{
static bool HasOpenWriteModeFiles(LinkedList<FileAccessor> list)
{
for (LinkedListNode<FileAccessor> file = list.First; file is not null; file = file.Next)
{
if (file.Value.GetOpenMode().HasFlag(OpenMode.Write))
{
return true;
}
}
return false;
}
using (ScopedLock.Lock(ref _openListLock))
{
DumpUnclosedAccessorList(OpenMode.Write, 0);
@ -346,12 +447,30 @@ namespace LibHac.Fs.Impl
public Result GetFileTimeStampRaw(out FileTimeStampRaw timeStamp, U8Span path)
{
return _fileSystem.GetFileTimeStampRaw(out timeStamp, path);
UnsafeHelpers.SkipParamInit(out timeStamp);
var pathNormalized = new Path();
Result rc = SetUpPath(ref pathNormalized, path);
if (rc.IsFailure()) return rc;
rc = _fileSystem.GetFileTimeStampRaw(out timeStamp, in pathNormalized);
if (rc.IsFailure()) return rc;
pathNormalized.Dispose();
return Result.Success;
}
public Result QueryEntry(Span<byte> outBuffer, ReadOnlySpan<byte> inBuffer, QueryId queryId, U8Span path)
{
return _fileSystem.QueryEntry(outBuffer, inBuffer, queryId, path);
var pathNormalized = new Path();
Result rc = SetUpPath(ref pathNormalized, path);
if (rc.IsFailure()) return rc;
rc = _fileSystem.QueryEntry(outBuffer, inBuffer, queryId, in pathNormalized);
if (rc.IsFailure()) return rc;
pathNormalized.Dispose();
return Result.Success;
}
public U8Span GetName()
@ -374,7 +493,10 @@ namespace LibHac.Fs.Impl
if (_saveDataAttributeGetter is null)
return ResultFs.PreconditionViolation.Log();
return _saveDataAttributeGetter.GetSaveDataAttribute(out attribute);
Result rc = _saveDataAttributeGetter.GetSaveDataAttribute(out attribute);
if (rc.IsFailure()) return rc;
return Result.Success;
}
public ReferenceCountedDisposable<IFileSystemSf> GetMultiCommitTarget()
@ -387,21 +509,203 @@ namespace LibHac.Fs.Impl
cacheAccessor.Purge(_fileSystem);
}
public void NotifyCloseFile(FileAccessor file)
internal void NotifyCloseFile(FileAccessor file)
{
using ScopedLock<SdkMutexType> lk = ScopedLock.Lock(ref _openListLock);
Remove(_openFiles, file);
}
public void NotifyCloseDirectory(DirectoryAccessor directory)
internal void NotifyCloseDirectory(DirectoryAccessor directory)
{
using ScopedLock<SdkMutexType> lk = ScopedLock.Lock(ref _openListLock);
Remove(_openDirectories, directory);
}
private static ReadOnlySpan<byte> LogFsModuleName => new[] { (byte)'$', (byte)'f', (byte)'s' }; // "$fs"
private static ReadOnlySpan<byte> LogFsErrorInfo => // "------ FS ERROR INFORMATION ------\n"
new[]
{
(byte)'-', (byte)'-', (byte)'-', (byte)'-', (byte)'-', (byte)'-', (byte)' ', (byte)'F',
(byte)'S', (byte)' ', (byte)'E', (byte)'R', (byte)'R', (byte)'O', (byte)'R', (byte)' ',
(byte)'I', (byte)'N', (byte)'F', (byte)'O', (byte)'R', (byte)'M', (byte)'A', (byte)'T',
(byte)'I', (byte)'O', (byte)'N', (byte)' ', (byte)'-', (byte)'-', (byte)'-', (byte)'-',
(byte)'-', (byte)'-', (byte)'\\', (byte)'n'
};
private static ReadOnlySpan<byte> LogFileNotClosed => // "Error: File not closed"
new[]
{
(byte)'E', (byte)'r', (byte)'r', (byte)'o', (byte)'r', (byte)':', (byte)' ', (byte)'F',
(byte)'i', (byte)'l', (byte)'e', (byte)' ', (byte)'n', (byte)'o', (byte)'t', (byte)' ',
(byte)'c', (byte)'l', (byte)'o', (byte)'s', (byte)'e', (byte)'d'
};
private static ReadOnlySpan<byte> LogDirectoryNotClosed => // "Error: Directory not closed"
new[]
{
(byte)'E', (byte)'r', (byte)'r', (byte)'o', (byte)'r', (byte)':', (byte)' ', (byte)'D',
(byte)'i', (byte)'r', (byte)'e', (byte)'c', (byte)'t', (byte)'o', (byte)'r', (byte)'y',
(byte)' ', (byte)'n', (byte)'o', (byte)'t', (byte)' ', (byte)'c', (byte)'l', (byte)'o',
(byte)'s', (byte)'e', (byte)'d'
};
private static ReadOnlySpan<byte> LogMountName => // " (mount_name: ""
new[]
{
(byte)' ', (byte)'(', (byte)'m', (byte)'o', (byte)'u', (byte)'n', (byte)'t', (byte)'_',
(byte)'n', (byte)'a', (byte)'m', (byte)'e', (byte)':', (byte)' ', (byte)'"'
};
private static ReadOnlySpan<byte> LogCount => // "", count: "
new[]
{
(byte)'"', (byte)',', (byte)' ', (byte)'c', (byte)'o', (byte)'u', (byte)'n', (byte)'t',
(byte)':', (byte)' '
};
public static ReadOnlySpan<byte> LogLineEnd => new[] { (byte)')', (byte)'\\', (byte)'n' }; // ")\n"
public static ReadOnlySpan<byte> LogOrOperator => new[] { (byte)' ', (byte)'|', (byte)' ' }; // " | "
private static ReadOnlySpan<byte> LogOpenModeRead => // "OpenMode_Read"
new[]
{
(byte)'O', (byte)'p', (byte)'e', (byte)'n', (byte)'M', (byte)'o', (byte)'d', (byte)'e',
(byte)'_', (byte)'R', (byte)'e', (byte)'a', (byte)'d'
};
private static ReadOnlySpan<byte> LogOpenModeWrite => // "OpenMode_Write"
new[]
{
(byte)'O', (byte)'p', (byte)'e', (byte)'n', (byte)'M', (byte)'o', (byte)'d', (byte)'e',
(byte)'_', (byte)'W', (byte)'r', (byte)'i', (byte)'t', (byte)'e'
};
private static ReadOnlySpan<byte> LogOpenModeAppend => // "OpenMode_AllowAppend"
new[]
{
(byte)'O', (byte)'p', (byte)'e', (byte)'n', (byte)'M', (byte)'o', (byte)'d', (byte)'e',
(byte)'_', (byte)'A', (byte)'l', (byte)'l', (byte)'o', (byte)'w', (byte)'A', (byte)'p',
(byte)'p', (byte)'e', (byte)'n', (byte)'d'
};
private static ReadOnlySpan<byte> LogHandle => // " handle: 0x"
new[]
{
(byte)' ', (byte)' ', (byte)' ', (byte)' ', (byte)' ', (byte)'h', (byte)'a', (byte)'n',
(byte)'d', (byte)'l', (byte)'e', (byte)':', (byte)' ', (byte)'0', (byte)'x'
};
private static ReadOnlySpan<byte> LogOpenMode => // ", open_mode: "
new[]
{
(byte)',', (byte)' ', (byte)'o', (byte)'p', (byte)'e', (byte)'n', (byte)'_', (byte)'m',
(byte)'o', (byte)'d', (byte)'e', (byte)':', (byte)' '
};
private static ReadOnlySpan<byte> LogSize => // ", size: "
new[]
{
(byte)',', (byte)' ', (byte)'s', (byte)'i', (byte)'z', (byte)'e', (byte)':', (byte)' '
};
private void DumpUnclosedAccessorList(OpenMode fileOpenModeMask, OpenDirectoryMode directoryOpenModeMask)
{
// Todo: Implement
static int GetOpenFileCount(LinkedList<FileAccessor> list, OpenMode mask)
{
int count = 0;
for (LinkedListNode<FileAccessor> file = list.First; file is not null; file = file.Next)
{
if ((file.Value.GetOpenMode() & mask) != 0)
count++;
}
return count;
}
Span<byte> stringBuffer = stackalloc byte[0xA0];
Span<byte> openModeStringBuffer = stackalloc byte[0x40];
int openFileCount = GetOpenFileCount(_openFiles, fileOpenModeMask);
if (openFileCount > 0 || directoryOpenModeMask != 0 && _openDirectories.Count != 0)
{
Hos.Diag.Impl.LogImpl(LogFsModuleName, LogSeverity.Error, LogFsErrorInfo);
}
if (openFileCount > 0)
{
var sb = new U8StringBuilder(stringBuffer, true);
sb.Append(LogFileNotClosed).Append(LogMountName).Append(GetName()).Append(LogCount)
.AppendFormat(openFileCount).Append(LogLineEnd);
Hos.Diag.Impl.LogImpl(LogFsModuleName, LogSeverity.Error, sb.Buffer);
sb.Dispose();
for (LinkedListNode<FileAccessor> file = _openFiles.First; file is not null; file = file.Next)
{
OpenMode openMode = file.Value.GetOpenMode();
if ((openMode & fileOpenModeMask) == 0)
continue;
Result rc = file.Value.GetSize(out long fileSize);
if (rc.IsFailure())
fileSize = -1;
var openModeString = new U8StringBuilder(openModeStringBuffer);
ReadOnlySpan<byte> readModeString = openMode.HasFlag(OpenMode.Read) ? LogOpenModeRead : default;
openModeString.Append(readModeString);
Assert.SdkAssert(!openModeString.Overflowed);
if (openMode.HasFlag(OpenMode.Write))
{
if (openModeString.Length > 0)
sb.Append(LogOrOperator);
openModeString.Append(LogOpenModeWrite);
Assert.SdkAssert(!openModeString.Overflowed);
}
if (openMode.HasFlag(OpenMode.AllowAppend))
{
if (openModeString.Length > 0)
sb.Append(LogOrOperator);
openModeString.Append(LogOpenModeAppend);
Assert.SdkAssert(!openModeString.Overflowed);
}
var fileInfoString = new U8StringBuilder(stringBuffer, true);
fileInfoString.Append(LogHandle).AppendFormat(file.Value.GetHashCode(), 'x', 16).Append(LogOpenMode)
.Append(openModeString.Buffer).Append(LogSize).AppendFormat(fileSize).Append((byte) '\n');
Hos.Diag.Impl.LogImpl(LogFsModuleName, LogSeverity.Error, fileInfoString.Buffer);
fileInfoString.Dispose();
}
}
if (directoryOpenModeMask != 0 && _openDirectories.Count != 0)
{
var sb = new U8StringBuilder(stringBuffer, true);
sb.Append(LogDirectoryNotClosed).Append(LogMountName).Append(GetName()).Append(LogCount)
.AppendFormat(_openDirectories.Count).Append(LogLineEnd);
Hos.Diag.Impl.LogImpl(LogFsModuleName, LogSeverity.Error, sb.Buffer);
sb.Dispose();
for (LinkedListNode<DirectoryAccessor> dir = _openDirectories.First; dir is not null; dir = dir.Next)
{
var dirInfoString = new U8StringBuilder(stringBuffer, true);
dirInfoString.Append(LogHandle).AppendFormat(dir.Value.GetHashCode(), 'x', 16).Append((byte)'\n');
Hos.Diag.Impl.LogImpl(LogFsModuleName, LogSeverity.Error, dirInfoString.Buffer);
dirInfoString.Dispose();
}
}
}
}
}

View File

@ -192,7 +192,7 @@ namespace LibHac.Fs.Fsa
{
rc = fs.Impl.Unmount(mountName);
}
fs.Impl.LogErrorMessage(rc);
fs.Impl.LogResultErrorMessage(rc);
Abort.DoAbortUnless(rc.IsSuccess());
}
@ -218,7 +218,7 @@ namespace LibHac.Fs.Fsa
{
rc = fs.Impl.IsMounted(out isMounted, mountName);
}
fs.Impl.LogErrorMessage(rc);
fs.Impl.LogResultErrorMessage(rc);
Abort.DoAbortUnless(rc.IsSuccess());
return isMounted;

View File

@ -18,7 +18,7 @@ namespace LibHac.Fs.Fsa
{
public static Result Register(this FileSystemClient fs, U8Span name, IFileSystem fileSystem)
{
var accessor = new FileSystemAccessor(fs, name, null, fileSystem, null, null);
var accessor = new FileSystemAccessor(fs.Hos, name, null, fileSystem, null, null);
fs.Impl.Register(accessor);
return Result.Success;
@ -27,7 +27,7 @@ namespace LibHac.Fs.Fsa
public static Result Register(this FileSystemClient fs, U8Span name, IFileSystem fileSystem,
ICommonMountNameGenerator mountNameGenerator)
{
var accessor = new FileSystemAccessor(fs, name, null, fileSystem, mountNameGenerator, null);
var accessor = new FileSystemAccessor(fs.Hos, name, null, fileSystem, mountNameGenerator, null);
fs.Impl.Register(accessor);
return Result.Success;
@ -44,7 +44,7 @@ namespace LibHac.Fs.Fsa
IFileSystem fileSystem, ICommonMountNameGenerator mountNameGenerator,
ISaveDataAttributeGetter saveAttributeGetter, bool useDataCache, bool usePathCache)
{
var accessor = new FileSystemAccessor(fs, name, multiCommitTarget, fileSystem, mountNameGenerator,
var accessor = new FileSystemAccessor(fs.Hos, name, multiCommitTarget, fileSystem, mountNameGenerator,
saveAttributeGetter);
accessor.SetFileDataCacheAttachable(useDataCache);

View File

@ -530,7 +530,7 @@ namespace LibHac.Fs.Fsa
public static Result OpenFile(this FileSystemClient fs, out FileHandle handle, IFile file, OpenMode mode)
{
var accessor = new FileAccessor(fs, ref file, null, mode);
var accessor = new FileAccessor(fs.Hos, ref file, null, mode);
handle = new FileHandle(accessor);
return Result.Success;

View File

@ -42,8 +42,18 @@ namespace LibHac.Fs
}
}
public static void LogErrorMessage(this FileSystemClientImpl fs, Result result,
[CallerMemberName] string functionName = "")
public static void LogErrorMessage(this FileSystemClientImpl fs, Result result, string message)
{
// Todo
}
public static void LogErrorMessage(this FileSystemClientImpl fs, Result result, string format, object arg0)
{
// Todo
}
public static void LogErrorMessage(this FileSystemClientImpl fs, Result result, string format,
params object[] args)
{
// Todo
}

View File

@ -163,7 +163,7 @@ namespace LibHac.Fs.Shim
int pathLen = StringUtils.GetLength(rootPath, PathTools.MaxPathLength + 1);
if (pathLen > PathTools.MaxPathLength)
{
fs.Impl.LogErrorMessage(ResultFs.TooLongPath.Value);
fs.Impl.LogResultErrorMessage(ResultFs.TooLongPath.Value);
return ResultFs.TooLongPath.Log();
}
@ -187,7 +187,7 @@ namespace LibHac.Fs.Shim
using ReferenceCountedDisposable<IFileSystemProxy> fsProxy = fs.Impl.GetFileSystemProxyServiceObject();
rc = fsProxy.Target.SetBisRootForHost(partitionId, in sfPath);
fs.Impl.LogErrorMessage(rc);
fs.Impl.LogResultErrorMessage(rc);
return rc;
}

View File

@ -157,11 +157,11 @@ namespace LibHac.Fs.Shim
try
{
Result rc = fsProxy.Target.OpenDeviceOperator(out deviceOperator);
fs.Impl.LogErrorMessage(rc);
fs.Impl.LogResultErrorMessage(rc);
Abort.DoAbortUnless(rc.IsSuccess());
rc = deviceOperator.Target.IsGameCardInserted(out bool isInserted);
fs.Impl.LogErrorMessage(rc);
fs.Impl.LogResultErrorMessage(rc);
Abort.DoAbortUnless(rc.IsSuccess());
return isInserted;

View File

@ -508,7 +508,7 @@ namespace LibHac.Fs.Shim
{
rc = fs.Impl.Unmount(mountName);
}
fs.Impl.LogErrorMessage(rc);
fs.Impl.LogResultErrorMessage(rc);
Abort.DoAbortUnless(rc.IsSuccess());
}
}

View File

@ -1788,7 +1788,7 @@ namespace LibHac.Fs.Shim
Result rc = fsProxy.Target.DisableAutoSaveDataCreation();
fs.Impl.LogErrorMessage(rc);
fs.Impl.LogResultErrorMessage(rc);
Abort.DoAbortUnless(rc.IsSuccess());
}

View File

@ -136,11 +136,11 @@ namespace LibHac.Fs.Shim
try
{
Result rc = fsProxy.Target.OpenDeviceOperator(out deviceOperator);
fs.Impl.LogErrorMessage(rc);
fs.Impl.LogResultErrorMessage(rc);
Abort.DoAbortUnless(rc.IsSuccess());
rc = CheckIfInserted(fs, deviceOperator, out bool isInserted);
fs.Impl.LogErrorMessage(rc);
fs.Impl.LogResultErrorMessage(rc);
Abort.DoAbortUnless(rc.IsSuccess());
return isInserted;
@ -191,7 +191,7 @@ namespace LibHac.Fs.Shim
public static void SetSdCardAccessibility(this FileSystemClient fs, bool isAccessible)
{
Result rc = fs.Impl.SetSdCardAccessibility(isAccessible);
fs.Impl.LogErrorMessage(rc);
fs.Impl.LogResultErrorMessage(rc);
Abort.DoAbortUnless(rc.IsSuccess());
}
@ -200,7 +200,7 @@ namespace LibHac.Fs.Shim
using ReferenceCountedDisposable<IFileSystemProxy> fsProxy = fs.Impl.GetFileSystemProxyServiceObject();
Result rc = fsProxy.Target.IsSdCardAccessible(out bool isAccessible);
fs.Impl.LogErrorMessage(rc);
fs.Impl.LogResultErrorMessage(rc);
Abort.DoAbortUnless(rc.IsSuccess());
return isAccessible;

View File

@ -330,7 +330,9 @@ namespace hactoolnet
var sb = new StringBuilder();
sb.AppendLine();
save.GetFreeSpaceSize(out long freeSpace, "".ToU8String()).ThrowIfFailure();
var emptyPath = new LibHac.Fs.Path();
emptyPath.InitializeAsEmpty().ThrowIfFailure();
save.GetFreeSpaceSize(out long freeSpace, in emptyPath).ThrowIfFailure();
sb.AppendLine("Savefile:");
PrintItem(sb, colLen, "CMAC Key Used:", keySet.DeviceUniqueSaveMacKeys[0].DataRo.ToArray());