Show disks in virtual filesystem.

This commit is contained in:
2020-09-04 20:38:43 +01:00
parent 5ecc7e5157
commit c0b1a7729c
3 changed files with 432 additions and 86 deletions

View File

@@ -6,7 +6,6 @@ using System.Linq;
using System.Runtime.InteropServices;
using Mono.Fuse.NETStandard;
using Mono.Unix.Native;
using RomRepoMgr.Database;
using RomRepoMgr.Database.Models;
namespace RomRepoMgr.Core.Filesystem
@@ -141,11 +140,11 @@ namespace RomRepoMgr.Core.Filesystem
CachedFile file = _vfs.GetFile(machine.Id, pieces[2]);
if(file == null)
return Errno.ENOENT;
if(pieces.Length == 3)
if(file != null)
{
if(pieces.Length != 3)
return Errno.ENOSYS;
stat = new Stat
{
st_mode = FilePermissions.S_IFREG | NativeConvert.FromOctalPermissionString("0444"),
@@ -161,7 +160,27 @@ namespace RomRepoMgr.Core.Filesystem
return 0;
}
return Errno.ENOSYS;
CachedDisk disk = _vfs.GetDisk(machine.Id, pieces[2]);
if(disk == null)
return Errno.ENOENT;
if(pieces.Length != 3)
return Errno.ENOSYS;
stat = new Stat
{
st_mode = FilePermissions.S_IFREG | NativeConvert.FromOctalPermissionString("0444"),
st_nlink = 1,
st_ctime = NativeConvert.ToTimeT(disk.CreatedOn.ToUniversalTime()),
st_mtime = NativeConvert.ToTimeT(disk.UpdatedOn.ToUniversalTime()),
st_blksize = 512,
st_blocks = (long)(disk.Size / 512),
st_ino = disk.Id,
st_size = (long)disk.Size
};
return 0;
}
protected override Errno OnReadSymbolicLink(string link, out string target)
@@ -221,41 +240,80 @@ namespace RomRepoMgr.Core.Filesystem
if(pieces.Length == 2)
return Errno.EISDIR;
long handle = 0;
Stat stat;
CachedFile file = _vfs.GetFile(machine.Id, pieces[2]);
if(file == null)
return Errno.ENOENT;
if(file != null)
{
if(pieces.Length > 3)
return Errno.ENOSYS;
if(pieces.Length > 3)
return Errno.ENOSYS;
if(file.Sha384 == null)
return Errno.ENOENT;
if(file.Sha384 == null)
return Errno.ENOENT;
if(info.OpenAccess.HasFlag(OpenFlags.O_APPEND) ||
info.OpenAccess.HasFlag(OpenFlags.O_CREAT) ||
info.OpenAccess.HasFlag(OpenFlags.O_EXCL) ||
info.OpenAccess.HasFlag(OpenFlags.O_TRUNC))
return Errno.EROFS;
if(info.OpenAccess.HasFlag(OpenFlags.O_APPEND) ||
info.OpenAccess.HasFlag(OpenFlags.O_CREAT) ||
info.OpenAccess.HasFlag(OpenFlags.O_EXCL) ||
info.OpenAccess.HasFlag(OpenFlags.O_TRUNC))
return Errno.EROFS;
handle = _vfs.Open(file.Sha384, (long)file.Size);
long handle = _vfs.Open(file.Sha384, (long)file.Size);
stat = new Stat
{
st_mode = FilePermissions.S_IFREG | NativeConvert.FromOctalPermissionString("0444"),
st_nlink = 1,
st_ctime = NativeConvert.ToTimeT(file.CreatedOn.ToUniversalTime()),
st_mtime = NativeConvert.ToTimeT(file.UpdatedOn.ToUniversalTime()),
st_blksize = 512,
st_blocks = (long)(file.Size / 512),
st_ino = file.Id,
st_size = (long)file.Size
};
}
else
{
CachedDisk disk = _vfs.GetDisk(machine.Id, pieces[2]);
if(disk == null)
return Errno.ENOENT;
if(pieces.Length > 3)
return Errno.ENOSYS;
if(disk.Sha1 == null &&
disk.Md5 == null)
return Errno.ENOENT;
if(info.OpenAccess.HasFlag(OpenFlags.O_APPEND) ||
info.OpenAccess.HasFlag(OpenFlags.O_CREAT) ||
info.OpenAccess.HasFlag(OpenFlags.O_EXCL) ||
info.OpenAccess.HasFlag(OpenFlags.O_TRUNC))
return Errno.EROFS;
handle = _vfs.OpenDisk(disk.Sha1, disk.Md5);
stat = new Stat
{
st_mode = FilePermissions.S_IFREG | NativeConvert.FromOctalPermissionString("0444"),
st_nlink = 1,
st_ctime = NativeConvert.ToTimeT(disk.CreatedOn.ToUniversalTime()),
st_mtime = NativeConvert.ToTimeT(disk.UpdatedOn.ToUniversalTime()),
st_blksize = 512,
st_blocks = (long)(disk.Size / 512),
st_ino = disk.Id,
st_size = (long)disk.Size
};
}
if(handle <= 0)
return Errno.ENOENT;
info.Handle = new IntPtr(handle);
_fileStatHandleCache[handle] = new Stat
{
st_mode = FilePermissions.S_IFREG | NativeConvert.FromOctalPermissionString("0444"),
st_nlink = 1,
st_ctime = NativeConvert.ToTimeT(file.CreatedOn.ToUniversalTime()),
st_mtime = NativeConvert.ToTimeT(file.UpdatedOn.ToUniversalTime()),
st_blksize = 512,
st_blocks = (long)(file.Size / 512),
st_ino = file.Id,
st_size = (long)file.Size
};
_fileStatHandleCache[handle] = stat;
return 0;
}
@@ -370,40 +428,59 @@ namespace RomRepoMgr.Core.Filesystem
CachedFile file = _vfs.GetFile(machine.Id, pieces[2]);
if(file == null)
return Errno.ENOENT;
if(pieces.Length > 3)
return Errno.ENOSYS;
string hash = null;
switch(name)
if(file != null)
{
case "user.crc32":
hash = file.Crc32;
if(pieces.Length > 3)
return Errno.ENOSYS;
break;
case "user.md5":
hash = file.Md5;
switch(name)
{
case "user.crc32":
hash = file.Crc32;
break;
case "user.sha1":
hash = file.Sha1;
break;
case "user.md5":
hash = file.Md5;
break;
case "user.sha256":
hash = file.Sha256;
break;
case "user.sha1":
hash = file.Sha1;
break;
case "user.sha384":
hash = file.Sha384;
break;
case "user.sha256":
hash = file.Sha256;
break;
case "user.sha512":
hash = file.Sha512;
break;
case "user.sha384":
hash = file.Sha384;
break;
break;
case "user.sha512":
hash = file.Sha512;
break;
}
}
else
{
CachedDisk disk = _vfs.GetDisk(machine.Id, pieces[2]);
if(disk == null)
return Errno.ENOENT;
switch(name)
{
case "user.md5":
hash = disk.Md5;
break;
case "user.sha1":
hash = disk.Sha1;
break;
}
}
if(hash == null)
@@ -479,34 +556,52 @@ namespace RomRepoMgr.Core.Filesystem
if(pieces.Length == 2)
return 0;
List<string> xattrs = new List<string>();
CachedFile file = _vfs.GetFile(machine.Id, pieces[2]);
if(file == null)
if(file != null)
{
if(pieces.Length > 3)
return Errno.ENOSYS;
if(file.Crc32 != null)
xattrs.Add("user.crc32");
if(file.Md5 != null)
xattrs.Add("user.md5");
if(file.Sha1 != null)
xattrs.Add("user.sha1");
if(file.Sha256 != null)
xattrs.Add("user.sha256");
if(file.Sha384 != null)
xattrs.Add("user.sha384");
if(file.Sha512 != null)
xattrs.Add("user.sha512");
names = xattrs.ToArray();
return 0;
}
CachedDisk disk = _vfs.GetDisk(machine.Id, pieces[2]);
if(disk == null)
return Errno.ENOENT;
if(pieces.Length > 3)
return Errno.ENOSYS;
List<string> xattrs = new List<string>();
if(file.Crc32 != null)
xattrs.Add("user.crc32");
if(file.Md5 != null)
if(disk.Md5 != null)
xattrs.Add("user.md5");
if(file.Sha1 != null)
if(disk.Sha1 != null)
xattrs.Add("user.sha1");
if(file.Sha256 != null)
xattrs.Add("user.sha256");
if(file.Sha384 != null)
xattrs.Add("user.sha384");
if(file.Sha512 != null)
xattrs.Add("user.sha512");
names = xattrs.ToArray();
return 0;
@@ -577,6 +672,7 @@ namespace RomRepoMgr.Core.Filesystem
return Errno.ENOENT;
ConcurrentDictionary<string, CachedFile> cachedMachineFiles = _vfs.GetFilesFromMachine(machine.Id);
ConcurrentDictionary<string, CachedDisk> cachedMachineDisks = _vfs.GetDisksFromMachine(machine.Id);
if(pieces.Length == 2)
{
@@ -587,6 +683,7 @@ namespace RomRepoMgr.Core.Filesystem
};
entries.AddRange(cachedMachineFiles.Select(file => new DirectoryEntry(file.Key)));
entries.AddRange(cachedMachineDisks.Select(disk => new DirectoryEntry(disk.Key + ".chd")));
_lastHandle++;
info.Handle = new IntPtr(_lastHandle);
@@ -666,7 +763,17 @@ namespace RomRepoMgr.Core.Filesystem
CachedFile file = _vfs.GetFile(machine.Id, pieces[2]);
if(file == null)
if(file != null)
{
if(pieces.Length > 3)
return Errno.ENOSYS;
return mode.HasFlag(AccessModes.W_OK) ? Errno.EROFS : 0;
}
CachedDisk disk = _vfs.GetDisk(machine.Id, pieces[2]);
if(disk == null)
return Errno.ENOENT;
if(pieces.Length > 3)

