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, 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, _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, 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, _uid: Option, _gid: Option, _size: Option, _atime: Option, _mtime: Option, _ctime: Option, _fh: Option, _crtime: Option, _chgtime: Option, _bkuptime: Option, _flags: Option, 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); } }