Add basic FileSystemManager methods

This commit is contained in:
Alex Barney 2019-06-17 00:09:01 -05:00
parent a44bdf780e
commit 9964483e83
10 changed files with 307 additions and 8 deletions

View file

@ -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.");
}
} }
} }

View 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();
}
}
}

View file

@ -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

View 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();
}
}
}

View file

@ -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);

View file

@ -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;
}
} }
} }

View file

@ -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;

View file

@ -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);
} }

View file

@ -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

View file

@ -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 =