diff --git a/Cargo.lock b/Cargo.lock index 6ee0b94..42708e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,6 +20,18 @@ version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.41" @@ -61,6 +73,64 @@ dependencies = [ "fat-bits", ] +[[package]] +name = "fat-fuse" +version = "0.1.0" +dependencies = [ + "anyhow", + "fat-bits", + "fuser", + "libc", + "log", + "thiserror", +] + +[[package]] +name = "fuser" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53274f494609e77794b627b1a3cddfe45d675a6b2e9ba9c0fdc8d8eee2184369" +dependencies = [ + "libc", + "log", + "memchr", + "nix", + "page_size", + "pkg-config", + "smallvec", + "zerocopy", +] + +[[package]] +name = "libc" +version = "0.2.174" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -76,6 +146,22 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "page_size" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + [[package]] name = "proc-macro2" version = "1.0.95" @@ -94,6 +180,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + [[package]] name = "static_assertions" version = "1.1.0" @@ -136,3 +228,45 @@ name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index 15a1a97..d353d2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] resolver = "3" -members = ["fat-bits", "fat-dump"] +members = ["fat-bits", "fat-dump", "fat-fuse"] diff --git a/fat-fuse/Cargo.toml b/fat-fuse/Cargo.toml new file mode 100644 index 0000000..f92e1df --- /dev/null +++ b/fat-fuse/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "fat-fuse" +version = "0.1.0" +edition = "2024" + +[dependencies] +anyhow = "1.0.98" +fat-bits = { version = "0.1.0", path = "../fat-bits" } +fuser = "0.15.1" +libc = "0.2.174" +log = "0.4.27" +thiserror = "2.0.12" diff --git a/fat-fuse/src/lib.rs b/fat-fuse/src/lib.rs new file mode 100644 index 0000000..fdaef8c --- /dev/null +++ b/fat-fuse/src/lib.rs @@ -0,0 +1,584 @@ +use std::ffi::c_int; + +use fat_bits::dir::DirEntry; +use fat_bits::{FatFs, SliceLike}; +use fuser::{FileAttr, Filesystem}; +use libc::{ENOSYS, EPERM}; +use log::{debug, warn}; + +#[allow(dead_code)] +fn dir_entry_to_attr(dir_entry: &DirEntry, ino: u64) -> FileAttr { + FileAttr { + ino, + size: dir_entry.file_size() as u64, + blocks: todo!(), + atime: todo!(), + mtime: todo!(), + ctime: todo!(), + crtime: todo!(), + kind: todo!(), + perm: todo!(), + nlink: todo!(), + uid: todo!(), + gid: todo!(), + rdev: todo!(), + blksize: todo!(), + flags: todo!(), + } +} + +#[allow(dead_code)] +pub struct FatFuse { + fat_fs: FatFs, + + uid: u32, + gid: u32, +} + +impl FatFuse { + pub fn new(data: S) -> anyhow::Result> { + let uid = unsafe { libc::getuid() }; + let gid = unsafe { libc::getgid() }; + + let fat_fs = FatFs::load(data)?; + + Ok(FatFuse { fat_fs, uid, gid }) + } +} + +impl Filesystem for FatFuse { + fn init( + &mut self, + _req: &fuser::Request<'_>, + _config: &mut fuser::KernelConfig, + ) -> Result<(), c_int> { + Ok(()) + } + + fn destroy(&mut self) {} + + fn lookup( + &mut self, + _req: &fuser::Request<'_>, + parent: u64, + name: &std::ffi::OsStr, + reply: fuser::ReplyEntry, + ) { + warn!("[Not Implemented] lookup(parent: {:#x?}, name {:?})", parent, name); + reply.error(ENOSYS); + } + + fn forget(&mut self, _req: &fuser::Request<'_>, _ino: u64, _nlookup: u64) {} + + fn getattr( + &mut self, + _req: &fuser::Request<'_>, + ino: u64, + fh: Option, + reply: fuser::ReplyAttr, + ) { + warn!("[Not Implemented] getattr(ino: {:#x?}, fh: {:#x?})", ino, fh); + reply.error(ENOSYS); + } + + fn setattr( + &mut self, + _req: &fuser::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: fuser::ReplyAttr, + ) { + debug!( + "[Not Implemented] setattr(ino: {:#x?}, mode: {:?}, uid: {:?}, \ + gid: {:?}, size: {:?}, fh: {:?}, flags: {:?})", + ino, mode, uid, gid, size, fh, flags + ); + reply.error(ENOSYS); + } + + fn readlink(&mut self, _req: &fuser::Request<'_>, ino: u64, reply: fuser::ReplyData) { + debug!("[Not Implemented] readlink(ino: {:#x?})", ino); + reply.error(ENOSYS); + } + + fn mknod( + &mut self, + _req: &fuser::Request<'_>, + parent: u64, + name: &std::ffi::OsStr, + mode: u32, + umask: u32, + rdev: u32, + reply: fuser::ReplyEntry, + ) { + debug!( + "[Not Implemented] mknod(parent: {:#x?}, name: {:?}, mode: {}, \ + umask: {:#x?}, rdev: {})", + parent, name, mode, umask, rdev + ); + reply.error(ENOSYS); + } + + fn mkdir( + &mut self, + _req: &fuser::Request<'_>, + parent: u64, + name: &std::ffi::OsStr, + mode: u32, + umask: u32, + reply: fuser::ReplyEntry, + ) { + debug!( + "[Not Implemented] mkdir(parent: {:#x?}, name: {:?}, mode: {}, umask: {:#x?})", + parent, name, mode, umask + ); + reply.error(ENOSYS); + } + + fn unlink( + &mut self, + _req: &fuser::Request<'_>, + parent: u64, + name: &std::ffi::OsStr, + reply: fuser::ReplyEmpty, + ) { + debug!("[Not Implemented] unlink(parent: {:#x?}, name: {:?})", parent, name,); + reply.error(ENOSYS); + } + + fn rmdir( + &mut self, + _req: &fuser::Request<'_>, + parent: u64, + name: &std::ffi::OsStr, + reply: fuser::ReplyEmpty, + ) { + debug!("[Not Implemented] rmdir(parent: {:#x?}, name: {:?})", parent, name,); + reply.error(ENOSYS); + } + + fn symlink( + &mut self, + _req: &fuser::Request<'_>, + parent: u64, + link_name: &std::ffi::OsStr, + target: &std::path::Path, + reply: fuser::ReplyEntry, + ) { + debug!( + "[Not Implemented] symlink(parent: {:#x?}, link_name: {:?}, target: {:?})", + parent, link_name, target, + ); + reply.error(EPERM); + } + + fn rename( + &mut self, + _req: &fuser::Request<'_>, + parent: u64, + name: &std::ffi::OsStr, + newparent: u64, + newname: &std::ffi::OsStr, + flags: u32, + reply: fuser::ReplyEmpty, + ) { + debug!( + "[Not Implemented] rename(parent: {:#x?}, name: {:?}, newparent: {:#x?}, \ + newname: {:?}, flags: {})", + parent, name, newparent, newname, flags, + ); + reply.error(ENOSYS); + } + + fn link( + &mut self, + _req: &fuser::Request<'_>, + ino: u64, + newparent: u64, + newname: &std::ffi::OsStr, + reply: fuser::ReplyEntry, + ) { + debug!( + "[Not Implemented] link(ino: {:#x?}, newparent: {:#x?}, newname: {:?})", + ino, newparent, newname + ); + reply.error(EPERM); + } + + fn open(&mut self, _req: &fuser::Request<'_>, _ino: u64, _flags: i32, reply: fuser::ReplyOpen) { + reply.opened(0, 0); + } + + fn read( + &mut self, + _req: &fuser::Request<'_>, + ino: u64, + fh: u64, + offset: i64, + size: u32, + flags: i32, + lock_owner: Option, + reply: fuser::ReplyData, + ) { + warn!( + "[Not Implemented] read(ino: {:#x?}, fh: {}, offset: {}, size: {}, \ + flags: {:#x?}, lock_owner: {:?})", + ino, fh, offset, size, flags, lock_owner + ); + reply.error(ENOSYS); + } + + fn write( + &mut self, + _req: &fuser::Request<'_>, + ino: u64, + fh: u64, + offset: i64, + data: &[u8], + write_flags: u32, + flags: i32, + lock_owner: Option, + reply: fuser::ReplyWrite, + ) { + debug!( + "[Not Implemented] write(ino: {:#x?}, fh: {}, offset: {}, data.len(): {}, \ + write_flags: {:#x?}, flags: {:#x?}, lock_owner: {:?})", + ino, + fh, + offset, + data.len(), + write_flags, + flags, + lock_owner + ); + reply.error(ENOSYS); + } + + fn flush( + &mut self, + _req: &fuser::Request<'_>, + ino: u64, + fh: u64, + lock_owner: u64, + reply: fuser::ReplyEmpty, + ) { + debug!( + "[Not Implemented] flush(ino: {:#x?}, fh: {}, lock_owner: {:?})", + ino, fh, lock_owner + ); + reply.error(ENOSYS); + } + + fn release( + &mut self, + _req: &fuser::Request<'_>, + _ino: u64, + _fh: u64, + _flags: i32, + _lock_owner: Option, + _flush: bool, + reply: fuser::ReplyEmpty, + ) { + reply.ok(); + } + + fn fsync( + &mut self, + _req: &fuser::Request<'_>, + ino: u64, + fh: u64, + datasync: bool, + reply: fuser::ReplyEmpty, + ) { + debug!("[Not Implemented] fsync(ino: {:#x?}, fh: {}, datasync: {})", ino, fh, datasync); + reply.error(ENOSYS); + } + + fn opendir( + &mut self, + _req: &fuser::Request<'_>, + _ino: u64, + _flags: i32, + reply: fuser::ReplyOpen, + ) { + reply.opened(0, 0); + } + + fn readdir( + &mut self, + _req: &fuser::Request<'_>, + ino: u64, + fh: u64, + offset: i64, + reply: fuser::ReplyDirectory, + ) { + warn!("[Not Implemented] readdir(ino: {:#x?}, fh: {}, offset: {})", ino, fh, offset); + reply.error(ENOSYS); + } + + fn readdirplus( + &mut self, + _req: &fuser::Request<'_>, + ino: u64, + fh: u64, + offset: i64, + reply: fuser::ReplyDirectoryPlus, + ) { + debug!( + "[Not Implemented] readdirplus(ino: {:#x?}, fh: {}, offset: {})", + ino, fh, offset + ); + reply.error(ENOSYS); + } + + fn releasedir( + &mut self, + _req: &fuser::Request<'_>, + _ino: u64, + _fh: u64, + _flags: i32, + reply: fuser::ReplyEmpty, + ) { + reply.ok(); + } + + fn fsyncdir( + &mut self, + _req: &fuser::Request<'_>, + ino: u64, + fh: u64, + datasync: bool, + reply: fuser::ReplyEmpty, + ) { + debug!( + "[Not Implemented] fsyncdir(ino: {:#x?}, fh: {}, datasync: {})", + ino, fh, datasync + ); + reply.error(ENOSYS); + } + + fn statfs(&mut self, _req: &fuser::Request<'_>, _ino: u64, reply: fuser::ReplyStatfs) { + reply.statfs(0, 0, 0, 0, 0, 512, 255, 0); + } + + fn setxattr( + &mut self, + _req: &fuser::Request<'_>, + ino: u64, + name: &std::ffi::OsStr, + _value: &[u8], + flags: i32, + position: u32, + reply: fuser::ReplyEmpty, + ) { + debug!( + "[Not Implemented] setxattr(ino: {:#x?}, name: {:?}, flags: {:#x?}, position: {})", + ino, name, flags, position + ); + reply.error(ENOSYS); + } + + fn getxattr( + &mut self, + _req: &fuser::Request<'_>, + ino: u64, + name: &std::ffi::OsStr, + size: u32, + reply: fuser::ReplyXattr, + ) { + debug!("[Not Implemented] getxattr(ino: {:#x?}, name: {:?}, size: {})", ino, name, size); + reply.error(ENOSYS); + } + + fn listxattr( + &mut self, + _req: &fuser::Request<'_>, + ino: u64, + size: u32, + reply: fuser::ReplyXattr, + ) { + debug!("[Not Implemented] listxattr(ino: {:#x?}, size: {})", ino, size); + reply.error(ENOSYS); + } + + fn removexattr( + &mut self, + _req: &fuser::Request<'_>, + ino: u64, + name: &std::ffi::OsStr, + reply: fuser::ReplyEmpty, + ) { + debug!("[Not Implemented] removexattr(ino: {:#x?}, name: {:?})", ino, name); + reply.error(ENOSYS); + } + + fn access(&mut self, _req: &fuser::Request<'_>, ino: u64, mask: i32, reply: fuser::ReplyEmpty) { + debug!("[Not Implemented] access(ino: {:#x?}, mask: {})", ino, mask); + reply.error(ENOSYS); + } + + fn create( + &mut self, + _req: &fuser::Request<'_>, + parent: u64, + name: &std::ffi::OsStr, + mode: u32, + umask: u32, + flags: i32, + reply: fuser::ReplyCreate, + ) { + debug!( + "[Not Implemented] create(parent: {:#x?}, name: {:?}, mode: {}, umask: {:#x?}, \ + flags: {:#x?})", + parent, name, mode, umask, flags + ); + reply.error(ENOSYS); + } + + fn getlk( + &mut self, + _req: &fuser::Request<'_>, + ino: u64, + fh: u64, + lock_owner: u64, + start: u64, + end: u64, + typ: i32, + pid: u32, + reply: fuser::ReplyLock, + ) { + debug!( + "[Not Implemented] getlk(ino: {:#x?}, fh: {}, lock_owner: {}, start: {}, \ + end: {}, typ: {}, pid: {})", + ino, fh, lock_owner, start, end, typ, pid + ); + reply.error(ENOSYS); + } + + fn setlk( + &mut self, + _req: &fuser::Request<'_>, + ino: u64, + fh: u64, + lock_owner: u64, + start: u64, + end: u64, + typ: i32, + pid: u32, + sleep: bool, + reply: fuser::ReplyEmpty, + ) { + debug!( + "[Not Implemented] setlk(ino: {:#x?}, fh: {}, lock_owner: {}, start: {}, \ + end: {}, typ: {}, pid: {}, sleep: {})", + ino, fh, lock_owner, start, end, typ, pid, sleep + ); + reply.error(ENOSYS); + } + + fn bmap( + &mut self, + _req: &fuser::Request<'_>, + ino: u64, + blocksize: u32, + idx: u64, + reply: fuser::ReplyBmap, + ) { + debug!( + "[Not Implemented] bmap(ino: {:#x?}, blocksize: {}, idx: {})", + ino, blocksize, idx, + ); + reply.error(ENOSYS); + } + + fn ioctl( + &mut self, + _req: &fuser::Request<'_>, + ino: u64, + fh: u64, + flags: u32, + cmd: u32, + in_data: &[u8], + out_size: u32, + reply: fuser::ReplyIoctl, + ) { + debug!( + "[Not Implemented] ioctl(ino: {:#x?}, fh: {}, flags: {}, cmd: {}, \ + in_data.len(): {}, out_size: {})", + ino, + fh, + flags, + cmd, + in_data.len(), + out_size, + ); + reply.error(ENOSYS); + } + + fn fallocate( + &mut self, + _req: &fuser::Request<'_>, + ino: u64, + fh: u64, + offset: i64, + length: i64, + mode: i32, + reply: fuser::ReplyEmpty, + ) { + debug!( + "[Not Implemented] fallocate(ino: {:#x?}, fh: {}, offset: {}, \ + length: {}, mode: {})", + ino, fh, offset, length, mode + ); + reply.error(ENOSYS); + } + + fn lseek( + &mut self, + _req: &fuser::Request<'_>, + ino: u64, + fh: u64, + offset: i64, + whence: i32, + reply: fuser::ReplyLseek, + ) { + debug!( + "[Not Implemented] lseek(ino: {:#x?}, fh: {}, offset: {}, whence: {})", + ino, fh, offset, whence + ); + reply.error(ENOSYS); + } + + fn copy_file_range( + &mut self, + _req: &fuser::Request<'_>, + ino_in: u64, + fh_in: u64, + offset_in: i64, + ino_out: u64, + fh_out: u64, + offset_out: i64, + len: u64, + flags: u32, + reply: fuser::ReplyWrite, + ) { + debug!( + "[Not Implemented] copy_file_range(ino_in: {:#x?}, fh_in: {}, \ + offset_in: {}, ino_out: {:#x?}, fh_out: {}, offset_out: {}, \ + len: {}, flags: {})", + ino_in, fh_in, offset_in, ino_out, fh_out, offset_out, len, flags + ); + reply.error(ENOSYS); + } +}