76856b893a
- musicfs-core: OriginId, FileId, VirtualPath, ContentHash, AudioMeta, FileMeta, EventBus with FileAccessed event (5 tests) - musicfs-fuse: FUSE skeleton with EROFS handlers for write ops - musicfs-origins: Origin trait with watch(), LocalOrigin impl (6 tests) - flake.nix: Nix dev shell with rust toolchain, clang, lld, fuse3 All 11 tests pass. Build produces no warnings.
278 lines
6.3 KiB
Rust
278 lines
6.3 KiB
Rust
use fuser::{
|
|
FileAttr, FileType, Filesystem, ReplyAttr, ReplyData, ReplyDirectory, ReplyEntry, ReplyOpen,
|
|
Request, FUSE_ROOT_ID,
|
|
};
|
|
use musicfs_core::{Error, Result};
|
|
use std::ffi::OsStr;
|
|
use std::path::Path;
|
|
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
|
use tracing::{debug, info};
|
|
|
|
const TTL: Duration = Duration::from_secs(1);
|
|
|
|
pub struct MusicFs {
|
|
uid: u32,
|
|
gid: u32,
|
|
}
|
|
|
|
impl MusicFs {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
uid: unsafe { libc::getuid() },
|
|
gid: unsafe { libc::getgid() },
|
|
}
|
|
}
|
|
|
|
pub fn mount(self, mountpoint: &Path) -> Result<()> {
|
|
info!("Mounting MusicFS at {:?}", mountpoint);
|
|
|
|
let options = vec![
|
|
fuser::MountOption::RO,
|
|
fuser::MountOption::FSName("musicfs".to_string()),
|
|
fuser::MountOption::AutoUnmount,
|
|
fuser::MountOption::AllowOther,
|
|
];
|
|
|
|
fuser::mount2(self, mountpoint, &options).map_err(Error::Io)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn root_attr(&self) -> FileAttr {
|
|
FileAttr {
|
|
ino: FUSE_ROOT_ID,
|
|
size: 0,
|
|
blocks: 0,
|
|
atime: UNIX_EPOCH,
|
|
mtime: UNIX_EPOCH,
|
|
ctime: UNIX_EPOCH,
|
|
crtime: UNIX_EPOCH,
|
|
kind: FileType::Directory,
|
|
perm: 0o755,
|
|
nlink: 2,
|
|
uid: self.uid,
|
|
gid: self.gid,
|
|
rdev: 0,
|
|
blksize: 512,
|
|
flags: 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for MusicFs {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
impl Filesystem for MusicFs {
|
|
fn init(
|
|
&mut self,
|
|
_req: &Request<'_>,
|
|
_config: &mut fuser::KernelConfig,
|
|
) -> std::result::Result<(), libc::c_int> {
|
|
info!("MusicFS initialized");
|
|
Ok(())
|
|
}
|
|
|
|
fn destroy(&mut self) {
|
|
info!("MusicFS destroyed");
|
|
}
|
|
|
|
fn lookup(&mut self, _req: &Request, parent: u64, name: &OsStr, reply: ReplyEntry) {
|
|
debug!("lookup(parent={}, name={:?})", parent, name);
|
|
reply.error(libc::ENOENT);
|
|
}
|
|
|
|
fn getattr(&mut self, _req: &Request, ino: u64, reply: ReplyAttr) {
|
|
debug!("getattr(ino={})", ino);
|
|
|
|
if ino == FUSE_ROOT_ID {
|
|
reply.attr(&TTL, &self.root_attr());
|
|
} else {
|
|
reply.error(libc::ENOENT);
|
|
}
|
|
}
|
|
|
|
fn readdir(
|
|
&mut self,
|
|
_req: &Request,
|
|
ino: u64,
|
|
_fh: u64,
|
|
offset: i64,
|
|
mut reply: ReplyDirectory,
|
|
) {
|
|
debug!("readdir(ino={}, offset={})", ino, offset);
|
|
|
|
if ino == FUSE_ROOT_ID {
|
|
if offset == 0 {
|
|
let _ = reply.add(FUSE_ROOT_ID, 1, FileType::Directory, ".");
|
|
}
|
|
if offset <= 1 {
|
|
let _ = reply.add(FUSE_ROOT_ID, 2, FileType::Directory, "..");
|
|
}
|
|
reply.ok();
|
|
} else {
|
|
reply.error(libc::ENOENT);
|
|
}
|
|
}
|
|
|
|
fn open(&mut self, _req: &Request, ino: u64, flags: i32, reply: ReplyOpen) {
|
|
debug!("open(ino={}, flags={})", ino, flags);
|
|
|
|
let write_flags = libc::O_WRONLY | libc::O_RDWR | libc::O_APPEND | libc::O_TRUNC;
|
|
if flags & write_flags != 0 {
|
|
reply.error(libc::EROFS);
|
|
return;
|
|
}
|
|
|
|
reply.error(libc::ENOENT);
|
|
}
|
|
|
|
fn read(
|
|
&mut self,
|
|
_req: &Request,
|
|
ino: u64,
|
|
_fh: u64,
|
|
offset: i64,
|
|
size: u32,
|
|
_flags: i32,
|
|
_lock_owner: Option<u64>,
|
|
reply: ReplyData,
|
|
) {
|
|
debug!("read(ino={}, offset={}, size={})", ino, offset, size);
|
|
reply.error(libc::ENOENT);
|
|
}
|
|
|
|
fn release(
|
|
&mut self,
|
|
_req: &Request,
|
|
ino: u64,
|
|
_fh: u64,
|
|
_flags: i32,
|
|
_lock_owner: Option<u64>,
|
|
_flush: bool,
|
|
reply: fuser::ReplyEmpty,
|
|
) {
|
|
debug!("release(ino={})", ino);
|
|
reply.ok();
|
|
}
|
|
|
|
fn write(
|
|
&mut self,
|
|
_req: &Request,
|
|
_ino: u64,
|
|
_fh: u64,
|
|
_offset: i64,
|
|
_data: &[u8],
|
|
_write_flags: u32,
|
|
_flags: i32,
|
|
_lock_owner: Option<u64>,
|
|
reply: fuser::ReplyWrite,
|
|
) {
|
|
reply.error(libc::EROFS);
|
|
}
|
|
|
|
fn mkdir(
|
|
&mut self,
|
|
_req: &Request,
|
|
_parent: u64,
|
|
_name: &OsStr,
|
|
_mode: u32,
|
|
_umask: u32,
|
|
reply: ReplyEntry,
|
|
) {
|
|
reply.error(libc::EROFS);
|
|
}
|
|
|
|
fn unlink(&mut self, _req: &Request, _parent: u64, _name: &OsStr, reply: fuser::ReplyEmpty) {
|
|
reply.error(libc::EROFS);
|
|
}
|
|
|
|
fn rmdir(&mut self, _req: &Request, _parent: u64, _name: &OsStr, reply: fuser::ReplyEmpty) {
|
|
reply.error(libc::EROFS);
|
|
}
|
|
|
|
fn rename(
|
|
&mut self,
|
|
_req: &Request,
|
|
_parent: u64,
|
|
_name: &OsStr,
|
|
_newparent: u64,
|
|
_newname: &OsStr,
|
|
_flags: u32,
|
|
reply: fuser::ReplyEmpty,
|
|
) {
|
|
reply.error(libc::EROFS);
|
|
}
|
|
|
|
fn create(
|
|
&mut self,
|
|
_req: &Request,
|
|
_parent: u64,
|
|
_name: &OsStr,
|
|
_mode: u32,
|
|
_umask: u32,
|
|
_flags: i32,
|
|
reply: fuser::ReplyCreate,
|
|
) {
|
|
reply.error(libc::EROFS);
|
|
}
|
|
|
|
fn setattr(
|
|
&mut self,
|
|
_req: &Request,
|
|
_ino: u64,
|
|
_mode: Option<u32>,
|
|
_uid: Option<u32>,
|
|
_gid: Option<u32>,
|
|
_size: Option<u64>,
|
|
_atime: Option<fuser::TimeOrNow>,
|
|
_mtime: Option<fuser::TimeOrNow>,
|
|
_ctime: Option<SystemTime>,
|
|
_fh: Option<u64>,
|
|
_crtime: Option<SystemTime>,
|
|
_chgtime: Option<SystemTime>,
|
|
_bkuptime: Option<SystemTime>,
|
|
_flags: Option<u32>,
|
|
reply: ReplyAttr,
|
|
) {
|
|
reply.error(libc::EROFS);
|
|
}
|
|
|
|
fn symlink(
|
|
&mut self,
|
|
_req: &Request,
|
|
_parent: u64,
|
|
_name: &OsStr,
|
|
_link: &Path,
|
|
reply: ReplyEntry,
|
|
) {
|
|
reply.error(libc::EROFS);
|
|
}
|
|
|
|
fn link(
|
|
&mut self,
|
|
_req: &Request,
|
|
_ino: u64,
|
|
_newparent: u64,
|
|
_newname: &OsStr,
|
|
reply: ReplyEntry,
|
|
) {
|
|
reply.error(libc::EROFS);
|
|
}
|
|
|
|
fn mknod(
|
|
&mut self,
|
|
_req: &Request,
|
|
_parent: u64,
|
|
_name: &OsStr,
|
|
_mode: u32,
|
|
_umask: u32,
|
|
_rdev: u32,
|
|
reply: ReplyEntry,
|
|
) {
|
|
reply.error(libc::EROFS);
|
|
}
|
|
}
|