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 <flokli@flokli.de>
Reviewed-by: adisbladis <adisbladis@gmail.com>
Tested-by: BuildkiteCI
This commit is contained in:
Florian Klink 2023-09-03 17:10:06 +03:00 committed by clbot
parent da9d706e0a
commit f9b5fc49b1
3 changed files with 111 additions and 4 deletions

View file

@ -66,6 +66,9 @@ pub struct FUSE {
directory_service: Arc<dyn DirectoryService>,
path_info_service: Arc<dyn PathInfoService>,
/// 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<StorePath, u64>,
@ -83,12 +86,15 @@ impl FUSE {
blob_service: Arc<dyn BlobService>,
directory_service: Arc<dyn DirectoryService>,
path_info_service: Arc<dyn PathInfoService>,
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.