Add close and unmount methods. Add helpers for common fs tasks

This commit is contained in:
Alex Barney 2019-06-21 00:30:39 -05:00
parent 1859b4ce26
commit b51d4397e9
10 changed files with 200 additions and 36 deletions

View File

@ -15,7 +15,7 @@ namespace LibHac.Fs.Accessors
public void Dispose()
{
Directory.Dispose();
Directory.Parent.FsManager.CloseDirectory(this);
}
}
}

View File

@ -15,7 +15,7 @@ namespace LibHac.Fs.Accessors
public void Dispose()
{
File.Dispose();
File.Parent.FsManager.CloseFile(this);
}
}
}

View File

@ -11,6 +11,7 @@ namespace LibHac.Fs.Accessors
public string Name { get; }
private IFileSystem FileSystem { get; }
internal FileSystemManager FsManager { get; }
private HashSet<FileAccessor> OpenFiles { get; } = new HashSet<FileAccessor>();
private HashSet<DirectoryAccessor> OpenDirectories { get; } = new HashSet<DirectoryAccessor>();
@ -19,10 +20,11 @@ namespace LibHac.Fs.Accessors
internal bool IsAccessLogEnabled { get; set; }
public FileSystemAccessor(string name, IFileSystem baseFileSystem)
public FileSystemAccessor(string name, IFileSystem baseFileSystem, FileSystemManager fsManager)
{
Name = name;
FileSystem = baseFileSystem;
FsManager = fsManager;
}
public void CreateDirectory(string path)
@ -153,5 +155,12 @@ namespace LibHac.Fs.Accessors
OpenDirectories.Remove(directory);
}
}
internal void Close()
{
// Todo: Possibly check for open files and directories
// Nintendo checks for them in DumpUnclosedAccessorList in
// FileSystemAccessor's destructor, but doesn't do anything with it
}
}
}

View File

