mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2024-11-14 10:49:41 +01:00
Add basic FileSystemManager methods
This commit is contained in:
parent
a44bdf780e
commit
9964483e83
10 changed files with 307 additions and 8 deletions
|
@ -1,10 +1,11 @@
|
||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace LibHac.Fs.Accessors
|
namespace LibHac.Fs.Accessors
|
||||||
{
|
{
|
||||||
public class DirectoryAccessor
|
public class DirectoryAccessor : IDisposable
|
||||||
{
|
{
|
||||||
private IDirectory Directory { get; }
|
private IDirectory Directory { get; set; }
|
||||||
|
|
||||||
public FileSystemAccessor Parent { get; }
|
public FileSystemAccessor Parent { get; }
|
||||||
|
|
||||||
|
@ -16,12 +17,30 @@ namespace LibHac.Fs.Accessors
|
||||||
|
|
||||||
public IEnumerable<DirectoryEntry> Read()
|
public IEnumerable<DirectoryEntry> Read()
|
||||||
{
|
{
|
||||||
|
CheckIfDisposed();
|
||||||
|
|
||||||
return Directory.Read();
|
return Directory.Read();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetEntryCount()
|
public int GetEntryCount()
|
||||||
{
|
{
|
||||||
|
CheckIfDisposed();
|
||||||
|
|
||||||
return Directory.GetEntryCount();
|
return Directory.GetEntryCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (Directory == null) return;
|
||||||
|
|
||||||
|
Parent?.NotifyCloseDirectory(this);
|
||||||
|
|
||||||
|
Directory = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CheckIfDisposed()
|
||||||
|
{
|
||||||
|
if (Directory == null) throw new ObjectDisposedException(null, "Cannot access closed directory.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
19
src/LibHac/Fs/Accessors/DirectoryHandle.cs
Normal file
19
src/LibHac/Fs/Accessors/DirectoryHandle.cs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace LibHac.Fs.Accessors
|
||||||
|
{
|
||||||
|
public struct DirectoryHandle : IDisposable
|
||||||
|
{
|
||||||
|
internal readonly DirectoryAccessor Directory;
|
||||||
|
|
||||||
|
internal DirectoryHandle(DirectoryAccessor directory)
|
||||||
|
{
|
||||||
|
Directory = directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Directory.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,14 +2,17 @@
|
||||||
|
|
||||||
namespace LibHac.Fs.Accessors
|
namespace LibHac.Fs.Accessors
|
||||||
{
|
{
|
||||||
public class FileAccessor
|
public class FileAccessor : IFile
|
||||||
{
|
{
|
||||||
private IFile File { get; }
|
private IFile File { get; set; }
|
||||||
|
|
||||||
public FileSystemAccessor Parent { get; }
|
public FileSystemAccessor Parent { get; }
|
||||||
public WriteState WriteState { get; private set; }
|
public WriteState WriteState { get; private set; }
|
||||||
public OpenMode OpenMode { get; }
|
public OpenMode OpenMode { get; }
|
||||||
|
|
||||||
|
// Todo: Consider removing Mode from interface because OpenMode is in FileAccessor
|
||||||
|
OpenMode IFile.Mode => OpenMode;
|
||||||
|
|
||||||
public FileAccessor(IFile baseFile, FileSystemAccessor parent, OpenMode mode)
|
public FileAccessor(IFile baseFile, FileSystemAccessor parent, OpenMode mode)
|
||||||
{
|
{
|
||||||
File = baseFile;
|
File = baseFile;
|
||||||
|
@ -19,11 +22,15 @@ namespace LibHac.Fs.Accessors
|
||||||
|
|
||||||
public int Read(Span<byte> destination, long offset, ReadOption options)
|
public int Read(Span<byte> destination, long offset, ReadOption options)
|
||||||
{
|
{
|
||||||
|
CheckIfDisposed();
|
||||||
|
|
||||||
return File.Read(destination, offset, options);
|
return File.Read(destination, offset, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Write(ReadOnlySpan<byte> source, long offset, WriteOption options)
|
public void Write(ReadOnlySpan<byte> source, long offset, WriteOption options)
|
||||||
{
|
{
|
||||||
|
CheckIfDisposed();
|
||||||
|
|
||||||
if (source.Length == 0)
|
if (source.Length == 0)
|
||||||
{
|
{
|
||||||
WriteState = (WriteState)(~options & WriteOption.Flush);
|
WriteState = (WriteState)(~options & WriteOption.Flush);
|
||||||
|
@ -38,6 +45,8 @@ namespace LibHac.Fs.Accessors
|
||||||
|
|
||||||
public void Flush()
|
public void Flush()
|
||||||
{
|
{
|
||||||
|
CheckIfDisposed();
|
||||||
|
|
||||||
File.Flush();
|
File.Flush();
|
||||||
|
|
||||||
WriteState = WriteState.None;
|
WriteState = WriteState.None;
|
||||||
|
@ -45,13 +54,40 @@ namespace LibHac.Fs.Accessors
|
||||||
|
|
||||||
public long GetSize()
|
public long GetSize()
|
||||||
{
|
{
|
||||||
|
CheckIfDisposed();
|
||||||
|
|
||||||
return File.GetSize();
|
return File.GetSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetSize(long size)
|
public void SetSize(long size)
|
||||||
{
|
{
|
||||||
|
CheckIfDisposed();
|
||||||
|
|
||||||
File.SetSize(size);
|
File.SetSize(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (File == null) return;
|
||||||
|
|
||||||
|
if (WriteState == WriteState.Unflushed)
|
||||||
|
{
|
||||||
|
// Original FS code would return an error:
|
||||||
|
// ThrowHelper.ThrowResult(ResultsFs.ResultFsWriteStateUnflushed);
|
||||||
|
|
||||||
|
Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
File.Dispose();
|
||||||
|
Parent?.NotifyCloseFile(this);
|
||||||
|
|
||||||
|
File = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CheckIfDisposed()
|
||||||
|
{
|
||||||
|
if (File == null) throw new ObjectDisposedException(null, "Cannot access closed file.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum WriteState
|
public enum WriteState
|
||||||
|
|
19
src/LibHac/Fs/Accessors/FileHandle.cs
Normal file
19
src/LibHac/Fs/Accessors/FileHandle.cs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace LibHac.Fs.Accessors
|
||||||
|
{
|
||||||
|
public struct FileHandle : IDisposable
|
||||||
|
{
|
||||||
|
internal readonly FileAccessor File;
|
||||||
|
|
||||||
|
internal FileHandle(FileAccessor file)
|
||||||
|
{
|
||||||
|
File = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
File.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,8 @@ namespace LibHac.Fs.Accessors
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
|
|
||||||
private IFileSystem FileSystem { get; }
|
private IFileSystem FileSystem { get; }
|
||||||
|
private IAccessLogger Logger { get; }
|
||||||
|
private ITimeSpanGenerator Timer { get; }
|
||||||
|
|
||||||
private HashSet<FileAccessor> OpenFiles { get; } = new HashSet<FileAccessor>();
|
private HashSet<FileAccessor> OpenFiles { get; } = new HashSet<FileAccessor>();
|
||||||
private HashSet<DirectoryAccessor> OpenDirectories { get; } = new HashSet<DirectoryAccessor>();
|
private HashSet<DirectoryAccessor> OpenDirectories { get; } = new HashSet<DirectoryAccessor>();
|
||||||
|
@ -23,6 +25,14 @@ namespace LibHac.Fs.Accessors
|
||||||
FileSystem = baseFileSystem;
|
FileSystem = baseFileSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FileSystemAccessor(string name, IFileSystem baseFileSystem, IAccessLogger logger, ITimeSpanGenerator timer)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
FileSystem = baseFileSystem;
|
||||||
|
Logger = logger;
|
||||||
|
Timer = timer;
|
||||||
|
}
|
||||||
|
|
||||||
public void CreateDirectory(string path)
|
public void CreateDirectory(string path)
|
||||||
{
|
{
|
||||||
FileSystem.CreateDirectory(path);
|
FileSystem.CreateDirectory(path);
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
using LibHac.Fs.Accessors;
|
using System;
|
||||||
|
using LibHac.Fs.Accessors;
|
||||||
|
|
||||||
|
using static LibHac.Results;
|
||||||
|
using static LibHac.Fs.ResultsFs;
|
||||||
|
|
||||||
namespace LibHac.Fs
|
namespace LibHac.Fs
|
||||||
{
|
{
|
||||||
|
@ -12,5 +16,183 @@ namespace LibHac.Fs
|
||||||
{
|
{
|
||||||
Os = os;
|
Os = os;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Register(string mountName, IFileSystem fileSystem)
|
||||||
|
{
|
||||||
|
var accessor = new FileSystemAccessor(mountName, fileSystem);
|
||||||
|
|
||||||
|
MountTable.Mount(accessor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateDirectory(string path)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateFile(string path, long size)
|
||||||
|
{
|
||||||
|
CreateFile(path, size, CreateFileOptions.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateFile(string path, long size, CreateFileOptions options)
|
||||||
|
{
|
||||||
|
FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
|
||||||
|
.ThrowIfFailure();
|
||||||
|
|
||||||
|
fileSystem.CreateFile(subPath.ToString(), size, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteDirectory(string path)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteDirectoryRecursively(string path)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CleanDirectoryRecursively(string path)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeleteFile(string path)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RenameDirectory(string oldPath, string newPath)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RenameFile(string oldPath, string newPath)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// How to report when entry isn't found?
|
||||||
|
public DirectoryEntryType GetEntryType(string path)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileHandle OpenFile(string path, OpenMode mode)
|
||||||
|
{
|
||||||
|
FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
|
||||||
|
.ThrowIfFailure();
|
||||||
|
|
||||||
|
FileAccessor file = fileSystem.OpenFile(subPath.ToString(), mode);
|
||||||
|
|
||||||
|
return new FileHandle(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DirectoryHandle OpenDirectory(string path, OpenDirectoryMode mode)
|
||||||
|
{
|
||||||
|
FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
|
||||||
|
.ThrowIfFailure();
|
||||||
|
|
||||||
|
DirectoryAccessor dir = fileSystem.OpenDirectory(subPath.ToString(), mode);
|
||||||
|
|
||||||
|
return new DirectoryHandle(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================
|
||||||
|
// Operations on file handles
|
||||||
|
// ==========================
|
||||||
|
public int ReadFile(FileHandle handle, Span<byte> destination, long offset)
|
||||||
|
{
|
||||||
|
return ReadFile(handle, destination, offset, ReadOption.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int ReadFile(FileHandle handle, Span<byte> destination, long offset, ReadOption option)
|
||||||
|
{
|
||||||
|
return handle.File.Read(destination, offset, option);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteFile(FileHandle handle, ReadOnlySpan<byte> source, long offset)
|
||||||
|
{
|
||||||
|
WriteFile(handle, source, offset, WriteOption.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteFile(FileHandle handle, ReadOnlySpan<byte> source, long offset, WriteOption option)
|
||||||
|
{
|
||||||
|
handle.File.Write(source, offset, option);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FlushFile(FileHandle handle)
|
||||||
|
{
|
||||||
|
handle.File.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long GetFileSize(FileHandle handle)
|
||||||
|
{
|
||||||
|
return handle.File.GetSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetFileSize(FileHandle handle, long size)
|
||||||
|
{
|
||||||
|
handle.File.SetSize(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OpenMode GetFileOpenMode(FileHandle handle)
|
||||||
|
{
|
||||||
|
return handle.File.OpenMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CloseFile(FileHandle handle)
|
||||||
|
{
|
||||||
|
handle.File.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Result FindFileSystem(ReadOnlySpan<char> path, out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
|
||||||
|
{
|
||||||
|
fileSystem = default;
|
||||||
|
|
||||||
|
Result result = GetMountName(path, out ReadOnlySpan<char> mountName, out subPath);
|
||||||
|
if (result.IsFailure()) return result;
|
||||||
|
|
||||||
|
result = MountTable.Find(mountName.ToString(), out fileSystem);
|
||||||
|
if (result.IsFailure()) return result;
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal Result GetMountName(ReadOnlySpan<char> path, out ReadOnlySpan<char> mountName, out ReadOnlySpan<char> subPath)
|
||||||
|
{
|
||||||
|
int mountLen = 0;
|
||||||
|
int maxMountLen = Math.Min(path.Length, PathTools.MountNameLength);
|
||||||
|
|
||||||
|
for (int i = 0; i < maxMountLen; i++)
|
||||||
|
{
|
||||||
|
if (path[i] == PathTools.MountSeparator)
|
||||||
|
{
|
||||||
|
mountLen = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mountLen == 0)
|
||||||
|
{
|
||||||
|
mountName = default;
|
||||||
|
subPath = default;
|
||||||
|
|
||||||
|
return ResultFsInvalidMountName;
|
||||||
|
}
|
||||||
|
|
||||||
|
mountName = path.Slice(0, mountLen);
|
||||||
|
|
||||||
|
if (mountLen + 2 < path.Length)
|
||||||
|
{
|
||||||
|
subPath = path.Slice(mountLen + 2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
subPath = default;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,6 +92,10 @@ namespace LibHac.Fs
|
||||||
sb.Append(c);
|
sb.Append(c);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case NormalizeState.Normal:
|
||||||
|
sb.Append(c);
|
||||||
|
break;
|
||||||
|
|
||||||
case NormalizeState.Delimiter when IsDirectorySeparator(c):
|
case NormalizeState.Delimiter when IsDirectorySeparator(c):
|
||||||
isNormalized = false;
|
isNormalized = false;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
public static Result ResultFsMountNameAlreadyExists => new Result(ModuleFs, 60);
|
public static Result ResultFsMountNameAlreadyExists => new Result(ModuleFs, 60);
|
||||||
public static Result ResultFsInvalidMountName => new Result(ModuleFs, 6065);
|
public static Result ResultFsInvalidMountName => new Result(ModuleFs, 6065);
|
||||||
|
public static Result ResultFsWriteStateUnflushed => new Result(ModuleFs, 6454);
|
||||||
public static Result ResultFsWritableFileOpen => new Result(ModuleFs, 6457);
|
public static Result ResultFsWritableFileOpen => new Result(ModuleFs, 6457);
|
||||||
public static Result ResultFsMountNameNotFound => new Result(ModuleFs, 6905);
|
public static Result ResultFsMountNameNotFound => new Result(ModuleFs, 6905);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,14 @@ namespace LibHac
|
||||||
|
|
||||||
public bool IsSuccess() => Value == 0;
|
public bool IsSuccess() => Value == 0;
|
||||||
public bool IsFailure() => Value != 0;
|
public bool IsFailure() => Value != 0;
|
||||||
|
|
||||||
|
public void ThrowIfFailure()
|
||||||
|
{
|
||||||
|
if (IsFailure())
|
||||||
|
{
|
||||||
|
ThrowHelper.ThrowResult(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Results
|
public static class Results
|
||||||
|
|
|
@ -19,10 +19,10 @@ namespace LibHac.Tests
|
||||||
new object[] {"/a/../../../a/b/c", "/a/b/c"},
|
new object[] {"/a/../../../a/b/c", "/a/b/c"},
|
||||||
new object[] {"//a/b//.//c", "/a/b/c"},
|
new object[] {"//a/b//.//c", "/a/b/c"},
|
||||||
new object[] {"/../a/b/c/.", "/a/b/c"},
|
new object[] {"/../a/b/c/.", "/a/b/c"},
|
||||||
new object[] {"/./a/b/c/.", "/a/b/c"},
|
new object[] {"/./aaa/bbb/ccc/.", "/aaa/bbb/ccc"},
|
||||||
|
|
||||||
new object[] {"/a/b/c/", "/a/b/c/"},
|
new object[] {"/a/b/c/", "/a/b/c/"},
|
||||||
new object[] {"/a/./b/../c/", "/a/c/"},
|
new object[] {"/aa/./bb/../cc/", "/aa/cc/"},
|
||||||
new object[] {"/./b/../c/", "/c/"},
|
new object[] {"/./b/../c/", "/c/"},
|
||||||
new object[] {"/a/../../../", "/"},
|
new object[] {"/a/../../../", "/"},
|
||||||
new object[] {"//a/b//.//c/", "/a/b/c/"},
|
new object[] {"//a/b//.//c/", "/a/b/c/"},
|
||||||
|
@ -46,6 +46,7 @@ namespace LibHac.Tests
|
||||||
new object[] {"abc:/", "abc:/"},
|
new object[] {"abc:/", "abc:/"},
|
||||||
new object[] {"abc://a/b//.//c", "abc:/a/b/c"},
|
new object[] {"abc://a/b//.//c", "abc:/a/b/c"},
|
||||||
new object[] {"abc:/././/././a/b//.//c", "abc:/a/b/c"},
|
new object[] {"abc:/././/././a/b//.//c", "abc:/a/b/c"},
|
||||||
|
new object[] {"mount:/d./aa", "mount:/d./aa"},
|
||||||
};
|
};
|
||||||
|
|
||||||
public static object[][] SubPathTestItems =
|
public static object[][] SubPathTestItems =
|
||||||
|
|
Loading…
Reference in a new issue