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
|
||||
{
|
||||
public class DirectoryAccessor
|
||||
public class DirectoryAccessor : IDisposable
|
||||
{
|
||||
private IDirectory Directory { get; }
|
||||
private IDirectory Directory { get; set; }
|
||||
|
||||
public FileSystemAccessor Parent { get; }
|
||||
|
||||
|
@ -16,12 +17,30 @@ namespace LibHac.Fs.Accessors
|
|||
|
||||
public IEnumerable<DirectoryEntry> Read()
|
||||
{
|
||||
CheckIfDisposed();
|
||||
|
||||
return Directory.Read();
|
||||
}
|
||||
|
||||
public int GetEntryCount()
|
||||
{
|
||||
CheckIfDisposed();
|
||||
|
||||
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
|
||||
{
|
||||
public class FileAccessor
|
||||
public class FileAccessor : IFile
|
||||
{
|
||||
private IFile File { get; }
|
||||
private IFile File { get; set; }
|
||||
|
||||
public FileSystemAccessor Parent { get; }
|
||||
public WriteState WriteState { get; private set; }
|
||||
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)
|
||||
{
|
||||
File = baseFile;
|
||||
|
@ -19,11 +22,15 @@ namespace LibHac.Fs.Accessors
|
|||
|
||||
public int Read(Span<byte> destination, long offset, ReadOption options)
|
||||
{
|
||||
CheckIfDisposed();
|
||||
|
||||
return File.Read(destination, offset, options);
|
||||
}
|
||||
|
||||
public void Write(ReadOnlySpan<byte> source, long offset, WriteOption options)
|
||||
{
|
||||
CheckIfDisposed();
|
||||
|
||||
if (source.Length == 0)
|
||||
{
|
||||
WriteState = (WriteState)(~options & WriteOption.Flush);
|
||||
|
@ -38,6 +45,8 @@ namespace LibHac.Fs.Accessors
|
|||
|
||||
public void Flush()
|
||||
{
|
||||
CheckIfDisposed();
|
||||
|
||||
File.Flush();
|
||||
|
||||
WriteState = WriteState.None;
|
||||
|
@ -45,13 +54,40 @@ namespace LibHac.Fs.Accessors
|
|||
|
||||
public long GetSize()
|
||||
{
|
||||
CheckIfDisposed();
|
||||
|
||||
return File.GetSize();
|
||||
}
|
||||
|
||||
public void SetSize(long size)
|
||||
{
|
||||
CheckIfDisposed();
|
||||
|
||||
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
|
||||
|
|
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; }
|
||||
|
||||
private IFileSystem FileSystem { get; }
|
||||
private IAccessLogger Logger { get; }
|
||||
private ITimeSpanGenerator Timer { get; }
|
||||
|
||||
private HashSet<FileAccessor> OpenFiles { get; } = new HashSet<FileAccessor>();
|
||||
private HashSet<DirectoryAccessor> OpenDirectories { get; } = new HashSet<DirectoryAccessor>();
|
||||
|
@ -23,6 +25,14 @@ namespace LibHac.Fs.Accessors
|
|||
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)
|
||||
{
|
||||
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
|
||||
{
|
||||
|
@ -12,5 +16,183 @@ namespace LibHac.Fs
|
|||
{
|
||||
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);
|
||||
break;
|
||||
|
||||
case NormalizeState.Normal:
|
||||
sb.Append(c);
|
||||
break;
|
||||
|
||||
case NormalizeState.Delimiter when IsDirectorySeparator(c):
|
||||
isNormalized = false;
|
||||
break;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
public static Result ResultFsMountNameAlreadyExists => new Result(ModuleFs, 60);
|
||||
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 ResultFsMountNameNotFound => new Result(ModuleFs, 6905);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,14 @@ namespace LibHac
|
|||
|
||||
public bool IsSuccess() => Value == 0;
|
||||
public bool IsFailure() => Value != 0;
|
||||
|
||||
public void ThrowIfFailure()
|
||||
{
|
||||
if (IsFailure())
|
||||
{
|
||||
ThrowHelper.ThrowResult(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Results
|
||||
|
|
|
@ -19,10 +19,10 @@ namespace LibHac.Tests
|
|||
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[] {"/./aaa/bbb/ccc/.", "/aaa/bbb/ccc"},
|
||||
|
||||
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[] {"/a/../../../", "/"},
|
||||
new object[] {"//a/b//.//c/", "/a/b/c/"},
|
||||
|
@ -46,6 +46,7 @@ namespace LibHac.Tests
|
|||
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[] {"mount:/d./aa", "mount:/d./aa"},
|
||||
};
|
||||
|
||||
public static object[][] SubPathTestItems =
|
||||
|
|
Loading…
Reference in a new issue