From 31211f84c2a2467a3b4b1a400d16c7195485b9d6 Mon Sep 17 00:00:00 2001 From: edef Date: Tue, 3 May 2022 18:46:31 +0000 Subject: ripple/fossil/mount: expose arbitrary directories by digest This makes the filesystem more like eg /nix/store. ~/src/ripple> ./target/release/add fossil puma5z7rnb4tmnqk8ixgryobay9ifg8txh69635snkgx8dis6quo ~/src/ripple> ./target/release/mount & ~/src/ripple> ls mnt/puma5z7rnb4tmnqk8ixgryobay9ifg8txh69635snkgx8dis6quo benches build.rs Cargo.toml src Change-Id: Ic35f81ffec521f49ce2e4a414919e1ff717d7041 --- ripple/fossil/src/bin/mount.rs | 189 ++++++++++++++++++++++++++++------------- 1 file changed, 132 insertions(+), 57 deletions(-) diff --git a/ripple/fossil/src/bin/mount.rs b/ripple/fossil/src/bin/mount.rs index 1dafc41..846e543 100644 --- a/ripple/fossil/src/bin/mount.rs +++ b/ripple/fossil/src/bin/mount.rs @@ -10,6 +10,7 @@ use { log::debug, std::{ cell::RefCell, + collections::{btree_map, hash_map, BTreeMap, HashMap}, io::{self, Read, Seek}, path::PathBuf, time::{Duration, SystemTime, UNIX_EPOCH}, @@ -77,19 +78,15 @@ fn file_attr(ino: u64, node: memtree::Node) -> fuser::FileAttr { struct Args { #[clap(long, default_value = "fossil.db")] store: PathBuf, - #[clap(parse(try_from_str = fossil::digest_from_str))] - root: fossil::Digest, } fn main() { env_logger::init(); let args = Args::parse(); - let store = fossil::Store::open(args.store).unwrap(); - let root = memtree::load_root(&store, args.root); fuser::mount2( - Filesystem::open(store, root), + Filesystem::open(store), "mnt", &[fuser::MountOption::DefaultPermissions], ) @@ -98,16 +95,56 @@ fn main() { struct Filesystem { store: fossil::Store, - root: memtree::Directory, + roots: BTreeMap, + roots_by_ident: HashMap, + inode_tail: u64, } impl Filesystem { - fn open(store: fossil::Store, root: memtree::Directory) -> Filesystem { - Filesystem { store, root } + fn open(store: fossil::Store) -> Filesystem { + Filesystem { + store, + roots: BTreeMap::new(), + roots_by_ident: HashMap::new(), + inode_tail: 0x1000, + } } fn find(&self, ino: u64) -> Option { - memtree::Node::Directory(&self.root).find(ino.checked_sub(1)?.try_into().ok()?) + if ino == 1 { + // the mountpoint is special-cased in relevant callers + unreachable!(); + } + + let (&root_ino, root) = self.roots.range(..=ino).next_back()?; + let index: u32 = ino.checked_sub(root_ino)?.try_into().ok()?; + + memtree::Node::Directory(root).find(index) + } + + fn lookup_root(&mut self, name: &std::ffi::OsStr) -> Option<(u64, &memtree::Directory)> { + let name = name.to_str()?; + let ident = fossil::digest_from_str(name).ok()?; + + Some(match self.roots_by_ident.entry(ident) { + hash_map::Entry::Occupied(e) => { + let &inode = e.get(); + (inode, &self.roots[&inode]) + } + hash_map::Entry::Vacant(e) => { + let root = memtree::load_root(&self.store, ident); + let inode = self.inode_tail; + self.inode_tail += inode + 1 + root.size() as u64; + + e.insert(inode); + let root = match self.roots.entry(inode) { + btree_map::Entry::Occupied(_) => unreachable!(), + btree_map::Entry::Vacant(e2) => e2.insert(root), + }; + + (inode, root) + } + }) } unsafe fn from_fh<'a>(&'a self, fh: u64) -> *mut Handle<'a> { @@ -138,24 +175,38 @@ impl fuser::Filesystem for Filesystem { name: &std::ffi::OsStr, reply: fuser::ReplyEntry, ) { - let dir = match self.find(parent) { - Some(memtree::Node::Directory(d)) => d, - Some(_) => { - reply.error(EINVAL); - return; - } - None => { - reply.error(ENOENT); - return; - } - }; + match parent { + 1 => match self.lookup_root(name) { + Some((ino, dir)) => { + reply.entry( + &Duration::ZERO, + &file_attr(ino, memtree::Node::Directory(dir)), + 0, + ); + } + None => reply.error(ENOENT), + }, + _ => { + let dir = match self.find(parent) { + Some(memtree::Node::Directory(d)) => d, + Some(_) => { + reply.error(EINVAL); + return; + } + None => { + reply.error(ENOENT); + return; + } + }; - let entry = name.to_str().and_then(|name| dir.lookup(name)); - match entry { - None => reply.error(ENOENT), - Some(entry) => { - let ino = parent + entry.index as u64 + 1; - reply.entry(&Duration::ZERO, &file_attr(ino, entry.node), 0); + let entry = name.to_str().and_then(|name| dir.lookup(name)); + match entry { + None => reply.error(ENOENT), + Some(entry) => { + let ino = parent + entry.index as u64 + 1; + reply.entry(&Duration::ZERO, &file_attr(ino, entry.node), 0); + } + } } } } @@ -163,10 +214,19 @@ impl fuser::Filesystem for Filesystem { fn forget(&mut self, _req: &fuser::Request<'_>, _ino: u64, _nlookup: u64) {} fn getattr(&mut self, _req: &fuser::Request<'_>, ino: u64, reply: fuser::ReplyAttr) { - if let Some(node) = self.find(ino) { - reply.attr(&Duration::ZERO, &file_attr(ino, node)); - } else { - reply.error(ENOENT); + match ino { + 1 => { + let node = memtree::Directory::default(); + let node = memtree::Node::Directory(&node); + reply.attr(&Duration::ZERO, &file_attr(ino, node)); + } + _ => { + if let Some(node) = self.find(ino) { + reply.attr(&Duration::ZERO, &file_attr(ino, node)); + } else { + reply.error(ENOENT); + } + } } } @@ -308,39 +368,48 @@ impl fuser::Filesystem for Filesystem { offset: i64, mut reply: fuser::ReplyDirectory, ) { - let dir = match self.find(ino) { - Some(memtree::Node::Directory(d)) => d, - Some(_) => { - reply.error(EINVAL); - return; + match ino { + 1 => { + reply.ok(); } - None => { - reply.error(ENOENT); - return; - } - }; + _ => { + let dir = match self.find(ino) { + Some(memtree::Node::Directory(d)) => d, + Some(_) => { + reply.error(EINVAL); + return; + } + None => { + reply.error(ENOENT); + return; + } + }; - let mut children = vec![ - (ino, fuser::FileType::Directory, "."), - (ino, fuser::FileType::Directory, ".."), - ]; + let mut children = vec![ + (ino, fuser::FileType::Directory, "."), + (ino, fuser::FileType::Directory, ".."), + ]; + + for entry in dir.iter() { + let kind = match entry.node { + memtree::Node::Directory(_) => fuser::FileType::Directory, + memtree::Node::File(_) => fuser::FileType::RegularFile, + memtree::Node::Link { .. } => fuser::FileType::Symlink, + }; + children.push((ino + entry.index as u64 + 1, kind, entry.name)); + } - for entry in dir.iter() { - let kind = match entry.node { - memtree::Node::Directory(_) => fuser::FileType::Directory, - memtree::Node::File(_) => fuser::FileType::RegularFile, - memtree::Node::Link { .. } => fuser::FileType::Symlink, - }; - children.push((ino + entry.index as u64 + 1, kind, entry.name)); - } + for (offset, &(ino, kind, name)) in + children.iter().enumerate().skip(offset as usize) + { + if reply.add(ino, (offset + 1) as i64, kind, name) { + break; + } + } - for (offset, &(ino, kind, name)) in children.iter().enumerate().skip(offset as usize) { - if reply.add(ino, (offset + 1) as i64, kind, name) { - break; + reply.ok(); } } - - reply.ok(); } fn readdirplus( @@ -727,6 +796,12 @@ mod memtree { node: self.by_index[&index].as_ref(), }) } + + /// Get the directory's size. + #[must_use] + pub fn size(&self) -> u32 { + self.size + } } impl fmt::Debug for Directory { -- cgit 1.4.1