From 5229b88074726af858946d0227286ab3311176cc Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Thu, 3 Sep 2020 01:54:56 +0100 Subject: [PATCH] Move opening, reading and closing files to VFS. --- RomRepoMgr.Core/Filesystem/Fuse.cs | 77 ++++------------------- RomRepoMgr.Core/Filesystem/Vfs.cs | 99 ++++++++++++++++++++++++++++-- 2 files changed, 106 insertions(+), 70 deletions(-) diff --git a/RomRepoMgr.Core/Filesystem/Fuse.cs b/RomRepoMgr.Core/Filesystem/Fuse.cs index 0f1c75b..140cb36 100644 --- a/RomRepoMgr.Core/Filesystem/Fuse.cs +++ b/RomRepoMgr.Core/Filesystem/Fuse.cs @@ -8,20 +8,14 @@ using Mono.Fuse.NETStandard; using Mono.Unix.Native; using RomRepoMgr.Database; using RomRepoMgr.Database.Models; -using SharpCompress.Compressors; -using SharpCompress.Compressors.LZMA; 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 public sealed class Fuse : FileSystem { readonly ConcurrentDictionary> _directoryCache; readonly ConcurrentDictionary _fileStatHandleCache; - readonly ConcurrentDictionary _streamsCache; readonly Vfs _vfs; long _lastHandle; string _umountToken; @@ -30,7 +24,6 @@ namespace RomRepoMgr.Core.Filesystem { _directoryCache = new ConcurrentDictionary>(); _lastHandle = 0; - _streamsCache = new ConcurrentDictionary(); _fileStatHandleCache = new ConcurrentDictionary(); Name = "romrepombgrfs"; _vfs = vfs; @@ -76,14 +69,6 @@ namespace RomRepoMgr.Core.Filesystem [DllImport("libdl")] 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) { stat = new Stat(); @@ -253,51 +238,14 @@ namespace RomRepoMgr.Core.Filesystem info.OpenAccess.HasFlag(OpenFlags.O_TRUNC)) return Errno.EROFS; - byte[] sha384Bytes = new byte[48]; - string sha384 = file.Sha384; + long handle = _vfs.Open(file.Sha384, (long)file.Size); - 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)) + if(handle <= 0) return Errno.ENOENT; - _lastHandle++; - info.Handle = new IntPtr(_lastHandle); + info.Handle = new IntPtr(handle); - _streamsCache[_lastHandle] = - Stream.Synchronized(new ForcedSeekStream((long)file.Size, - new FileStream(repoPath, FileMode.Open, - FileAccess.Read), - CompressionMode.Decompress)); - - _fileStatHandleCache[_lastHandle] = new Stat + _fileStatHandleCache[handle] = new Stat { st_mode = FilePermissions.S_IFREG | NativeConvert.FromOctalPermissionString("0444"), st_nlink = 1, @@ -315,15 +263,14 @@ namespace RomRepoMgr.Core.Filesystem protected override Errno OnReadHandle(string file, OpenedPathInfo info, byte[] buf, long offset, out int bytesWritten) { + bytesWritten = _vfs.Read(info.Handle.ToInt64(), buf, offset); + + if(bytesWritten >= 0) + return 0; + bytesWritten = 0; - if(!_streamsCache.TryGetValue(info.Handle.ToInt64(), out Stream fileStream)) - return Errno.EBADF; - - fileStream.Position = offset; - bytesWritten = fileStream.Read(buf, 0, buf.Length); - - return 0; + return Errno.EBADF; } 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) { - if(!_streamsCache.TryGetValue(info.Handle.ToInt64(), out Stream fileStream)) + if(!_vfs.Close(info.Handle.ToInt64())) return Errno.EBADF; - fileStream.Close(); - _streamsCache.TryRemove(info.Handle.ToInt64(), out _); _fileStatHandleCache.TryRemove(info.Handle.ToInt64(), out _); return 0; diff --git a/RomRepoMgr.Core/Filesystem/Vfs.cs b/RomRepoMgr.Core/Filesystem/Vfs.cs index 5737e45..230b241 100644 --- a/RomRepoMgr.Core/Filesystem/Vfs.cs +++ b/RomRepoMgr.Core/Filesystem/Vfs.cs @@ -1,20 +1,29 @@ using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Threading.Tasks; using RomRepoMgr.Database; using RomRepoMgr.Database.Models; +using SharpCompress.Compressors; +using SharpCompress.Compressors.LZMA; 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 { readonly ConcurrentDictionary> _machineFilesCache; readonly ConcurrentDictionary> _machinesStatCache; readonly ConcurrentDictionary _romSetsCache; + readonly ConcurrentDictionary _streamsCache; Fuse _fuse; + long _lastHandle; ConcurrentDictionary _rootDirectoryCache; Winfsp _winfsp; @@ -24,11 +33,13 @@ namespace RomRepoMgr.Core.Filesystem _romSetsCache = new ConcurrentDictionary(); _machinesStatCache = new ConcurrentDictionary>(); _machineFilesCache = new ConcurrentDictionary>(); + _streamsCache = new ConcurrentDictionary(); + _lastHandle = 0; } public static bool IsAvailable => Winfsp.IsAvailable || Fuse.IsAvailable; - public void Dispose() => _fuse?.Dispose(); + public void Dispose() => Umount(); public event EventHandler Umounted; @@ -45,7 +56,7 @@ namespace RomRepoMgr.Core.Filesystem { _fuse.Start(); - Umounted?.Invoke(this, System.EventArgs.Empty); + CleanUp(); }); } else if(Winfsp.IsAvailable) @@ -57,10 +68,10 @@ namespace RomRepoMgr.Core.Filesystem return; _winfsp = null; - Umounted?.Invoke(this, System.EventArgs.Empty); + CleanUp(); } else - Umounted?.Invoke(this, System.EventArgs.Empty); + CleanUp(); } public void Umount() @@ -70,6 +81,17 @@ namespace RomRepoMgr.Core.Filesystem _winfsp?.Umount(); _winfsp = null; + CleanUp(); + } + + public void CleanUp() + { + foreach(KeyValuePair handle in _streamsCache) + handle.Value.Close(); + + _streamsCache.Clear(); + _lastHandle = 0; + Umounted?.Invoke(this, System.EventArgs.Empty); } @@ -239,6 +261,75 @@ namespace RomRepoMgr.Core.Filesystem 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(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