mirror of
https://github.com/Thealexbarney/LibHac.git
synced 2025-02-09 13:14:46 +01:00
Add close and unmount methods. Add helpers for common fs tasks
This commit is contained in:
parent
1859b4ce26
commit
b51d4397e9
@ -15,7 +15,7 @@ namespace LibHac.Fs.Accessors
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Directory.Dispose();
|
||||
Directory.Parent.FsManager.CloseDirectory(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ namespace LibHac.Fs.Accessors
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
File.Dispose();
|
||||
File.Parent.FsManager.CloseFile(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 = "");
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
113
src/LibHac/Fs/FileSystemManagerUtils.cs
Normal file
113
src/LibHac/Fs/FileSystemManagerUtils.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user