View File

@@ -18,6 +18,7 @@ namespace RomRepoMgr.Core.Filesystem
// TODO: Do not show machines or romsets with no ROMs in repo
public class Vfs : IDisposable
{
readonly ConcurrentDictionary<ulong, ConcurrentDictionary<string, CachedDisk>> _machineDisksCache;
readonly ConcurrentDictionary<ulong, ConcurrentDictionary<string, CachedFile>> _machineFilesCache;
readonly ConcurrentDictionary<long, ConcurrentDictionary<string, CachedMachine>> _machinesStatCache;
readonly ConcurrentDictionary<long, RomSet> _romSetsCache;
@@ -33,6 +34,7 @@ namespace RomRepoMgr.Core.Filesystem
_romSetsCache = new ConcurrentDictionary<long, RomSet>();
_machinesStatCache = new ConcurrentDictionary<long, ConcurrentDictionary<string, CachedMachine>>();
_machineFilesCache = new ConcurrentDictionary<ulong, ConcurrentDictionary<string, CachedFile>>();
_machineDisksCache = new ConcurrentDictionary<ulong, ConcurrentDictionary<string, CachedDisk>>();
_streamsCache = new ConcurrentDictionary<long, Stream>();
_lastHandle = 0;
}
@@ -251,6 +253,39 @@ namespace RomRepoMgr.Core.Filesystem
return cachedMachineFiles;
}
internal ConcurrentDictionary<string, CachedDisk> GetDisksFromMachine(ulong id)
{
_machineDisksCache.TryGetValue(id, out ConcurrentDictionary<string, CachedDisk> cachedMachineDisks);
if(cachedMachineDisks != null)
return cachedMachineDisks;
using var ctx = Context.Create(Settings.Settings.Current.DatabasePath);
cachedMachineDisks = new ConcurrentDictionary<string, CachedDisk>();
foreach(DiskByMachine machineDisk in ctx.DisksByMachines.Where(dbm => dbm.Machine.Id == id &&
dbm.Disk.IsInRepo &&
dbm.Disk.Size != null))
{
var cachedDisk = new CachedDisk
{
Id = machineDisk.Disk.Id,
Md5 = machineDisk.Disk.Md5,
Sha1 = machineDisk.Disk.Sha1,
Size = machineDisk.Disk.Size ?? 0,
CreatedOn = machineDisk.Disk.CreatedOn,
UpdatedOn = machineDisk.Disk.UpdatedOn
};
cachedMachineDisks[machineDisk.Name] = cachedDisk;
}
_machineDisksCache[id] = cachedMachineDisks;
return cachedMachineDisks;
}
internal CachedFile GetFile(ulong machineId, string name)
{
ConcurrentDictionary<string, CachedFile> cachedFiles = GetFilesFromMachine(machineId);
@@ -262,6 +297,20 @@ namespace RomRepoMgr.Core.Filesystem
return file;
}
internal CachedDisk GetDisk(ulong machineId, string name)
{
if(name.EndsWith(".chd", StringComparison.OrdinalIgnoreCase))
name = name.Substring(0, name.Length - 4);
ConcurrentDictionary<string, CachedDisk> cachedDisks = GetDisksFromMachine(machineId);
if(cachedDisks == null ||
!cachedDisks.TryGetValue(name, out CachedDisk disk))
return null;
return disk;
}
internal long Open(string sha384, long fileSize)
{
byte[] sha384Bytes = new byte[48];
@@ -338,6 +387,100 @@ namespace RomRepoMgr.Core.Filesystem
return _rootDirectoryCache.Keys.ToArray();
}
public long OpenDisk(string sha1, string md5)
{
if(sha1 == null &&
md5 == null)
return -1;
string repoPath = null;
string md5Path = null;
string sha1Path = null;
if(sha1 != null)
{
byte[] sha1Bytes = new byte[20];
for(int i = 0; i < 20; i++)
{
if(sha1[i * 2] >= 0x30 &&
sha1[i * 2] <= 0x39)
sha1Bytes[i] = (byte)((sha1[i * 2] - 0x30) * 0x10);
else if(sha1[i * 2] >= 0x41 &&
sha1[i * 2] <= 0x46)
sha1Bytes[i] = (byte)((sha1[i * 2] - 0x37) * 0x10);
else if(sha1[i * 2] >= 0x61 &&
sha1[i * 2] <= 0x66)
sha1Bytes[i] = (byte)((sha1[i * 2] - 0x57) * 0x10);
if(sha1[(i * 2) + 1] >= 0x30 &&
sha1[(i * 2) + 1] <= 0x39)
sha1Bytes[i] += (byte)(sha1[(i * 2) + 1] - 0x30);
else if(sha1[(i * 2) + 1] >= 0x41 &&
sha1[(i * 2) + 1] <= 0x46)
sha1Bytes[i] += (byte)(sha1[(i * 2) + 1] - 0x37);
else if(sha1[(i * 2) + 1] >= 0x61 &&
sha1[(i * 2) + 1] <= 0x66)
sha1Bytes[i] += (byte)(sha1[(i * 2) + 1] - 0x57);
}
string sha1B32 = Base32.ToBase32String(sha1Bytes);
sha1Path = Path.Combine(Settings.Settings.Current.RepositoryPath, "chd", "sha1", sha1B32[0].ToString(),
sha1B32[1].ToString(), sha1B32[2].ToString(), sha1B32[3].ToString(),
sha1B32[4].ToString(), sha1B32 + ".chd");
}
if(md5 != null)
{
byte[] md5Bytes = new byte[16];
for(int i = 0; i < 16; i++)
{
if(md5[i * 2] >= 0x30 &&
md5[i * 2] <= 0x39)
md5Bytes[i] = (byte)((md5[i * 2] - 0x30) * 0x10);
else if(md5[i * 2] >= 0x41 &&
md5[i * 2] <= 0x46)
md5Bytes[i] = (byte)((md5[i * 2] - 0x37) * 0x10);
else if(md5[i * 2] >= 0x61 &&
md5[i * 2] <= 0x66)
md5Bytes[i] = (byte)((md5[i * 2] - 0x57) * 0x10);
if(md5[(i * 2) + 1] >= 0x30 &&
md5[(i * 2) + 1] <= 0x39)
md5Bytes[i] += (byte)(md5[(i * 2) + 1] - 0x30);
else if(md5[(i * 2) + 1] >= 0x41 &&
md5[(i * 2) + 1] <= 0x46)
md5Bytes[i] += (byte)(md5[(i * 2) + 1] - 0x37);
else if(md5[(i * 2) + 1] >= 0x61 &&
md5[(i * 2) + 1] <= 0x66)
md5Bytes[i] += (byte)(md5[(i * 2) + 1] - 0x57);
}
string md5B32 = Base32.ToBase32String(md5Bytes);
md5Path = Path.Combine(Settings.Settings.Current.RepositoryPath, "chd", "md5", md5B32[0].ToString(),
md5B32[1].ToString(), md5B32[2].ToString(), md5B32[3].ToString(),
md5B32[4].ToString(), md5B32 + ".chd");
}
if(File.Exists(sha1Path))
repoPath = sha1Path;
else if(File.Exists(md5Path))
repoPath = md5Path;
if(repoPath == null)
return -1;
_lastHandle++;
long handle = _lastHandle;
_streamsCache[handle] = Stream.Synchronized(new FileStream(repoPath, FileMode.Open, FileAccess.Read));
return handle;
}
}
internal sealed class CachedMachine
@@ -360,4 +503,14 @@ namespace RomRepoMgr.Core.Filesystem
public DateTime CreatedOn { get; set; }
public DateTime UpdatedOn { get; set; }
}
internal sealed class CachedDisk
{
public ulong Id { get; set; }
public ulong Size { get; set; }
public string Md5 { get; set; }
public string Sha1 { get; set; }
public DateTime CreatedOn { get; set; }
public DateTime UpdatedOn { get; set; }
}
}

