Files
romrepomgr/RomRepoMgr.Core/Filesystem/Fuse.cs

832 lines
26 KiB
C#
Raw Normal View History

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
2020-12-21 01:37:00 +00:00
using System.Runtime.Versioning;
using System.Text;
using Mono.Fuse.NETStandard;
using Mono.Unix.Native;
using RomRepoMgr.Database.Models;
2024-11-09 01:37:59 +00:00
namespace RomRepoMgr.Core.Filesystem;
// TODO: Last handle goes negative
[SupportedOSPlatform("Linux")]
[SupportedOSPlatform("macOS")]
public sealed class Fuse : FileSystem
{
2024-11-09 01:37:59 +00:00
readonly ConcurrentDictionary<long, List<DirectoryEntry>> _directoryCache;
readonly ConcurrentDictionary<long, Stat> _fileStatHandleCache;
readonly Vfs _vfs;
long _lastHandle;
string _umountToken;
2024-11-09 01:37:59 +00:00
public Fuse(Vfs vfs)
{
2025-07-08 23:10:49 +01:00
_directoryCache = [];
2024-11-09 01:37:59 +00:00
_lastHandle = 0;
2025-07-08 23:10:49 +01:00
_fileStatHandleCache = [];
2025-07-08 01:07:19 +01:00
Name = "romrepomgrfs";
2024-11-09 01:37:59 +00:00
_vfs = vfs;
}
2024-11-09 01:37:59 +00:00
public static bool IsAvailable
{
get
{
2024-11-09 01:37:59 +00:00
try
{
2024-11-09 01:37:59 +00:00
IntPtr fuse = dlopen("libfuse.so.2", 2);
2024-11-09 01:37:59 +00:00
if(fuse == IntPtr.Zero) return false;
2024-11-09 01:37:59 +00:00
dlclose(fuse);
2024-11-09 01:37:59 +00:00
IntPtr helper = dlopen("libMonoFuseHelper.so", 2);
2024-11-09 01:37:59 +00:00
if(helper == IntPtr.Zero)
{
helper = dlopen("./libMonoFuseHelper.so", 2);
2024-11-09 01:37:59 +00:00
if(helper == IntPtr.Zero) return false;
}
2024-11-09 01:37:59 +00:00
dlclose(helper);
2024-11-09 01:37:59 +00:00
return true;
}
catch(Exception e)
{
return false;
}
}
2024-11-09 01:37:59 +00:00
}
2024-11-09 01:37:59 +00:00
[DllImport("libdl")]
static extern IntPtr dlopen(string filename, int flags);
2024-11-09 01:37:59 +00:00
[DllImport("libdl")]
static extern int dlclose(IntPtr handle);
2024-11-09 01:37:59 +00:00
protected override Errno OnGetPathStatus(string path, out Stat stat)
{
stat = new Stat();
2024-11-09 01:37:59 +00:00
string[] pieces = _vfs.SplitPath(path);
2020-09-01 11:54:16 +01:00
2024-11-09 01:37:59 +00:00
if(pieces.Length == 0)
{
stat.st_mode = FilePermissions.S_IFDIR | NativeConvert.FromOctalPermissionString("0555");
stat.st_nlink = 2;
2020-09-01 11:54:16 +01:00
2024-11-09 01:37:59 +00:00
return 0;
}
2024-11-09 01:37:59 +00:00
long romSetId = _vfs.GetRomSetId(pieces[0]);
2024-11-09 01:37:59 +00:00
if(romSetId <= 0)
{
if(pieces[0] != ".fuse_umount" || _umountToken == null) return Errno.ENOENT;
2024-11-09 01:37:59 +00:00
stat = new Stat
{
2024-11-09 01:37:59 +00:00
st_mode = FilePermissions.S_IFREG | NativeConvert.FromOctalPermissionString("0444"),
st_nlink = 1,
st_ctime = NativeConvert.ToTimeT(DateTime.UtcNow),
st_mtime = NativeConvert.ToTimeT(DateTime.UtcNow),
st_blksize = 0,
st_blocks = 0,
st_ino = 0,
st_size = 0
};
2024-11-09 01:37:59 +00:00
return 0;
}
2024-11-09 01:37:59 +00:00
RomSet romSet = _vfs.GetRomSet(romSetId);
2024-11-09 01:37:59 +00:00
if(romSet == null) return Errno.ENOENT;
2024-11-09 01:37:59 +00:00
if(pieces.Length == 1)
{
stat.st_mode = FilePermissions.S_IFDIR | NativeConvert.FromOctalPermissionString("0555");
stat.st_nlink = 2;
stat.st_ctime = NativeConvert.ToTimeT(romSet.CreatedOn.ToUniversalTime());
stat.st_mtime = NativeConvert.ToTimeT(romSet.UpdatedOn.ToUniversalTime());
2024-11-09 01:37:59 +00:00
return 0;
}
2024-11-09 01:37:59 +00:00
CachedMachine machine = _vfs.GetMachine(romSetId, pieces[1]);
2024-11-09 01:37:59 +00:00
if(machine == null) return Errno.ENOENT;
if(pieces.Length == 2)
{
stat = new Stat
{
2024-11-09 01:37:59 +00:00
st_mode = FilePermissions.S_IFDIR | NativeConvert.FromOctalPermissionString("0555"),
st_nlink = 2,
st_ctime = NativeConvert.ToTimeT(machine.CreationDate.ToUniversalTime()),
st_mtime = NativeConvert.ToTimeT(machine.ModificationDate.ToUniversalTime())
};
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
return 0;
}
2024-11-09 01:37:59 +00:00
CachedFile file = _vfs.GetFile(machine.Id, pieces[2]);
2024-11-09 01:37:59 +00:00
if(file != null)
{
if(pieces.Length != 3) return Errno.ENOSYS;
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
stat = new Stat
{
2024-11-09 01:37:59 +00:00
st_mode = FilePermissions.S_IFREG | NativeConvert.FromOctalPermissionString("0444"),
st_nlink = 1,
st_ctime = NativeConvert.ToTimeT(file.CreatedOn.ToUniversalTime()),
st_mtime =
NativeConvert.ToTimeT(file.FileLastModification?.ToUniversalTime() ??
file.UpdatedOn.ToUniversalTime()),
st_blksize = 512,
st_blocks = (long)(file.Size / 512),
st_ino = file.Id,
st_size = (long)file.Size
};
2024-11-09 01:37:59 +00:00
return 0;
}
2024-11-09 01:37:59 +00:00
CachedDisk disk = _vfs.GetDisk(machine.Id, pieces[2]);
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
if(disk != null)
{
if(pieces.Length != 3) return Errno.ENOSYS;
2020-09-04 20:38:43 +01:00
stat = new Stat
{
st_mode = FilePermissions.S_IFREG | NativeConvert.FromOctalPermissionString("0444"),
st_nlink = 1,
2024-11-09 01:37:59 +00:00
st_ctime = NativeConvert.ToTimeT(disk.CreatedOn.ToUniversalTime()),
st_mtime = NativeConvert.ToTimeT(disk.UpdatedOn.ToUniversalTime()),
2020-09-04 20:38:43 +01:00
st_blksize = 512,
2024-11-09 01:37:59 +00:00
st_blocks = (long)(disk.Size / 512),
st_ino = disk.Id,
st_size = (long)disk.Size
2020-09-04 20:38:43 +01:00
};
return 0;
}
2024-11-09 01:37:59 +00:00
CachedMedia media = _vfs.GetMedia(machine.Id, pieces[2]);
if(media == null) return Errno.ENOENT;
if(pieces.Length != 3) return Errno.ENOSYS;
stat = new Stat
{
2024-11-09 01:37:59 +00:00
st_mode = FilePermissions.S_IFREG | NativeConvert.FromOctalPermissionString("0444"),
st_nlink = 1,
st_ctime = NativeConvert.ToTimeT(media.CreatedOn.ToUniversalTime()),
st_mtime = NativeConvert.ToTimeT(media.UpdatedOn.ToUniversalTime()),
st_blksize = 512,
st_blocks = (long)(media.Size / 512),
st_ino = media.Id,
st_size = (long)media.Size
};
return 0;
}
2024-11-09 01:37:59 +00:00
protected override Errno OnReadSymbolicLink(string link, out string target)
{
target = null;
2024-11-09 01:37:59 +00:00
return Errno.EOPNOTSUPP;
}
2024-11-09 01:37:59 +00:00
protected override Errno OnCreateSpecialFile(string file, FilePermissions perms, ulong dev) => Errno.EROFS;
2024-11-09 01:37:59 +00:00
protected override Errno OnCreateDirectory(string directory, FilePermissions mode) => Errno.EROFS;
2024-11-09 01:37:59 +00:00
protected override Errno OnRemoveFile(string file) => Errno.EROFS;
2024-11-09 01:37:59 +00:00
protected override Errno OnRemoveDirectory(string directory) => Errno.EROFS;
2024-11-09 01:37:59 +00:00
protected override Errno OnCreateSymbolicLink(string target, string link) => Errno.EROFS;
2024-11-09 01:37:59 +00:00
protected override Errno OnRenamePath(string oldPath, string newPath) => Errno.EROFS;
2024-11-09 01:37:59 +00:00
protected override Errno OnCreateHardLink(string oldPath, string link) => Errno.EROFS;
2024-11-09 01:37:59 +00:00
protected override Errno OnChangePathPermissions(string path, FilePermissions mode) => Errno.EROFS;
2024-11-09 01:37:59 +00:00
protected override Errno OnChangePathOwner(string path, long owner, long group) => Errno.EROFS;
2024-11-09 01:37:59 +00:00
protected override Errno OnTruncateFile(string file, long length) => Errno.EROFS;
2024-11-09 01:37:59 +00:00
protected override Errno OnChangePathTimes(string path, ref Utimbuf buf) => Errno.EROFS;
2024-11-09 01:37:59 +00:00
protected override Errno OnOpenHandle(string path, OpenedPathInfo info)
{
string[] pieces = _vfs.SplitPath(path);
2024-11-09 01:37:59 +00:00
if(pieces.Length == 0) return Errno.EISDIR;
2024-11-09 01:37:59 +00:00
long romSetId = _vfs.GetRomSetId(pieces[0]);
2024-11-09 01:37:59 +00:00
if(romSetId <= 0) return Errno.ENOENT;
2024-11-09 01:37:59 +00:00
RomSet romSet = _vfs.GetRomSet(romSetId);
2024-11-09 01:37:59 +00:00
if(romSet == null) return Errno.ENOENT;
2024-11-09 01:37:59 +00:00
if(pieces.Length == 1) return Errno.EISDIR;
2024-11-09 01:37:59 +00:00
CachedMachine machine = _vfs.GetMachine(romSetId, pieces[1]);
2024-11-09 01:37:59 +00:00
if(machine == null) return Errno.ENOENT;
if(pieces.Length == 2) return Errno.EISDIR;
long handle = 0;
Stat stat;
CachedFile file = _vfs.GetFile(machine.Id, pieces[2]);
if(file != null)
{
if(pieces.Length > 3) return Errno.ENOSYS;
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;
2024-11-09 01:37:59 +00:00
handle = _vfs.Open(file.Sha384, (long)file.Size);
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
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.FileLastModification?.ToUniversalTime() ??
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]);
2024-11-09 01:37:59 +00:00
if(disk != null)
2020-09-04 20:38:43 +01:00
{
2024-11-09 01:37:59 +00:00
if(pieces.Length > 3) return Errno.ENOSYS;
2024-11-09 01:37:59 +00:00
if(disk.Sha1 == null && disk.Md5 == null) return Errno.ENOENT;
2020-09-04 20:38:43 +01:00
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;
2024-11-09 01:37:59 +00:00
handle = _vfs.OpenDisk(disk.Sha1, disk.Md5);
2020-09-04 20:38:43 +01:00
stat = new Stat
{
st_mode = FilePermissions.S_IFREG | NativeConvert.FromOctalPermissionString("0444"),
st_nlink = 1,
2024-11-09 01:37:59 +00:00
st_ctime = NativeConvert.ToTimeT(disk.CreatedOn.ToUniversalTime()),
st_mtime = NativeConvert.ToTimeT(disk.UpdatedOn.ToUniversalTime()),
2020-09-04 20:38:43 +01:00
st_blksize = 512,
2024-11-09 01:37:59 +00:00
st_blocks = (long)(disk.Size / 512),
st_ino = disk.Id,
st_size = (long)disk.Size
2020-09-04 20:38:43 +01:00
};
}
else
{
2024-11-09 01:37:59 +00:00
CachedMedia media = _vfs.GetMedia(machine.Id, pieces[2]);
2024-11-09 01:37:59 +00:00
if(media == null) return Errno.ENOENT;
if(pieces.Length > 3) return Errno.ENOSYS;
2024-11-09 01:37:59 +00:00
if(media.Sha256 == null && media.Sha1 == null && media.Md5 == null) return Errno.ENOENT;
2024-11-09 01:37:59 +00:00
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;
2024-11-09 01:37:59 +00:00
handle = _vfs.OpenMedia(media.Sha256, media.Sha1, media.Md5);
2024-11-09 01:37:59 +00:00
stat = new Stat
{
st_mode = FilePermissions.S_IFREG | NativeConvert.FromOctalPermissionString("0444"),
st_nlink = 1,
st_ctime = NativeConvert.ToTimeT(media.CreatedOn.ToUniversalTime()),
st_mtime = NativeConvert.ToTimeT(media.UpdatedOn.ToUniversalTime()),
st_blksize = 512,
st_blocks = (long)(media.Size / 512),
st_ino = media.Id,
st_size = (long)media.Size
};
}
}
2024-11-09 01:37:59 +00:00
if(handle <= 0) return Errno.ENOENT;
2024-11-09 01:37:59 +00:00
info.Handle = new IntPtr(handle);
2024-11-09 01:37:59 +00:00
_fileStatHandleCache[handle] = stat;
2024-11-09 01:37:59 +00:00
return 0;
}
2024-11-09 01:37:59 +00:00
protected override Errno OnReadHandle(string file, OpenedPathInfo info, byte[] buf, long offset,
out int bytesWritten)
{
bytesWritten = _vfs.Read(info.Handle.ToInt64(), buf, offset);
2024-11-09 01:37:59 +00:00
if(bytesWritten >= 0) return 0;
2024-11-09 01:37:59 +00:00
bytesWritten = 0;
2024-11-09 01:37:59 +00:00
return Errno.EBADF;
}
2024-11-09 01:37:59 +00:00
protected override Errno OnWriteHandle(string file, OpenedPathInfo info, byte[] buf, long offset, out int bytesRead)
{
bytesRead = 0;
2024-11-09 01:37:59 +00:00
return Errno.EROFS;
}
protected override Errno OnGetFileSystemStatus(string path, out Statvfs buf)
{
_vfs.GetInfo(out ulong files, out ulong totalSize);
2024-11-09 01:37:59 +00:00
buf = new Statvfs
{
2024-11-09 01:37:59 +00:00
f_bsize = 512,
f_frsize = 512,
f_blocks = totalSize / 512,
f_bavail = 0,
f_files = files,
f_ffree = 0,
f_favail = 0,
f_fsid = 0xFFFFFFFF,
f_flag = 0,
f_namemax = 255
};
return 0;
}
2024-11-09 01:37:59 +00:00
protected override Errno OnFlushHandle(string file, OpenedPathInfo info) => Errno.ENOSYS;
2024-11-09 01:37:59 +00:00
protected override Errno OnReleaseHandle(string file, OpenedPathInfo info)
{
if(!_vfs.Close(info.Handle.ToInt64())) return Errno.EBADF;
2024-11-09 01:37:59 +00:00
_fileStatHandleCache.TryRemove(info.Handle.ToInt64(), out _);
2024-11-09 01:37:59 +00:00
return 0;
}
2020-09-01 11:54:16 +01:00
2024-11-09 01:37:59 +00:00
protected override Errno OnSynchronizeHandle(string file, OpenedPathInfo info, bool onlyUserData) =>
Errno.EOPNOTSUPP;
2020-09-01 11:54:16 +01:00
2024-11-09 01:37:59 +00:00
protected override Errno OnSetPathExtendedAttribute(string path, string name, byte[] value, XattrFlags flags)
{
if(_umountToken == null) return Errno.EROFS;
2020-09-01 11:54:16 +01:00
2024-11-09 01:37:59 +00:00
if(path != "/.fuse_umount") return Errno.EROFS;
2020-09-01 11:54:16 +01:00
2024-11-09 01:37:59 +00:00
if(name != _umountToken) return Errno.EROFS;
2020-09-01 11:54:16 +01:00
2024-11-09 01:37:59 +00:00
if(value?.Length != 0) return Errno.EROFS;
2024-11-09 01:37:59 +00:00
_umountToken = null;
Stop();
return 0;
}
2024-11-09 01:37:59 +00:00
protected override Errno OnGetPathExtendedAttribute(string path, string name, byte[] value, out int bytesWritten)
{
bytesWritten = 0;
2024-11-09 01:37:59 +00:00
string[] pieces = _vfs.SplitPath(path);
2024-11-09 01:37:59 +00:00
if(pieces.Length == 0) return Errno.ENODATA;
2024-11-09 01:37:59 +00:00
long romSetId = _vfs.GetRomSetId(pieces[0]);
2024-11-09 01:37:59 +00:00
if(romSetId <= 0) return Errno.ENOENT;
2024-11-09 01:37:59 +00:00
RomSet romSet = _vfs.GetRomSet(romSetId);
2024-11-09 01:37:59 +00:00
if(romSet == null) return Errno.ENOENT;
2024-11-09 01:37:59 +00:00
if(pieces.Length == 1) return Errno.ENODATA;
2024-11-09 01:37:59 +00:00
CachedMachine machine = _vfs.GetMachine(romSetId, pieces[1]);
2024-11-09 01:37:59 +00:00
if(machine == null) return Errno.ENOENT;
2024-11-09 01:37:59 +00:00
if(pieces.Length == 2) return Errno.ENODATA;
2024-11-09 01:37:59 +00:00
CachedFile file = _vfs.GetFile(machine.Id, pieces[2]);
2024-11-09 01:37:59 +00:00
string hash = null;
if(file != null)
{
if(pieces.Length > 3) return Errno.ENOSYS;
2024-11-12 06:50:41 +00:00
hash = name switch
{
"user.crc32" => file.Crc32,
"user.md5" => file.Md5,
"user.sha1" => file.Sha1,
"user.sha256" => file.Sha256,
"user.sha384" => file.Sha384,
"user.sha512" => file.Sha512,
_ => hash
};
2024-11-09 01:37:59 +00:00
}
else
{
CachedDisk disk = _vfs.GetDisk(machine.Id, pieces[2]);
if(disk != null)
{
2024-11-12 06:50:41 +00:00
hash = name switch
{
"user.md5" => disk.Md5,
"user.sha1" => disk.Sha1,
_ => hash
};
2020-09-04 20:38:43 +01:00
}
else
{
2024-11-09 01:37:59 +00:00
CachedMedia media = _vfs.GetMedia(machine.Id, pieces[2]);
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
if(media == null) return Errno.ENOENT;
2020-09-04 20:38:43 +01:00
2024-11-12 06:50:41 +00:00
hash = name switch
{
"user.md5" => media.Md5,
"user.sha1" => media.Sha1,
"user.sha256" => media.Sha256,
"user.spamsum" => media.SpamSum,
_ => hash
};
}
2024-11-09 01:37:59 +00:00
}
2024-11-09 01:37:59 +00:00
if(hash == null) return Errno.ENODATA;
2024-11-09 01:37:59 +00:00
byte[] xattr = null;
2024-11-09 01:37:59 +00:00
if(name == "user.spamsum")
xattr = Encoding.ASCII.GetBytes(hash);
else
{
xattr = new byte[hash.Length / 2];
2025-07-27 17:32:31 +01:00
for(int i = 0; i < xattr.Length; i++)
{
2024-11-09 01:37:59 +00:00
if(hash[i * 2] >= 0x30 && hash[i * 2] <= 0x39)
xattr[i] = (byte)((hash[i * 2] - 0x30) * 0x10);
else if(hash[i * 2] >= 0x41 && hash[i * 2] <= 0x46)
xattr[i] = (byte)((hash[i * 2] - 0x37) * 0x10);
else if(hash[i * 2] >= 0x61 && hash[i * 2] <= 0x66) xattr[i] = (byte)((hash[i * 2] - 0x57) * 0x10);
if(hash[i * 2 + 1] >= 0x30 && hash[i * 2 + 1] <= 0x39)
xattr[i] += (byte)(hash[i * 2 + 1] - 0x30);
else if(hash[i * 2 + 1] >= 0x41 && hash[i * 2 + 1] <= 0x46)
xattr[i] += (byte)(hash[i * 2 + 1] - 0x37);
else if(hash[i * 2 + 1] >= 0x61 && hash[i * 2 + 1] <= 0x66) xattr[i] += (byte)(hash[i * 2 + 1] - 0x57);
}
2024-11-09 01:37:59 +00:00
}
2024-11-09 01:37:59 +00:00
if(value == null)
{
bytesWritten = xattr.Length;
return 0;
}
2024-11-09 01:37:59 +00:00
int maxSize = value.Length > xattr.Length ? xattr.Length : value.Length;
2024-11-09 01:37:59 +00:00
Array.Copy(xattr, 0, value, 0, maxSize);
bytesWritten = maxSize;
2024-11-09 01:37:59 +00:00
return 0;
}
2024-11-09 01:37:59 +00:00
protected override Errno OnListPathExtendedAttributes(string path, out string[] names)
{
names = null;
2024-11-09 01:37:59 +00:00
string[] pieces = _vfs.SplitPath(path);
2024-11-09 01:37:59 +00:00
if(pieces.Length == 0) return 0;
2024-11-09 01:37:59 +00:00
long romSetId = _vfs.GetRomSetId(pieces[0]);
2024-11-09 01:37:59 +00:00
if(romSetId <= 0) return Errno.ENOENT;
2024-11-09 01:37:59 +00:00
RomSet romSet = _vfs.GetRomSet(romSetId);
2024-11-09 01:37:59 +00:00
if(romSet == null) return Errno.ENOENT;
2024-11-09 01:37:59 +00:00
if(pieces.Length == 1) return 0;
2024-11-09 01:37:59 +00:00
CachedMachine machine = _vfs.GetMachine(romSetId, pieces[1]);
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
if(machine == null) return Errno.ENOENT;
2024-11-09 01:37:59 +00:00
if(pieces.Length == 2) return 0;
2024-11-09 01:37:59 +00:00
var xattrs = new List<string>();
2024-11-09 01:37:59 +00:00
CachedFile file = _vfs.GetFile(machine.Id, pieces[2]);
2024-11-09 01:37:59 +00:00
if(file != null)
{
if(pieces.Length > 3) return Errno.ENOSYS;
2024-11-09 01:37:59 +00:00
if(file.Crc32 != null) xattrs.Add("user.crc32");
2024-11-09 01:37:59 +00:00
if(file.Md5 != null) xattrs.Add("user.md5");
2024-11-09 01:37:59 +00:00
if(file.Sha1 != null) xattrs.Add("user.sha1");
2024-11-09 01:37:59 +00:00
if(file.Sha256 != null) xattrs.Add("user.sha256");
2024-11-09 01:37:59 +00:00
if(file.Sha384 != null) xattrs.Add("user.sha384");
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
if(file.Sha512 != null) xattrs.Add("user.sha512");
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
names = xattrs.ToArray();
2024-11-09 01:37:59 +00:00
return 0;
}
2024-11-09 01:37:59 +00:00
CachedDisk disk = _vfs.GetDisk(machine.Id, pieces[2]);
2024-11-09 01:37:59 +00:00
if(disk != null)
{
if(pieces.Length > 3) return Errno.ENOSYS;
2024-11-09 01:37:59 +00:00
if(disk.Md5 != null) xattrs.Add("user.md5");
2024-11-09 01:37:59 +00:00
if(disk.Sha1 != null) xattrs.Add("user.sha1");
2024-11-09 01:37:59 +00:00
names = xattrs.ToArray();
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
return 0;
}
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
CachedMedia media = _vfs.GetMedia(machine.Id, pieces[2]);
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
if(media == null) return Errno.ENOENT;
2024-11-09 01:37:59 +00:00
if(pieces.Length > 3) return Errno.ENOSYS;
2024-11-09 01:37:59 +00:00
if(media.Md5 != null) xattrs.Add("user.md5");
2024-11-09 01:37:59 +00:00
if(media.Sha1 != null) xattrs.Add("user.sha1");
2024-11-09 01:37:59 +00:00
if(media.Sha256 != null) xattrs.Add("user.sha256");
2024-11-09 01:37:59 +00:00
if(media.SpamSum != null) xattrs.Add("user.spamsum");
2024-11-09 01:37:59 +00:00
names = xattrs.ToArray();
return 0;
}
protected override Errno OnRemovePathExtendedAttribute(string path, string name) => Errno.EROFS;
protected override Errno OnOpenDirectory(string directory, OpenedPathInfo info)
{
try
{
2024-11-09 01:37:59 +00:00
if(directory == "/")
{
2024-11-09 01:37:59 +00:00
var entries = new List<DirectoryEntry>
{
2024-11-09 01:37:59 +00:00
new("."),
new("..")
};
2024-11-09 01:37:59 +00:00
entries.AddRange(_vfs.GetRootEntries().Select(e => new DirectoryEntry(e)));
2024-11-09 01:37:59 +00:00
_lastHandle++;
info.Handle = new IntPtr(_lastHandle);
2024-11-09 01:37:59 +00:00
_directoryCache[_lastHandle] = entries;
2024-11-09 01:37:59 +00:00
return 0;
}
2024-11-09 01:37:59 +00:00
string[] pieces = directory.Split("/", StringSplitOptions.RemoveEmptyEntries);
2024-11-09 01:37:59 +00:00
if(pieces.Length == 0) return Errno.ENOENT;
2024-11-09 01:37:59 +00:00
long romSetId = _vfs.GetRomSetId(pieces[0]);
2024-11-09 01:37:59 +00:00
if(romSetId <= 0) return Errno.ENOENT;
2024-11-09 01:37:59 +00:00
RomSet romSet = _vfs.GetRomSet(romSetId);
2024-11-09 01:37:59 +00:00
if(romSet == null) return Errno.ENOENT;
2024-11-09 01:37:59 +00:00
ConcurrentDictionary<string, CachedMachine> machines = _vfs.GetMachinesFromRomSet(romSetId);
2024-11-09 01:37:59 +00:00
if(pieces.Length == 1)
{
var entries = new List<DirectoryEntry>
{
2024-11-09 01:37:59 +00:00
new("."),
new("..")
};
2024-11-09 01:37:59 +00:00
entries.AddRange(machines.Select(mach => new DirectoryEntry(mach.Key)));
2024-11-09 01:37:59 +00:00
_lastHandle++;
info.Handle = new IntPtr(_lastHandle);
2024-11-09 01:37:59 +00:00
_directoryCache[_lastHandle] = entries;
2024-11-09 01:37:59 +00:00
return 0;
}
2024-11-09 01:37:59 +00:00
CachedMachine machine = _vfs.GetMachine(romSetId, pieces[1]);
2024-11-09 01:37:59 +00:00
if(machine == null) return Errno.ENOENT;
2024-11-09 01:37:59 +00:00
ConcurrentDictionary<string, CachedFile> cachedMachineFiles = _vfs.GetFilesFromMachine(machine.Id);
ConcurrentDictionary<string, CachedDisk> cachedMachineDisks = _vfs.GetDisksFromMachine(machine.Id);
ConcurrentDictionary<string, CachedMedia> cachedMachineMedias = _vfs.GetMediasFromMachine(machine.Id);
2024-11-09 01:37:59 +00:00
if(pieces.Length == 2)
{
var entries = new List<DirectoryEntry>
{
2024-11-09 01:37:59 +00:00
new("."),
new("..")
};
2024-11-09 01:37:59 +00:00
entries.AddRange(cachedMachineFiles.Select(file => new DirectoryEntry(file.Key)));
entries.AddRange(cachedMachineDisks.Select(disk => new DirectoryEntry(disk.Key + ".chd")));
entries.AddRange(cachedMachineMedias.Select(media => new DirectoryEntry(media.Key + ".aif")));
2024-11-09 01:37:59 +00:00
_lastHandle++;
info.Handle = new IntPtr(_lastHandle);
2024-11-09 01:37:59 +00:00
_directoryCache[_lastHandle] = entries;
2024-11-09 01:37:59 +00:00
return 0;
}
2024-11-09 01:37:59 +00:00
// TODO: DATs with subfolders as game name
if(pieces.Length >= 3) return Errno.EISDIR;
2024-11-09 01:37:59 +00:00
return Errno.ENOENT;
}
catch(Exception e)
{
2024-11-09 01:37:59 +00:00
Console.WriteLine(e);
2024-11-09 01:37:59 +00:00
throw;
}
2024-11-09 01:37:59 +00:00
}
2024-11-09 01:37:59 +00:00
protected override Errno OnReadDirectory(string directory, OpenedPathInfo info,
out IEnumerable<DirectoryEntry> paths)
{
paths = null;
2024-11-09 01:37:59 +00:00
if(!_directoryCache.TryGetValue(info.Handle.ToInt64(), out List<DirectoryEntry> cache)) return Errno.EBADF;
2024-11-09 01:37:59 +00:00
paths = cache;
2024-11-09 01:37:59 +00:00
return 0;
}
2024-11-09 01:37:59 +00:00
protected override Errno OnReleaseDirectory(string directory, OpenedPathInfo info)
{
if(!_directoryCache.TryGetValue(info.Handle.ToInt64(), out _)) return Errno.EBADF;
2024-11-09 01:37:59 +00:00
_directoryCache.Remove(info.Handle.ToInt64(), out _);
2024-11-09 01:37:59 +00:00
return 0;
}
2024-11-09 01:37:59 +00:00
protected override Errno OnSynchronizeDirectory(string directory, OpenedPathInfo info, bool onlyUserData) =>
Errno.ENOSYS;
2024-11-09 01:37:59 +00:00
protected override Errno OnAccessPath(string path, AccessModes mode)
{
string[] pieces = _vfs.SplitPath(path);
2024-11-09 01:37:59 +00:00
if(pieces.Length == 0) return mode.HasFlag(AccessModes.W_OK) ? Errno.EROFS : 0;
2024-11-09 01:37:59 +00:00
long romSetId = _vfs.GetRomSetId(pieces[0]);
2024-11-09 01:37:59 +00:00
if(romSetId <= 0) return Errno.ENOENT;
2024-11-09 01:37:59 +00:00
RomSet romSet = _vfs.GetRomSet(romSetId);
2024-11-09 01:37:59 +00:00
if(romSet == null) return Errno.ENOENT;
2024-11-09 01:37:59 +00:00
if(pieces.Length == 1) return mode.HasFlag(AccessModes.W_OK) ? Errno.EROFS : 0;
2024-11-09 01:37:59 +00:00
CachedMachine machine = _vfs.GetMachine(romSetId, pieces[1]);
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
if(machine == null) return Errno.ENOENT;
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
if(pieces.Length == 2) return mode.HasFlag(AccessModes.W_OK) ? Errno.EROFS : 0;
2020-09-04 20:38:43 +01:00
2024-11-09 01:37:59 +00:00
CachedFile file = _vfs.GetFile(machine.Id, pieces[2]);
2024-11-09 01:37:59 +00:00
if(file != null)
{
if(pieces.Length > 3) return Errno.ENOSYS;
2024-11-09 01:37:59 +00:00
return mode.HasFlag(AccessModes.W_OK) ? Errno.EROFS : 0;
}
2024-11-09 01:37:59 +00:00
CachedDisk disk = _vfs.GetDisk(machine.Id, pieces[2]);
2024-11-09 01:37:59 +00:00
if(disk != null)
{
if(pieces.Length > 3) return Errno.ENOSYS;
return mode.HasFlag(AccessModes.W_OK) ? Errno.EROFS : 0;
}
2024-11-09 01:37:59 +00:00
CachedMedia media = _vfs.GetMedia(machine.Id, pieces[2]);
2024-11-09 01:37:59 +00:00
if(media == null) return Errno.ENOENT;
2024-11-09 01:37:59 +00:00
if(pieces.Length > 3) return Errno.ENOSYS;
2024-11-09 01:37:59 +00:00
return mode.HasFlag(AccessModes.W_OK) ? Errno.EROFS : 0;
}
2024-11-09 01:37:59 +00:00
protected override Errno OnCreateHandle(string file, OpenedPathInfo info, FilePermissions mode) => Errno.EROFS;
2024-11-09 01:37:59 +00:00
protected override Errno OnTruncateHandle(string file, OpenedPathInfo info, long length) => Errno.EROFS;
2024-11-09 01:37:59 +00:00
protected override Errno OnGetHandleStatus(string file, OpenedPathInfo info, out Stat buf)
{
buf = new Stat();
2024-11-09 01:37:59 +00:00
if(!_fileStatHandleCache.TryGetValue(info.Handle.ToInt64(), out Stat fileStat)) return Errno.EBADF;
2024-11-09 01:37:59 +00:00
buf = fileStat;
2024-11-09 01:37:59 +00:00
return 0;
}
2020-09-01 11:54:16 +01:00
2024-11-09 01:37:59 +00:00
protected override Errno OnLockHandle(string file, OpenedPathInfo info, FcntlCommand cmd, ref Flock @lock) =>
Errno.EOPNOTSUPP;
protected override Errno OnMapPathLogicalToPhysicalIndex(string path, ulong logical, out ulong physical)
{
physical = ulong.MaxValue;
return Errno.EOPNOTSUPP;
}
[DllImport("libc", SetLastError = true)]
static extern int setxattr(string path, string name, IntPtr value, long size, int flags);
public void Umount()
{
2025-07-27 17:32:31 +01:00
var rnd = new Random();
byte[] token = new byte[64];
2024-11-09 01:37:59 +00:00
rnd.NextBytes(token);
_umountToken = Base32.ToBase32String(token);
setxattr(Path.Combine(MountPoint, ".fuse_umount"), _umountToken, IntPtr.Zero, 0, 0);
}
}