From f9b5fc49b123cb4db3941ee2ae9b891f5262deef Mon Sep 17 00:00:00 2001 From: Florian Klink Date: Sun, 3 Sep 2023 17:10:06 +0300 Subject: feat(tvix/store/fuse): allow listing This provides an additional configuration flag to the tvix-store mount subcommand, and logic in the fuse module to request listing for the root of the mountpoint. Change-Id: I05a8bc11f7991b574696f27a30afe0f4e718a58c Reviewed-on: https://cl.tvl.fyi/c/depot/+/9217 Autosubmit: flokli Reviewed-by: adisbladis Tested-by: BuildkiteCI --- tvix/store/src/fuse/mod.rs | 57 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) (limited to 'tvix/store/src/fuse/mod.rs') diff --git a/tvix/store/src/fuse/mod.rs b/tvix/store/src/fuse/mod.rs index 8b44f7db550e..0015abb9d557 100644 --- a/tvix/store/src/fuse/mod.rs +++ b/tvix/store/src/fuse/mod.rs @@ -66,6 +66,9 @@ pub struct FUSE { directory_service: Arc, path_info_service: Arc, + /// Whether to (try) listing elements in the root. + list_root: bool, + /// This maps a given StorePath to the inode we allocated for the root inode. store_paths: HashMap, @@ -83,12 +86,15 @@ impl FUSE { blob_service: Arc, directory_service: Arc, path_info_service: Arc, + list_root: bool, ) -> Self { Self { blob_service, directory_service, path_info_service, + list_root, + store_paths: HashMap::default(), inode_tracker: Default::default(), @@ -311,8 +317,55 @@ impl fuser::Filesystem for FUSE { debug!("readdir"); if ino == fuser::FUSE_ROOT_ID { - reply.error(libc::EPERM); // same error code as ipfs/kubo - return; + if !self.list_root { + reply.error(libc::EPERM); // same error code as ipfs/kubo + return; + } else { + for (i, path_info) in self + .path_info_service + .list() + .skip(offset as usize) + .enumerate() + { + let path_info = match path_info { + Err(e) => { + warn!("failed to retrieve pathinfo: {}", e); + reply.error(libc::EPERM); + return; + } + Ok(path_info) => path_info, + }; + + // We know the root node exists and the store_path can be parsed because clients MUST validate. + let root_node = path_info.node.unwrap().node.unwrap(); + let store_path = StorePath::from_bytes(root_node.get_name()).unwrap(); + + let ino = match self.store_paths.get(&store_path) { + Some(ino) => *ino, + None => { + // insert the (sparse) inode data and register in + // self.store_paths. + let ino = self.inode_tracker.put((&root_node).into()); + self.store_paths.insert(store_path.clone(), ino); + ino + } + }; + + let ty = match root_node { + Node::Directory(_) => fuser::FileType::Directory, + Node::File(_) => fuser::FileType::RegularFile, + Node::Symlink(_) => fuser::FileType::Symlink, + }; + + let full = + reply.add(ino, offset + i as i64 + 1_i64, ty, store_path.to_string()); + if full { + break; + } + } + reply.ok(); + return; + } } // lookup the inode data. -- cgit 1.4.1