View File

@@ -247,18 +247,62 @@ namespace RomRepoMgr.Core.Filesystem
return STATUS_SUCCESS;
}
CachedFile file = _vfs.GetFile(machine.Id, pieces[2]);
long handle = 0;
CachedFile file = _vfs.GetFile(machine.Id, pieces[2]);
if(file == null)
if(file != null)
{
if(pieces.Length > 3)
return STATUS_INVALID_DEVICE_REQUEST;
if(file.Sha384 == null)
return STATUS_OBJECT_NAME_NOT_FOUND;
handle = _vfs.Open(file.Sha384, (long)file.Size);
if(handle <= 0)
return STATUS_OBJECT_NAME_NOT_FOUND;
normalizedName = Path.GetFileName(fileName);
// TODO: Real allocation size
fileInfo = new FileInfo
{
ChangeTime = (ulong)file.UpdatedOn.ToFileTimeUtc(),
AllocationSize = (file.Size + 511) / 512,
FileSize = file.Size,
CreationTime = (ulong)file.CreatedOn.ToFileTimeUtc(),
FileAttributes =
(uint)(FileAttributes.Normal | FileAttributes.Compressed | FileAttributes.ReadOnly),
IndexNumber = file.Id,
LastAccessTime = (ulong)DateTime.UtcNow.ToFileTimeUtc(),
LastWriteTime = (ulong)file.UpdatedOn.ToFileTimeUtc()
};
fileNode = new FileNode
{
FileName = normalizedName,
Info = fileInfo,
Path = fileName,
Handle = handle
};
return STATUS_SUCCESS;
}
CachedDisk disk = _vfs.GetDisk(machine.Id, pieces[2]);
if(disk == null)
return STATUS_OBJECT_NAME_NOT_FOUND;
if(pieces.Length > 3)
return STATUS_INVALID_DEVICE_REQUEST;
if(file.Sha384 == null)
if(disk.Sha1 == null &&
disk.Md5 == null)
return STATUS_OBJECT_NAME_NOT_FOUND;
long handle = _vfs.Open(file.Sha384, (long)file.Size);
handle = _vfs.OpenDisk(disk.Sha1, disk.Md5);
if(handle <= 0)
return STATUS_OBJECT_NAME_NOT_FOUND;
@@ -268,14 +312,14 @@ namespace RomRepoMgr.Core.Filesystem
// TODO: Real allocation size
fileInfo = new FileInfo
{
ChangeTime = (ulong)file.UpdatedOn.ToFileTimeUtc(),
AllocationSize = (file.Size + 511) / 512,
FileSize = file.Size,
CreationTime = (ulong)file.CreatedOn.ToFileTimeUtc(),
ChangeTime = (ulong)disk.UpdatedOn.ToFileTimeUtc(),
AllocationSize = (disk.Size + 511) / 512,
FileSize = disk.Size,
CreationTime = (ulong)disk.CreatedOn.ToFileTimeUtc(),
FileAttributes = (uint)(FileAttributes.Normal | FileAttributes.Compressed | FileAttributes.ReadOnly),
IndexNumber = file.Id,
IndexNumber = disk.Id,
LastAccessTime = (ulong)DateTime.UtcNow.ToFileTimeUtc(),
LastWriteTime = (ulong)file.UpdatedOn.ToFileTimeUtc()
LastWriteTime = (ulong)disk.UpdatedOn.ToFileTimeUtc()
};
fileNode = new FileNode
@@ -352,6 +396,9 @@ namespace RomRepoMgr.Core.Filesystem
ConcurrentDictionary<string, CachedFile> cachedMachineFiles =
_vfs.GetFilesFromMachine(node.MachineId);
ConcurrentDictionary<string, CachedDisk> cachedMachineDisks =
_vfs.GetDisksFromMachine(node.MachineId);
node.Children = new List<FileEntry>
{
new FileEntry
@@ -382,6 +429,23 @@ namespace RomRepoMgr.Core.Filesystem
LastWriteTime = (ulong)file.Value.UpdatedOn.ToFileTimeUtc()
}
}));
node.Children.AddRange(cachedMachineDisks.Select(disk => new FileEntry
{
FileName = disk.Key + ".chd",
Info = new FileInfo
{
ChangeTime = (ulong)disk.Value.UpdatedOn.ToFileTimeUtc(),
AllocationSize = (disk.Value.Size + 511) / 512,
FileSize = disk.Value.Size,
CreationTime = (ulong)disk.Value.CreatedOn.ToFileTimeUtc(),
FileAttributes =
(uint)(FileAttributes.Normal | FileAttributes.Compressed | FileAttributes.ReadOnly),
IndexNumber = disk.Value.Id,
LastAccessTime = (ulong)DateTime.UtcNow.ToFileTimeUtc(),
LastWriteTime = (ulong)disk.Value.UpdatedOn.ToFileTimeUtc()
}
}));
}
else if(node.RomSetId > 0)
{
@@ -525,18 +589,40 @@ namespace RomRepoMgr.Core.Filesystem
return STATUS_SUCCESS;
}
CachedFile file = _vfs.GetFile(machine.Id, pieces[2]);
long handle = 0;
CachedFile file = _vfs.GetFile(machine.Id, pieces[2]);
if(file == null)
if(file != null)
{
if(pieces.Length > 3)
return STATUS_INVALID_DEVICE_REQUEST;
if(file.Sha384 == null)
return STATUS_OBJECT_NAME_NOT_FOUND;
handle = _vfs.Open(file.Sha384, (long)file.Size);
if(handle <= 0)
return STATUS_OBJECT_NAME_NOT_FOUND;
fileAttributes = (uint)(FileAttributes.Normal | FileAttributes.Compressed | FileAttributes.ReadOnly);
return STATUS_SUCCESS;
}
CachedDisk disk = _vfs.GetDisk(machine.Id, pieces[2]);
if(disk == null)
return STATUS_OBJECT_NAME_NOT_FOUND;
if(pieces.Length > 3)
return STATUS_INVALID_DEVICE_REQUEST;
if(file.Sha384 == null)
if(disk.Sha1 == null &&
disk.Md5 == null)
return STATUS_OBJECT_NAME_NOT_FOUND;
long handle = _vfs.Open(file.Sha384, (long)file.Size);
handle = _vfs.OpenDisk(disk.Sha1, disk.Md5);
if(handle <= 0)
return STATUS_OBJECT_NAME_NOT_FOUND;