From f708ab0b501183d021d9d0ffaab046b2d953759c Mon Sep 17 00:00:00 2001 From: Moritz Gmeiner Date: Thu, 31 Jul 2025 01:07:01 +0200 Subject: [PATCH 1/5] moved the Rc> back into FatFs --- fat-bits/src/bpb.rs | 1 + fat-bits/src/lib.rs | 9 ++++++++- fat-dump/src/main.rs | 2 +- fat-fuse/src/lib.rs | 5 ++++- fat-mount/src/main.rs | 2 +- 5 files changed, 15 insertions(+), 4 deletions(-) diff --git a/fat-bits/src/bpb.rs b/fat-bits/src/bpb.rs index 26e4223..362346c 100644 --- a/fat-bits/src/bpb.rs +++ b/fat-bits/src/bpb.rs @@ -236,6 +236,7 @@ impl Bpb { if self.fat_type() == FatType::Fat32 { return None; } + Some(self.fat_offset() + self.sector_to_offset(self.num_fats() as u32 * self.fat_size())) } diff --git a/fat-bits/src/lib.rs b/fat-bits/src/lib.rs index f17275c..5db4130 100644 --- a/fat-bits/src/lib.rs +++ b/fat-bits/src/lib.rs @@ -98,8 +98,15 @@ pub struct FatFs { fat: fat::Fat, } +unsafe impl Send for FatFs {} + impl FatFs { - pub fn load(data: Rc>) -> anyhow::Result { + pub fn load(data: S) -> anyhow::Result + where + S: SliceLike + Send + 'static, + { + let data = Rc::new(RefCell::new(data)); + let mut bpb_bytes = [0; 512]; data.borrow_mut().read_at_offset(0, &mut bpb_bytes)?; diff --git a/fat-dump/src/main.rs b/fat-dump/src/main.rs index 2ddc569..39e16ff 100644 --- a/fat-dump/src/main.rs +++ b/fat-dump/src/main.rs @@ -22,7 +22,7 @@ pub fn main() -> anyhow::Result<()> { // println!("{}", bpb); - let fat_fs = FatFs::load(Rc::new(RefCell::new(file)))?; + let fat_fs = FatFs::load(file)?; println!("{}", fat_fs.bpb()); println!(); diff --git a/fat-fuse/src/lib.rs b/fat-fuse/src/lib.rs index e3a77c3..309e0e0 100644 --- a/fat-fuse/src/lib.rs +++ b/fat-fuse/src/lib.rs @@ -26,7 +26,10 @@ pub struct FatFuse { } impl FatFuse { - pub fn new(data: Rc>) -> anyhow::Result { + pub fn new(data: S) -> anyhow::Result + where + S: SliceLike + Send + 'static, + { let uid = unsafe { libc::getuid() }; let gid = unsafe { libc::getgid() }; diff --git a/fat-mount/src/main.rs b/fat-mount/src/main.rs index f9ea244..d1e5bef 100644 --- a/fat-mount/src/main.rs +++ b/fat-mount/src/main.rs @@ -15,7 +15,7 @@ fn main() -> anyhow::Result<()> { let file = File::open(path)?; - let fat_fuse = FatFuse::new(Rc::new(RefCell::new(file)))?; + let fat_fuse = FatFuse::new(file)?; let options = vec![ MountOption::RO, From df20ae6f0ce4b929de68730cdc1a1c9e4af6e738 Mon Sep 17 00:00:00 2001 From: Moritz Gmeiner Date: Thu, 31 Jul 2025 01:07:48 +0200 Subject: [PATCH 2/5] fixed and update fat-mount --- Cargo.lock | 25 ++++++++++++++++++++++++- fat-mount/Cargo.toml | 1 + fat-mount/src/main.rs | 17 ++++++++++++++--- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b60f235..a9c6e8a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -168,6 +168,16 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "ctrlc" +version = "3.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46f93780a459b7d656ef7f071fe699c4d3d2cb201c4b24d085b6ddc505276e73" +dependencies = [ + "nix 0.30.1", + "windows-sys", +] + [[package]] name = "enum_dispatch" version = "0.3.13" @@ -243,6 +253,7 @@ name = "fat-mount" version = "0.1.0" dependencies = [ "anyhow", + "ctrlc", "env_logger", "fat-fuse", "fuser", @@ -257,7 +268,7 @@ dependencies = [ "libc", "log", "memchr", - "nix", + "nix 0.29.0", "page_size", "pkg-config", "smallvec", @@ -376,6 +387,18 @@ dependencies = [ "libc", ] +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "num-traits" version = "0.2.19" diff --git a/fat-mount/Cargo.toml b/fat-mount/Cargo.toml index e9f82da..ba48f80 100644 --- a/fat-mount/Cargo.toml +++ b/fat-mount/Cargo.toml @@ -5,6 +5,7 @@ edition = "2024" [dependencies] anyhow = "1.0.98" +ctrlc = "3.4.7" env_logger = "0.11.8" fat-fuse = { version = "0.1.0", path = "../fat-fuse" } fuser = "0.15.1" diff --git a/fat-mount/src/main.rs b/fat-mount/src/main.rs index d1e5bef..31a5a8f 100644 --- a/fat-mount/src/main.rs +++ b/fat-mount/src/main.rs @@ -1,6 +1,5 @@ -use std::cell::RefCell; use std::fs::File; -use std::rc::Rc; +use std::sync::mpsc::channel; use fat_fuse::FatFuse; use fuser::MountOption; @@ -10,6 +9,7 @@ fn main() -> anyhow::Result<()> { let mut args = std::env::args(); + let _prog_name = args.next().unwrap(); let path = args.next().ok_or(anyhow::anyhow!("missing fs path"))?; let mountpoint = args.next().ok_or(anyhow::anyhow!("missing mount point"))?; @@ -23,7 +23,18 @@ fn main() -> anyhow::Result<()> { MountOption::AutoUnmount, ]; - fuser::mount2(fat_fuse, mountpoint, &options).unwrap(); + let (tx, rx) = channel(); + + ctrlc::set_handler(move || { + tx.send(()).unwrap(); + }) + .unwrap(); + + let handle = fuser::spawn_mount2(fat_fuse, mountpoint, &options)?; + + rx.recv().unwrap(); + + println!("done"); Ok(()) } From 7a4d89a09a4f43885f68ebfed5dee8fdc0be1817 Mon Sep 17 00:00:00 2001 From: Moritz Gmeiner Date: Thu, 31 Jul 2025 01:11:27 +0200 Subject: [PATCH 3/5] removed some FUSE ops that will not be implemented (for now) --- fat-fuse/src/fuse.rs | 195 +------------------------------------------ 1 file changed, 4 insertions(+), 191 deletions(-) diff --git a/fat-fuse/src/fuse.rs b/fat-fuse/src/fuse.rs index 512b99d..c2a2e2d 100644 --- a/fat-fuse/src/fuse.rs +++ b/fat-fuse/src/fuse.rs @@ -214,21 +214,6 @@ impl Filesystem for FatFuse { 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<'_>, @@ -262,6 +247,10 @@ impl Filesystem for FatFuse { reply.error(EPERM); } + if let Some(old_ino) = self.ino_by_fh.insert(fh, ino) { + debug!("fh {} was associated with ino {}, now with ino {}", fh, old_ino, ino); + } + fn open(&mut self, _req: &fuser::Request<'_>, _ino: u64, _flags: i32, reply: fuser::ReplyOpen) { reply.opened(0, 0); } @@ -418,62 +407,6 @@ impl Filesystem for FatFuse { 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<'_>, @@ -492,104 +425,6 @@ impl Filesystem for FatFuse { 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<'_>, @@ -605,26 +440,4 @@ impl Filesystem for FatFuse { ); 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); - } } From bdcda26d77059b083481844db8113162a08334ab Mon Sep 17 00:00:00 2001 From: Moritz Gmeiner Date: Thu, 31 Jul 2025 01:13:32 +0200 Subject: [PATCH 4/5] cluster 0 as 'empty' marker --- fat-bits/src/lib.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/fat-bits/src/lib.rs b/fat-bits/src/lib.rs index 5db4130..bd50960 100644 --- a/fat-bits/src/lib.rs +++ b/fat-bits/src/lib.rs @@ -185,12 +185,28 @@ impl FatFs { } pub fn cluster_as_subslice_mut(&mut self, cluster: u32) -> SubSliceMut<'_> { + if cluster == 0 { + // for cluster 0 simply return empty subslice + // this makes things a bit easier, since cluster 0 is used as a marker that a file/dir + // is empty + + SubSliceMut::new(self, 0, 0); + } + let offset = self.data_cluster_to_offset(cluster); SubSliceMut::new(self, offset, self.bytes_per_cluster) } pub fn cluster_as_subslice(&self, cluster: u32) -> SubSlice<'_> { + if cluster == 0 { + // for cluster 0 simply return empty subslice + // this makes things a bit easier, since cluster 0 is used as a marker that a file/dir + // is empty + + SubSlice::new(self, 0, 0); + } + let offset = self.data_cluster_to_offset(cluster); SubSlice::new(self, offset, self.bytes_per_cluster) From 18c45d514142a701f41992af1c81aa3f83ced92a Mon Sep 17 00:00:00 2001 From: Moritz Gmeiner Date: Thu, 31 Jul 2025 01:14:28 +0200 Subject: [PATCH 5/5] ls/ll works now --- fat-fuse/src/fuse.rs | 207 ++++++++++++++++++++++++++++++++---------- fat-fuse/src/inode.rs | 60 +++++++++++- fat-fuse/src/lib.rs | 146 +++++++++++++++++++++++------ 3 files changed, 332 insertions(+), 81 deletions(-) diff --git a/fat-fuse/src/fuse.rs b/fat-fuse/src/fuse.rs index c2a2e2d..b037ea2 100644 --- a/fat-fuse/src/fuse.rs +++ b/fat-fuse/src/fuse.rs @@ -2,13 +2,15 @@ use std::ffi::c_int; use std::time::Duration; use fat_bits::dir::DirEntry; -use fuser::Filesystem; -use libc::{EIO, ENOENT, ENOSYS, EPERM}; +use fuser::{FileType, Filesystem}; +use libc::{EINVAL, EIO, ENOENT, ENOSYS, ENOTDIR, EPERM}; use log::{debug, warn}; use crate::FatFuse; use crate::inode::Inode; +const TTL: Duration = Duration::from_secs(1); + impl Filesystem for FatFuse { fn init( &mut self, @@ -32,14 +34,18 @@ impl Filesystem for FatFuse { let Some(name) = name.to_str() else { // TODO: add proper handling of non-utf8 strings + debug!("cannot convert OsStr {:?} to str", name); reply.error(ENOSYS); return; }; + debug!("looking up file {} with parent ino {}", name, parent); + let Some(parent_inode) = self.get_inode(parent) else { // parent inode does not exist // TODO: how can we make sure this does not happed? // TODO: panic? + debug!("could not find inode for parent ino {}", parent); reply.error(EIO); return; @@ -67,29 +73,33 @@ impl Filesystem for FatFuse { }) { Ok(dir_entry) => dir_entry, Err(err) => { + debug!("error: {}", err); reply.error(err); return; } }; - let inode = match self.get_inode_by_first_cluster(dir_entry.first_cluster()) { - Some(inode) => inode, - None => { - // no inode found, make a new one + // let inode = match self.get_inode_by_first_cluster(dir_entry.first_cluster()) { + // Some(inode) => inode, + // None => { + // // no inode found, make a new one + // let ino = self.next_ino(); - let ino = self.next_ino(); + // let inode = Inode::new(&self.fat_fs, &dir_entry, ino, self.uid, self.gid); - let inode = Inode::new(&self.fat_fs, dir_entry, ino, self.uid, self.gid); + // self.insert_inode(inode) + // } + // }; - self.insert_inode(inode) - } - }; + let inode = self.get_or_make_inode_by_dir_entry(&dir_entry); let attr = inode.file_attr(); let generation = inode.generation(); - reply.entry(&Duration::from_secs(1), &attr, generation as u64); + reply.entry(&TTL, &attr, generation as u64); + + inode.refcount_inc(); } fn forget(&mut self, _req: &fuser::Request<'_>, ino: u64, nlookup: u64) { @@ -99,18 +109,9 @@ impl Filesystem for FatFuse { return; }; - let ref_count = inode.ref_count_mut(); + // *ref_count = ref_count.saturating_sub(nlookup); - if *ref_count < nlookup { - debug!( - "tried to forget {} refs of inode {}, but ref_count is only {}", - nlookup, ino, *ref_count - ); - } - - *ref_count = ref_count.saturating_sub(nlookup); - - if *ref_count == 0 { + if inode.refcount_dec(nlookup) == 0 { // no more references, drop inode self.drop_inode(ino); } @@ -123,8 +124,28 @@ impl Filesystem for FatFuse { fh: Option, reply: fuser::ReplyAttr, ) { - warn!("[Not Implemented] getattr(ino: {:#x?}, fh: {:#x?})", ino, fh); - reply.error(ENOSYS); + // warn!("[Not Implemented] getattr(ino: {:#x?}, fh: {:#x?})", ino, fh); + // reply.error(ENOSYS); + + let inode = if let Some(fh) = fh { + let Some(inode) = self.get_inode_by_fh(fh) else { + reply.error(EIO); + + return; + }; + + inode + } else if let Some(inode) = self.get_inode(ino) { + inode + } else { + reply.error(EIO); + + return; + }; + + let attr = inode.file_attr(); + + reply.attr(&TTL, &attr); } fn setattr( @@ -232,27 +253,19 @@ impl Filesystem for FatFuse { 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) { + if !self.inode_table.contains_key(&ino) { + reply.error(EINVAL); + return; + } + + let fh = self.next_fh(); if let Some(old_ino) = self.ino_by_fh.insert(fh, ino) { debug!("fh {} was associated with ino {}, now with ino {}", fh, old_ino, ino); } - fn open(&mut self, _req: &fuser::Request<'_>, _ino: u64, _flags: i32, reply: fuser::ReplyOpen) { - reply.opened(0, 0); + reply.opened(fh, 0); } fn read( @@ -343,11 +356,17 @@ impl Filesystem for FatFuse { fn opendir( &mut self, _req: &fuser::Request<'_>, - _ino: u64, + ino: u64, _flags: i32, reply: fuser::ReplyOpen, ) { - reply.opened(0, 0); + let fh = self.next_fh(); + + if let Some(old_ino) = self.ino_by_fh.insert(fh, ino) { + debug!("fh {} was already associated with ino {}, now with ino {}", fh, old_ino, ino); + } + + reply.opened(fh, 0); } fn readdir( @@ -356,10 +375,79 @@ impl Filesystem for FatFuse { ino: u64, fh: u64, offset: i64, - reply: fuser::ReplyDirectory, + mut reply: fuser::ReplyDirectory, ) { - warn!("[Not Implemented] readdir(ino: {:#x?}, fh: {}, offset: {})", ino, fh, offset); - reply.error(ENOSYS); + let Ok(mut offset): Result = offset.try_into() else { + return; + }; + + let Some(inode) = self.get_inode_by_fh(fh) else { + debug!("could not find inode accociated with fh {} (ino: {})", fh, ino); + + reply.error(EINVAL); + return; + }; + + if inode.ino() != ino { + debug!( + "ino {} of inode associated with fh {} does not match given ino {}", + inode.ino(), + fh, + ino + ); + + reply.error(EINVAL); + return; + } + + let mut _next_idx = 1; + let mut next_offset = || { + let next_idx = _next_idx; + _next_idx += 1; + next_idx + }; + + if inode.is_root() { + if offset == 0 { + debug!("adding . to root dir"); + if reply.add(1, next_offset(), FileType::Directory, ".") { + return; + } + } else { + offset -= 1; + } + + if offset == 0 { + debug!("adding .. to root dir"); + if reply.add(1, next_offset(), FileType::Directory, "..") { + return; + } + } else { + offset -= 1; + } + } + + let Ok(dir_iter) = inode.dir_iter(&self.fat_fs) else { + reply.error(ENOTDIR); + return; + }; + + // need to drop dir_iter here so we can borrow self mut again + // also skip over `offset` entries + let dirs: Vec = dir_iter.skip(offset).collect(); + + for dir_entry in dirs { + let name = dir_entry.name_string().unwrap_or("".into()); + + let inode: &Inode = self.get_or_make_inode_by_dir_entry(&dir_entry); + + debug!("adding entry {} (ino: {})", name, inode.ino()); + if reply.add(ino, next_offset(), inode.kind().into(), name) { + return; + } + } + + reply.ok(); } fn readdirplus( @@ -380,11 +468,34 @@ impl Filesystem for FatFuse { fn releasedir( &mut self, _req: &fuser::Request<'_>, - _ino: u64, - _fh: u64, + ino: u64, + fh: u64, _flags: i32, reply: fuser::ReplyEmpty, ) { + let Some(ino) = self.ino_by_fh.remove(&fh) else { + debug!("can't find inode {} by fh {}", ino, fh); + + reply.error(EIO); + return; + }; + + let Some(inode) = self.inode_table.get(&ino) else { + debug!("ino {} not associated with an inode", ino); + + reply.ok(); + return; + }; + + if inode.ino() != ino { + debug!( + "inode with ino {}, associated with fh {}, does not have expected ino {}", + inode.ino(), + fh, + ino + ); + } + reply.ok(); } diff --git a/fat-fuse/src/inode.rs b/fat-fuse/src/inode.rs index 96c8000..ead17ec 100644 --- a/fat-fuse/src/inode.rs +++ b/fat-fuse/src/inode.rs @@ -6,6 +6,7 @@ use fat_bits::FatFs; use fat_bits::dir::DirEntry; use fuser::FileAttr; use libc::ENOTDIR; +use log::debug; use rand::{Rng, SeedableRng as _}; thread_local! { @@ -91,7 +92,7 @@ impl Inode { ((secs as u32) << 16) | rand as u32 } - pub fn new(fat_fs: &FatFs, dir_entry: DirEntry, ino: u64, uid: u32, gid: u32) -> Inode { + pub fn new(fat_fs: &FatFs, dir_entry: &DirEntry, ino: u64, uid: u32, gid: u32) -> Inode { assert!(dir_entry.is_file() || dir_entry.is_dir()); let generation = Self::new_generation(); @@ -114,6 +115,12 @@ impl Inode { let mtime = datetime_to_system(dir_entry.write_time()); let crtime = datetime_to_system(dir_entry.create_time()); + debug!( + "creating new inode: ino: {} name: {}", + ino, + dir_entry.name_string().unwrap_or("".into()) + ); + Inode { ino, generation, @@ -131,6 +138,28 @@ impl Inode { } } + pub fn root_inode(fat_fs: &FatFs, uid: u32, gid: u32) -> Inode { + // let generation = Self::new_generation(); + + let root_cluster = fat_fs.bpb().root_cluster().unwrap_or(0); + + Inode { + ino: 1, + generation: 0, + ref_count: 0, + size: 0, + block_size: fat_fs.bpb().bytes_per_sector() as u32, + kind: Kind::Dir, + read_only: false, + atime: SystemTime::UNIX_EPOCH, + mtime: SystemTime::UNIX_EPOCH, + crtime: SystemTime::UNIX_EPOCH, + uid, + gid, + first_cluster: root_cluster, + } + } + pub fn ino(&self) -> u64 { self.ino } @@ -143,14 +172,37 @@ impl Inode { self.ref_count } - pub fn ref_count_mut(&mut self) -> &mut u64 { - &mut self.ref_count + pub fn refcount_inc(&mut self) { + self.ref_count += 1; + } + + pub fn refcount_dec(&mut self, n: u64) -> u64 { + if self.ref_count < n { + debug!( + "inode {}: tried to decrement refcount by {}, but is only {}", + self.ino(), + n, + self.ref_count + ); + } + + self.ref_count = self.ref_count.saturating_sub(n); + + self.ref_count + } + + pub fn kind(&self) -> Kind { + self.kind } pub fn first_cluster(&self) -> u32 { self.first_cluster } + pub fn is_root(&self) -> bool { + self.ino == ROOT_INO + } + pub fn file_attr(&self) -> FileAttr { let perm = if self.read_only { 0o555 } else { 0o777 }; @@ -180,7 +232,7 @@ impl Inode { return Err(ENOTDIR); } - if self.ino == ROOT_INO { + if self.is_root() { // root dir return Ok(fat_fs.root_dir_iter()); diff --git a/fat-fuse/src/lib.rs b/fat-fuse/src/lib.rs index 309e0e0..6e993b6 100644 --- a/fat-fuse/src/lib.rs +++ b/fat-fuse/src/lib.rs @@ -5,6 +5,7 @@ use std::cell::RefCell; use std::collections::BTreeMap; use std::rc::Rc; +use fat_bits::dir::DirEntry; use fat_bits::{FatFs, SliceLike}; use log::debug; @@ -18,11 +19,25 @@ pub struct FatFuse { gid: u32, next_ino: u64, - next_fd: u32, + next_fh: u64, inode_table: BTreeMap, ino_by_first_cluster: BTreeMap, + ino_by_fh: BTreeMap, +} + +impl Drop for FatFuse { + fn drop(&mut self) { + println!("inode_table: {}", self.inode_table.len()); + + println!("ino_by_first_cluster: {}", self.ino_by_first_cluster.len()); + for (&first_cluster, &ino) in self.ino_by_first_cluster.iter() { + println!("{} -> {}", first_cluster, ino); + } + + println!("ino_by_fh: {}", self.ino_by_fh.len()); + } } impl FatFuse { @@ -35,17 +50,24 @@ impl FatFuse { let fat_fs = FatFs::load(data)?; - // TODO: build and insert root dir inode - - Ok(FatFuse { + let mut fat_fuse = FatFuse { fat_fs, uid, gid, next_ino: 2, // 0 is reserved and 1 is root - next_fd: 0, + next_fh: 0, inode_table: BTreeMap::new(), ino_by_first_cluster: BTreeMap::new(), - }) + ino_by_fh: BTreeMap::new(), + }; + + // TODO: build and insert root dir inode + + let root_inode = Inode::root_inode(&fat_fuse.fat_fs, uid, gid); + + fat_fuse.insert_inode(root_inode); + + Ok(fat_fuse) } fn next_ino(&mut self) -> u64 { @@ -58,6 +80,16 @@ impl FatFuse { ino } + fn next_fh(&mut self) -> u64 { + let fh = self.next_fh; + + assert!(!self.ino_by_fh.contains_key(&fh)); + + self.next_fh += 1; + + fh + } + fn insert_inode(&mut self, inode: Inode) -> &mut Inode { let ino = inode.ino(); let generation = inode.generation(); @@ -81,8 +113,6 @@ impl FatFuse { } }; - let old_ino = self.ino_by_first_cluster.insert(first_cluster, ino); - debug!( "inserted new inode with ino {} and generation {} (first cluster: {})", ino, generation, first_cluster @@ -92,14 +122,18 @@ impl FatFuse { debug!("ejected inode {} {}", old_inode.ino(), old_inode.generation()); } - if let Some(old_ino) = old_ino { - debug!("ejected old {} -> {} cluster to ino mapping", first_cluster, old_ino); + if first_cluster != 0 { + if let Some(old_ino) = self.ino_by_first_cluster.insert(first_cluster, ino) { + debug!("ejected old {} -> {} cluster to ino mapping", first_cluster, old_ino); + } } new_inode } fn drop_inode(&mut self, ino: u64) { + debug!("dropping ino {}", ino); + let Some(inode) = self.inode_table.remove(&ino) else { debug!("tried to drop inode with ino {}, but was not in table", ino); @@ -108,30 +142,30 @@ impl FatFuse { let first_cluster = inode.first_cluster(); - let entry = self.ino_by_first_cluster.entry(first_cluster); + if first_cluster != 0 { + let entry = self.ino_by_first_cluster.entry(first_cluster); - match entry { - std::collections::btree_map::Entry::Vacant(_) => debug!( - "removed inode with ino {} from table, but it's first cluster did not point to any ino", - ino - ), - std::collections::btree_map::Entry::Occupied(occupied_entry) => { - let found_ino = *occupied_entry.get(); + match entry { + std::collections::btree_map::Entry::Vacant(_) => debug!( + "removed inode with ino {} from table, but it's first cluster did not point to any ino", + ino + ), + std::collections::btree_map::Entry::Occupied(occupied_entry) => { + let found_ino = *occupied_entry.get(); - if found_ino == ino { - // matches our inode, remove it - occupied_entry.remove(); - } else { - // does not match removed inode, leave it as is - debug!( - "removed inode with ino {} from table, but it's first cluster pointer to ino {} instead", - ino, found_ino - ); + if found_ino == ino { + // matches our inode, remove it + occupied_entry.remove(); + } else { + // does not match removed inode, leave it as is + debug!( + "removed inode with ino {} from table, but it's first cluster pointer to ino {} instead", + ino, found_ino + ); + } } } } - - todo!() } fn get_inode(&self, ino: u64) -> Option<&Inode> { @@ -142,7 +176,31 @@ impl FatFuse { self.inode_table.get_mut(&ino) } + fn get_or_make_inode_by_dir_entry(&mut self, dir_entry: &DirEntry) -> &mut Inode { + if self + .get_inode_by_first_cluster_mut(dir_entry.first_cluster()) + .is_some() + { + return self + .get_inode_by_first_cluster_mut(dir_entry.first_cluster()) + .unwrap(); + } + + // no inode found, make a new one + let ino = self.next_ino(); + + let inode = Inode::new(&self.fat_fs, dir_entry, ino, self.uid, self.gid); + + self.insert_inode(inode) + } + pub fn get_inode_by_first_cluster(&self, first_cluster: u32) -> Option<&Inode> { + if first_cluster == 0 { + debug!("trying to get inode by first cluster 0"); + + return None; + } + let ino = self.ino_by_first_cluster.get(&first_cluster)?; if let Some(inode) = self.inode_table.get(ino) { @@ -158,6 +216,12 @@ impl FatFuse { } pub fn get_inode_by_first_cluster_mut(&mut self, first_cluster: u32) -> Option<&mut Inode> { + if first_cluster == 0 { + debug!("trying to get inode by first cluster 0"); + + return None; + } + let ino = self.ino_by_first_cluster.get(&first_cluster)?; if let Some(inode) = self.inode_table.get_mut(ino) { @@ -171,4 +235,28 @@ impl FatFuse { None } } + + pub fn get_inode_by_fh(&self, fh: u64) -> Option<&Inode> { + let ino = self.ino_by_fh.get(&fh)?; + + if let Some(inode) = self.inode_table.get(ino) { + Some(inode) + } else { + debug!("fh {} is mapped to ino {}, but inode is not in table", fh, ino); + + None + } + } + + pub fn get_inode_by_fh_mut(&mut self, fh: u64) -> Option<&mut Inode> { + let ino = self.ino_by_fh.get(&fh)?; + + if let Some(inode) = self.inode_table.get_mut(ino) { + Some(inode) + } else { + debug!("fh {} is mapped to ino {}, but inode is not in table", fh, ino); + + None + } + } }