mirror of
https://github.com/claunia/romrepomgr.git
synced 2025-12-16 19:24:51 +00:00
Move opening, reading and closing files to VFS.
This commit is contained in:
@@ -8,20 +8,14 @@ using Mono.Fuse.NETStandard;
|
|||||||
using Mono.Unix.Native;
|
using Mono.Unix.Native;
|
||||||
using RomRepoMgr.Database;
|
using RomRepoMgr.Database;
|
||||||
using RomRepoMgr.Database.Models;
|
using RomRepoMgr.Database.Models;
|
||||||
using SharpCompress.Compressors;
|
|
||||||
using SharpCompress.Compressors.LZMA;
|
|
||||||
|
|
||||||
namespace RomRepoMgr.Core.Filesystem
|
namespace RomRepoMgr.Core.Filesystem
|
||||||
{
|
{
|
||||||
// TODO: Invalidate caches
|
|
||||||
// TODO: Mount options
|
|
||||||
// TODO: Do not show machines or romsets with no ROMs in repo
|
|
||||||
// TODO: Last handle goes negative
|
// TODO: Last handle goes negative
|
||||||
public sealed class Fuse : FileSystem
|
public sealed class Fuse : FileSystem
|
||||||
{
|
{
|
||||||
readonly ConcurrentDictionary<long, List<DirectoryEntry>> _directoryCache;
|
readonly ConcurrentDictionary<long, List<DirectoryEntry>> _directoryCache;
|
||||||
readonly ConcurrentDictionary<long, Stat> _fileStatHandleCache;
|
readonly ConcurrentDictionary<long, Stat> _fileStatHandleCache;
|
||||||
readonly ConcurrentDictionary<long, Stream> _streamsCache;
|
|
||||||
readonly Vfs _vfs;
|
readonly Vfs _vfs;
|
||||||
long _lastHandle;
|
long _lastHandle;
|
||||||
string _umountToken;
|
string _umountToken;
|
||||||
@@ -30,7 +24,6 @@ namespace RomRepoMgr.Core.Filesystem
|
|||||||
{
|
{
|
||||||
_directoryCache = new ConcurrentDictionary<long, List<DirectoryEntry>>();
|
_directoryCache = new ConcurrentDictionary<long, List<DirectoryEntry>>();
|
||||||
_lastHandle = 0;
|
_lastHandle = 0;
|
||||||
_streamsCache = new ConcurrentDictionary<long, Stream>();
|
|
||||||
_fileStatHandleCache = new ConcurrentDictionary<long, Stat>();
|
_fileStatHandleCache = new ConcurrentDictionary<long, Stat>();
|
||||||
Name = "romrepombgrfs";
|
Name = "romrepombgrfs";
|
||||||
_vfs = vfs;
|
_vfs = vfs;
|
||||||
@@ -76,14 +69,6 @@ namespace RomRepoMgr.Core.Filesystem
|
|||||||
[DllImport("libdl")]
|
[DllImport("libdl")]
|
||||||
static extern int dlclose(IntPtr handle);
|
static extern int dlclose(IntPtr handle);
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if(!disposing)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// TODO: Close streams manually
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Errno OnGetPathStatus(string path, out Stat stat)
|
protected override Errno OnGetPathStatus(string path, out Stat stat)
|
||||||
{
|
{
|
||||||
stat = new Stat();
|
stat = new Stat();
|
||||||
@@ -253,51 +238,14 @@ namespace RomRepoMgr.Core.Filesystem
|
|||||||
info.OpenAccess.HasFlag(OpenFlags.O_TRUNC))
|
info.OpenAccess.HasFlag(OpenFlags.O_TRUNC))
|
||||||
return Errno.EROFS;
|
return Errno.EROFS;
|
||||||
|
|
||||||
byte[] sha384Bytes = new byte[48];
|
long handle = _vfs.Open(file.Sha384, (long)file.Size);
|
||||||
string sha384 = file.Sha384;
|
|
||||||
|
|
||||||
for(int i = 0; i < 48; i++)
|
if(handle <= 0)
|
||||||
{
|
|
||||||
if(sha384[i * 2] >= 0x30 &&
|
|
||||||
sha384[i * 2] <= 0x39)
|
|
||||||
sha384Bytes[i] = (byte)((sha384[i * 2] - 0x30) * 0x10);
|
|
||||||
else if(sha384[i * 2] >= 0x41 &&
|
|
||||||
sha384[i * 2] <= 0x46)
|
|
||||||
sha384Bytes[i] = (byte)((sha384[i * 2] - 0x37) * 0x10);
|
|
||||||
else if(sha384[i * 2] >= 0x61 &&
|
|
||||||
sha384[i * 2] <= 0x66)
|
|
||||||
sha384Bytes[i] = (byte)((sha384[i * 2] - 0x57) * 0x10);
|
|
||||||
|
|
||||||
if(sha384[(i * 2) + 1] >= 0x30 &&
|
|
||||||
sha384[(i * 2) + 1] <= 0x39)
|
|
||||||
sha384Bytes[i] += (byte)(sha384[(i * 2) + 1] - 0x30);
|
|
||||||
else if(sha384[(i * 2) + 1] >= 0x41 &&
|
|
||||||
sha384[(i * 2) + 1] <= 0x46)
|
|
||||||
sha384Bytes[i] += (byte)(sha384[(i * 2) + 1] - 0x37);
|
|
||||||
else if(sha384[(i * 2) + 1] >= 0x61 &&
|
|
||||||
sha384[(i * 2) + 1] <= 0x66)
|
|
||||||
sha384Bytes[i] += (byte)(sha384[(i * 2) + 1] - 0x57);
|
|
||||||
}
|
|
||||||
|
|
||||||
string sha384B32 = Base32.ToBase32String(sha384Bytes);
|
|
||||||
|
|
||||||
string repoPath = Path.Combine(Settings.Settings.Current.RepositoryPath, "files", sha384B32[0].ToString(),
|
|
||||||
sha384B32[1].ToString(), sha384B32[2].ToString(), sha384B32[3].ToString(),
|
|
||||||
sha384B32[4].ToString(), sha384B32 + ".lz");
|
|
||||||
|
|
||||||
if(!File.Exists(repoPath))
|
|
||||||
return Errno.ENOENT;
|
return Errno.ENOENT;
|
||||||
|
|
||||||
_lastHandle++;
|
info.Handle = new IntPtr(handle);
|
||||||
info.Handle = new IntPtr(_lastHandle);
|
|
||||||
|
|
||||||
_streamsCache[_lastHandle] =
|
_fileStatHandleCache[handle] = new Stat
|
||||||
Stream.Synchronized(new ForcedSeekStream<LZipStream>((long)file.Size,
|
|
||||||
new FileStream(repoPath, FileMode.Open,
|
|
||||||
FileAccess.Read),
|
|
||||||
CompressionMode.Decompress));
|
|
||||||
|
|
||||||
_fileStatHandleCache[_lastHandle] = new Stat
|
|
||||||
{
|
{
|
||||||
st_mode = FilePermissions.S_IFREG | NativeConvert.FromOctalPermissionString("0444"),
|
st_mode = FilePermissions.S_IFREG | NativeConvert.FromOctalPermissionString("0444"),
|
||||||
st_nlink = 1,
|
st_nlink = 1,
|
||||||
@@ -315,15 +263,14 @@ namespace RomRepoMgr.Core.Filesystem
|
|||||||
protected override Errno OnReadHandle(string file, OpenedPathInfo info, byte[] buf, long offset,
|
protected override Errno OnReadHandle(string file, OpenedPathInfo info, byte[] buf, long offset,
|
||||||
out int bytesWritten)
|
out int bytesWritten)
|
||||||
{
|
{
|
||||||
|
bytesWritten = _vfs.Read(info.Handle.ToInt64(), buf, offset);
|
||||||
|
|
||||||
|
if(bytesWritten >= 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
bytesWritten = 0;
|
bytesWritten = 0;
|
||||||
|
|
||||||
if(!_streamsCache.TryGetValue(info.Handle.ToInt64(), out Stream fileStream))
|
|
||||||
return Errno.EBADF;
|
return Errno.EBADF;
|
||||||
|
|
||||||
fileStream.Position = offset;
|
|
||||||
bytesWritten = fileStream.Read(buf, 0, buf.Length);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Errno OnWriteHandle(string file, OpenedPathInfo info, byte[] buf, long offset,
|
protected override Errno OnWriteHandle(string file, OpenedPathInfo info, byte[] buf, long offset,
|
||||||
@@ -359,11 +306,9 @@ namespace RomRepoMgr.Core.Filesystem
|
|||||||
|
|
||||||
protected override Errno OnReleaseHandle(string file, OpenedPathInfo info)
|
protected override Errno OnReleaseHandle(string file, OpenedPathInfo info)
|
||||||
{
|
{
|
||||||
if(!_streamsCache.TryGetValue(info.Handle.ToInt64(), out Stream fileStream))
|
if(!_vfs.Close(info.Handle.ToInt64()))
|
||||||
return Errno.EBADF;
|
return Errno.EBADF;
|
||||||
|
|
||||||
fileStream.Close();
|
|
||||||
_streamsCache.TryRemove(info.Handle.ToInt64(), out _);
|
|
||||||
_fileStatHandleCache.TryRemove(info.Handle.ToInt64(), out _);
|
_fileStatHandleCache.TryRemove(info.Handle.ToInt64(), out _);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -1,20 +1,29 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using RomRepoMgr.Database;
|
using RomRepoMgr.Database;
|
||||||
using RomRepoMgr.Database.Models;
|
using RomRepoMgr.Database.Models;
|
||||||
|
using SharpCompress.Compressors;
|
||||||
|
using SharpCompress.Compressors.LZMA;
|
||||||
|
|
||||||
namespace RomRepoMgr.Core.Filesystem
|
namespace RomRepoMgr.Core.Filesystem
|
||||||
{
|
{
|
||||||
|
// TODO: Last handle goes negative
|
||||||
|
// TODO: Invalidate caches
|
||||||
|
// TODO: Mount options
|
||||||
|
// TODO: Do not show machines or romsets with no ROMs in repo
|
||||||
public class Vfs : IDisposable
|
public class Vfs : IDisposable
|
||||||
{
|
{
|
||||||
readonly ConcurrentDictionary<ulong, ConcurrentDictionary<string, CachedFile>> _machineFilesCache;
|
readonly ConcurrentDictionary<ulong, ConcurrentDictionary<string, CachedFile>> _machineFilesCache;
|
||||||
readonly ConcurrentDictionary<long, ConcurrentDictionary<string, CachedMachine>> _machinesStatCache;
|
readonly ConcurrentDictionary<long, ConcurrentDictionary<string, CachedMachine>> _machinesStatCache;
|
||||||
readonly ConcurrentDictionary<long, RomSet> _romSetsCache;
|
readonly ConcurrentDictionary<long, RomSet> _romSetsCache;
|
||||||
|
readonly ConcurrentDictionary<long, Stream> _streamsCache;
|
||||||
Fuse _fuse;
|
Fuse _fuse;
|
||||||
|
long _lastHandle;
|
||||||
ConcurrentDictionary<string, long> _rootDirectoryCache;
|
ConcurrentDictionary<string, long> _rootDirectoryCache;
|
||||||
Winfsp _winfsp;
|
Winfsp _winfsp;
|
||||||
|
|
||||||
@@ -24,11 +33,13 @@ namespace RomRepoMgr.Core.Filesystem
|
|||||||
_romSetsCache = new ConcurrentDictionary<long, RomSet>();
|
_romSetsCache = new ConcurrentDictionary<long, RomSet>();
|
||||||
_machinesStatCache = new ConcurrentDictionary<long, ConcurrentDictionary<string, CachedMachine>>();
|
_machinesStatCache = new ConcurrentDictionary<long, ConcurrentDictionary<string, CachedMachine>>();
|
||||||
_machineFilesCache = new ConcurrentDictionary<ulong, ConcurrentDictionary<string, CachedFile>>();
|
_machineFilesCache = new ConcurrentDictionary<ulong, ConcurrentDictionary<string, CachedFile>>();
|
||||||
|
_streamsCache = new ConcurrentDictionary<long, Stream>();
|
||||||
|
_lastHandle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsAvailable => Winfsp.IsAvailable || Fuse.IsAvailable;
|
public static bool IsAvailable => Winfsp.IsAvailable || Fuse.IsAvailable;
|
||||||
|
|
||||||
public void Dispose() => _fuse?.Dispose();
|
public void Dispose() => Umount();
|
||||||
|
|
||||||
public event EventHandler<System.EventArgs> Umounted;
|
public event EventHandler<System.EventArgs> Umounted;
|
||||||
|
|
||||||
@@ -45,7 +56,7 @@ namespace RomRepoMgr.Core.Filesystem
|
|||||||
{
|
{
|
||||||
_fuse.Start();
|
_fuse.Start();
|
||||||
|
|
||||||
Umounted?.Invoke(this, System.EventArgs.Empty);
|
CleanUp();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if(Winfsp.IsAvailable)
|
else if(Winfsp.IsAvailable)
|
||||||
@@ -57,10 +68,10 @@ namespace RomRepoMgr.Core.Filesystem
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
_winfsp = null;
|
_winfsp = null;
|
||||||
Umounted?.Invoke(this, System.EventArgs.Empty);
|
CleanUp();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
Umounted?.Invoke(this, System.EventArgs.Empty);
|
CleanUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Umount()
|
public void Umount()
|
||||||
@@ -70,6 +81,17 @@ namespace RomRepoMgr.Core.Filesystem
|
|||||||
_winfsp?.Umount();
|
_winfsp?.Umount();
|
||||||
_winfsp = null;
|
_winfsp = null;
|
||||||
|
|
||||||
|
CleanUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CleanUp()
|
||||||
|
{
|
||||||
|
foreach(KeyValuePair<long, Stream> handle in _streamsCache)
|
||||||
|
handle.Value.Close();
|
||||||
|
|
||||||
|
_streamsCache.Clear();
|
||||||
|
_lastHandle = 0;
|
||||||
|
|
||||||
Umounted?.Invoke(this, System.EventArgs.Empty);
|
Umounted?.Invoke(this, System.EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,6 +261,75 @@ namespace RomRepoMgr.Core.Filesystem
|
|||||||
|
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal long Open(string sha384, long fileSize)
|
||||||
|
{
|
||||||
|
byte[] sha384Bytes = new byte[48];
|
||||||
|
|
||||||
|
for(int i = 0; i < 48; i++)
|
||||||
|
{
|
||||||
|
if(sha384[i * 2] >= 0x30 &&
|
||||||
|
sha384[i * 2] <= 0x39)
|
||||||
|
sha384Bytes[i] = (byte)((sha384[i * 2] - 0x30) * 0x10);
|
||||||
|
else if(sha384[i * 2] >= 0x41 &&
|
||||||
|
sha384[i * 2] <= 0x46)
|
||||||
|
sha384Bytes[i] = (byte)((sha384[i * 2] - 0x37) * 0x10);
|
||||||
|
else if(sha384[i * 2] >= 0x61 &&
|
||||||
|
sha384[i * 2] <= 0x66)
|
||||||
|
sha384Bytes[i] = (byte)((sha384[i * 2] - 0x57) * 0x10);
|
||||||
|
|
||||||
|
if(sha384[(i * 2) + 1] >= 0x30 &&
|
||||||
|
sha384[(i * 2) + 1] <= 0x39)
|
||||||
|
sha384Bytes[i] += (byte)(sha384[(i * 2) + 1] - 0x30);
|
||||||
|
else if(sha384[(i * 2) + 1] >= 0x41 &&
|
||||||
|
sha384[(i * 2) + 1] <= 0x46)
|
||||||
|
sha384Bytes[i] += (byte)(sha384[(i * 2) + 1] - 0x37);
|
||||||
|
else if(sha384[(i * 2) + 1] >= 0x61 &&
|
||||||
|
sha384[(i * 2) + 1] <= 0x66)
|
||||||
|
sha384Bytes[i] += (byte)(sha384[(i * 2) + 1] - 0x57);
|
||||||
|
}
|
||||||
|
|
||||||
|
string sha384B32 = Base32.ToBase32String(sha384Bytes);
|
||||||
|
|
||||||
|
string repoPath = Path.Combine(Settings.Settings.Current.RepositoryPath, "files", sha384B32[0].ToString(),
|
||||||
|
sha384B32[1].ToString(), sha384B32[2].ToString(), sha384B32[3].ToString(),
|
||||||
|
sha384B32[4].ToString(), sha384B32 + ".lz");
|
||||||
|
|
||||||
|
if(!File.Exists(repoPath))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
_lastHandle++;
|
||||||
|
long handle = _lastHandle;
|
||||||
|
|
||||||
|
_streamsCache[handle] =
|
||||||
|
Stream.Synchronized(new ForcedSeekStream<LZipStream>(fileSize,
|
||||||
|
new FileStream(repoPath, FileMode.Open,
|
||||||
|
FileAccess.Read),
|
||||||
|
CompressionMode.Decompress));
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal int Read(long handle, byte[] buf, long offset)
|
||||||
|
{
|
||||||
|
if(!_streamsCache.TryGetValue(handle, out Stream stream))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
stream.Position = offset;
|
||||||
|
|
||||||
|
return stream.Read(buf, 0, buf.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool Close(long handle)
|
||||||
|
{
|
||||||
|
if(!_streamsCache.TryGetValue(handle, out Stream stream))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
stream.Close();
|
||||||
|
_streamsCache.TryRemove(handle, out _);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class CachedMachine
|
internal sealed class CachedMachine
|
||||||
|
|||||||
Reference in New Issue
Block a user