@ -3,7 +3,7 @@ using System.Runtime.CompilerServices;
namespace LibHac.Fs.Accessors
{
public interface IAccessLogger
public interface IAccessLog
{
void Log(TimeSpan startTime, TimeSpan endTime, int handleId, string message, [CallerMemberName] string caller = "");
}

View File

@ -45,11 +45,14 @@ namespace LibHac.Fs.Accessors
{
lock (_locker)
{
if (!Table.Remove(name))
if (!Table.TryGetValue(name, out FileSystemAccessor fsAccessor))
{
return ResultFsMountNameNotFound;
}
Table.Remove(name);
fsAccessor.Close();
return ResultSuccess;
}
}

View File

@ -3,10 +3,6 @@ using System.Buffers;
using System.Collections.Generic;
using System.IO;
#if !NETFRAMEWORK
using System.IO.Enumeration;
#endif
namespace LibHac.Fs
{
public static class FileSystemExtensions
@ -88,7 +84,7 @@ namespace LibHac.Fs
foreach (DirectoryEntry entry in directory.Read())
{
if (MatchesPattern(searchPattern, entry.Name, ignoreCase))
if (PathTools.MatchesPattern(searchPattern, entry.Name, ignoreCase))
{
yield return entry;
}
@ -156,17 +152,6 @@ namespace LibHac.Fs
return count;
}
public static bool MatchesPattern(string searchPattern, string name, bool ignoreCase)
{
#if NETFRAMEWORK
return Compatibility.FileSystemName.MatchesSimpleExpression(searchPattern.AsSpan(),
name.AsSpan(), ignoreCase);
#else
return FileSystemName.MatchesSimpleExpression(searchPattern.AsSpan(),
name.AsSpan(), ignoreCase);
#endif
}
public static NxFileAttributes ToNxAttributes(this FileAttributes attributes)
{
return (NxFileAttributes)(((int)attributes >> 4) & 3);

View File

@ -12,33 +12,57 @@ namespace LibHac.Fs
{
internal Horizon Os { get; }
internal ITimeSpanGenerator Time { get; }
internal IAccessLogger Logger { get; }
private IAccessLog AccessLog { get; set; }
internal MountTable MountTable { get; } = new MountTable();
private bool AccessLogEnabled { get; set; } = true;
private bool AccessLogEnabled { get; set; }
public FileSystemManager(Horizon os)
{
Os = os;
}
public FileSystemManager(Horizon os, IAccessLogger logger, ITimeSpanGenerator timer)
public FileSystemManager(Horizon os, ITimeSpanGenerator timer)
{
Os = os;
Logger = logger;
Time = timer;
}
public void Register(string mountName, IFileSystem fileSystem)
{
var accessor = new FileSystemAccessor(mountName, fileSystem);
var accessor = new FileSystemAccessor(mountName, fileSystem, this);
MountTable.Mount(accessor);
MountTable.Mount(accessor).ThrowIfFailure();
accessor.IsAccessLogEnabled = IsEnabledAccessLog();
}
public void Unmount(string mountName)
{
MountTable.Find(mountName, out FileSystemAccessor fileSystem).ThrowIfFailure();
if (IsEnabledAccessLog() && fileSystem.IsAccessLogEnabled)
{
TimeSpan startTime = Time.GetCurrent();
MountTable.Unmount(mountName);
TimeSpan endTime = Time.GetCurrent();
OutputAccessLog(startTime, endTime, $", name: \"{mountName}\"");
}
else
{
MountTable.Unmount(mountName);
}
}
public void SetAccessLog(bool isEnabled, IAccessLog accessLog = null)
{
AccessLogEnabled = isEnabled;
if (accessLog != null) AccessLog = accessLog;
}
public void CreateDirectory(string path)
{
FindFileSystem(path.AsSpan(), out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
@ -425,14 +449,14 @@ namespace LibHac.Fs
if (IsEnabledAccessLog() && IsEnabledHandleAccessLog(handle))
{
TimeSpan startTime = Time.GetCurrent();
handle.Dispose();
handle.File.Dispose();
TimeSpan endTime = Time.GetCurrent();
OutputAccessLog(startTime, endTime, handle, string.Empty);
}
else
{
handle.Dispose();
handle.File.Dispose();
}
}
@ -459,6 +483,22 @@ namespace LibHac.Fs
return handle.Directory.Read();
}
public void CloseDirectory(DirectoryHandle handle)
{
if (IsEnabledAccessLog() && IsEnabledHandleAccessLog(handle))
{
TimeSpan startTime = Time.GetCurrent();
handle.Directory.Dispose();
TimeSpan endTime = Time.GetCurrent();
OutputAccessLog(startTime, endTime, handle, string.Empty);
}
else
{
handle.Directory.Dispose();
}
}
internal Result FindFileSystem(ReadOnlySpan<char> path, out FileSystemAccessor fileSystem, out ReadOnlySpan<char> subPath)
{
fileSystem = default;
@ -510,7 +550,7 @@ namespace LibHac.Fs
internal bool IsEnabledAccessLog()
{
return AccessLogEnabled && Logger != null && Time != null;
return AccessLogEnabled && AccessLog != null && Time != null;
}
internal bool IsEnabledHandleAccessLog(FileHandle handle)
@ -525,17 +565,17 @@ namespace LibHac.Fs
internal void OutputAccessLog(TimeSpan startTime, TimeSpan endTime, string message, [CallerMemberName] string caller = "")
{
Logger.Log(startTime, endTime, 0, message, caller);
AccessLog.Log(startTime, endTime, 0, message, caller);
}
internal void OutputAccessLog(TimeSpan startTime, TimeSpan endTime, FileHandle handle, string message, [CallerMemberName] string caller = "")
{
Logger.Log(startTime, endTime, handle.GetId(), message, caller);
AccessLog.Log(startTime, endTime, handle.GetId(), message, caller);
}
internal void OutputAccessLog(TimeSpan startTime, TimeSpan endTime, DirectoryHandle handle, string message, [CallerMemberName] string caller = "")
{
Logger.Log(startTime, endTime, handle.GetId(), message, caller);
AccessLog.Log(startTime, endTime, handle.GetId(), message, caller);
}
}
}

View File

@ -0,0 +1,113 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using LibHac.Fs.Accessors;
namespace LibHac.Fs
{
public static class FileSystemManagerUtils
{
public static void CopyDirectory(this FileSystemManager fs, string sourcePath, string destPath,
CreateFileOptions options = CreateFileOptions.None, IProgressReport logger = null)
{
using (DirectoryHandle sourceHandle = fs.OpenDirectory(sourcePath, OpenDirectoryMode.All))
{
foreach (DirectoryEntry entry in fs.ReadDirectory(sourceHandle))
{
string subSrcPath = PathTools.Normalize(PathTools.Combine(sourcePath, entry.Name));
string subDstPath = PathTools.Normalize(PathTools.Combine(destPath, entry.Name));
if (entry.Type == DirectoryEntryType.Directory)
{
fs.CreateDirectory(subDstPath);
fs.CopyDirectory(subSrcPath, subDstPath, options, logger);
}
if (entry.Type == DirectoryEntryType.File)
{
logger?.LogMessage(subSrcPath);
fs.CreateFile(subDstPath, entry.Size, options);
fs.CopyFile(subSrcPath, subDstPath, logger);
}
}
}
}
public static void CopyFile(this FileSystemManager fs, string sourcePath, string destPath, IProgressReport logger = null)
{
using (FileHandle sourceHandle = fs.OpenFile(sourcePath, OpenMode.Read))
using (FileHandle destHandle = fs.OpenFile(destPath, OpenMode.Write | OpenMode.Append))
{
const int maxBufferSize = 0x10000;
long fileSize = fs.GetFileSize(sourceHandle);
int bufferSize = (int)Math.Min(maxBufferSize, fileSize);
logger?.SetTotal(fileSize);
byte[] buffer = ArrayPool<byte>.Shared.Rent(bufferSize);
try
{
for (long offset = 0; offset < fileSize; offset += bufferSize)
{
int toRead = (int)Math.Min(fileSize - offset, bufferSize);
Span<byte> buf = buffer.AsSpan(0, toRead);
fs.ReadFile(sourceHandle, buf, offset);
fs.WriteFile(destHandle, buf, offset);
logger?.ReportAdd(toRead);
}
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
logger?.SetTotal(0);
}
fs.FlushFile(destHandle);
}
}
public static IEnumerable<DirectoryEntry> EnumerateEntries(this FileSystemManager fs, string path)
{
return fs.EnumerateEntries(path, "*");
}
public static IEnumerable<DirectoryEntry> EnumerateEntries(this FileSystemManager fs, string path, string searchPattern)
{
return fs.EnumerateEntries(path, searchPattern, SearchOptions.RecurseSubdirectories);
}
public static IEnumerable<DirectoryEntry> EnumerateEntries(this FileSystemManager fs, string path, string searchPattern, SearchOptions searchOptions)
{
bool ignoreCase = searchOptions.HasFlag(SearchOptions.CaseInsensitive);
bool recurse = searchOptions.HasFlag(SearchOptions.RecurseSubdirectories);
using (DirectoryHandle sourceHandle = fs.OpenDirectory(path, OpenDirectoryMode.All))
{
foreach (DirectoryEntry entry in fs.ReadDirectory(sourceHandle))
{
if (PathTools.MatchesPattern(searchPattern, entry.Name, ignoreCase))
{
yield return entry;
}
if (entry.Type != DirectoryEntryType.Directory || !recurse) continue;
string subPath = PathTools.Normalize(PathTools.Combine(path, entry.Name));
IEnumerable<DirectoryEntry> subEntries = fs.EnumerateEntries(subPath, searchPattern, searchOptions);
foreach (DirectoryEntry subEntry in subEntries)
{
subEntry.FullPath = PathTools.Combine(path, subEntry.Name);
yield return subEntry;
}
}
}
}
}
}

View File

@ -5,6 +5,10 @@ using System.Runtime.CompilerServices;
using static LibHac.Results;
using static LibHac.Fs.ResultsFs;
#if !NETFRAMEWORK
using System.IO.Enumeration;
#endif
namespace LibHac.Fs
{
public static class PathTools
@ -381,6 +385,17 @@ namespace LibHac.Fs
return ResultFsInvalidMountName;
}
public static bool MatchesPattern(string searchPattern, string name, bool ignoreCase)
{
#if NETFRAMEWORK
return Compatibility.FileSystemName.MatchesSimpleExpression(searchPattern.AsSpan(),
name.AsSpan(), ignoreCase);
#else
return FileSystemName.MatchesSimpleExpression(searchPattern.AsSpan(),
name.AsSpan(), ignoreCase);
#endif
}
private static bool IsValidMountNameChar(char c)
{
c |= (char)0x20;

View File

@ -1,5 +1,4 @@
using LibHac.Fs;
using LibHac.Fs.Accessors;
namespace LibHac
{
@ -14,11 +13,11 @@ namespace LibHac
Fs = new FileSystemManager(this);
}
public Horizon(IAccessLogger logger, ITimeSpanGenerator timer)
public Horizon(ITimeSpanGenerator timer)
{
Time = timer;
Fs = new FileSystemManager(this, logger, timer);
Fs = new FileSystemManager(this, timer);
}
}
}