Struct tvix_castore::fs::TvixStoreFs
source · pub struct TvixStoreFs<BS, DS, RN> {
blob_service: BS,
directory_service: DS,
root_nodes_provider: RN,
list_root: bool,
show_xattr: bool,
root_nodes: RwLock<HashMap<PathComponent, u64>>,
inode_tracker: RwLock<InodeTracker>,
dir_handles: RwLock<HashMap<u64, (Span, Arc<Mutex<Receiver<(usize, Result<(PathComponent, Node), Error>)>>>)>>,
next_dir_handle: AtomicU64,
file_handles: RwLock<HashMap<u64, (Span, Arc<Mutex<Box<dyn BlobReader>>>)>>,
next_file_handle: AtomicU64,
tokio_handle: Handle,
}
Expand description
This implements a read-only FUSE filesystem for a tvix-store with the passed BlobService, DirectoryService and RootNodes.
Linux uses inodes in filesystems. When implementing FUSE, most calls are for a given inode.
This means, we need to have a stable mapping of inode numbers to the corresponding store nodes.
We internally delegate all inode allocation and state keeping to the inode tracker. We store a mapping from currently “explored” names in the root to their inode.
There’s some places where inodes are allocated / data inserted into the inode tracker, if not allocated before already:
- Processing a
lookup
request, either in the mount root, or somewhere deeper. - Processing a
readdir
request
Things pointing to the same contents get the same inodes, irrespective of their own location. This means:
- Symlinks with the same target will get the same inode.
- Regular/executable files with the same contents will get the same inode
- Directories with the same contents will get the same inode.
Due to the above being valid across the whole store, and considering the merkle structure is a DAG, not a tree, this also means we can’t do “bucketed allocation”, aka reserve Directory.size inodes for each directory node we explore. Tests for this live in the tvix-store crate.
Fields§
§blob_service: BS
§directory_service: DS
§root_nodes_provider: RN
§list_root: bool
Whether to (try) listing elements in the root.
show_xattr: bool
Whether to expose blob and directory digests as extended attributes.
root_nodes: RwLock<HashMap<PathComponent, u64>>
This maps a given basename in the root to the inode we allocated for the node.
inode_tracker: RwLock<InodeTracker>
This keeps track of inodes and data alongside them.
dir_handles: RwLock<HashMap<u64, (Span, Arc<Mutex<Receiver<(usize, Result<(PathComponent, Node), Error>)>>>)>>
Maps from the handle returned from an opendir to This holds all opendir handles (for the root inode) They point to the rx part of the channel producing the listing.
next_dir_handle: AtomicU64
§file_handles: RwLock<HashMap<u64, (Span, Arc<Mutex<Box<dyn BlobReader>>>)>>
This holds all open file handles
next_file_handle: AtomicU64
§tokio_handle: Handle
Implementations§
source§impl<BS, DS, RN> TvixStoreFs<BS, DS, RN>where
BS: BlobService + Clone + Send,
DS: DirectoryService + Clone + Send + 'static,
RN: RootNodes + Clone + 'static,
impl<BS, DS, RN> TvixStoreFs<BS, DS, RN>where
BS: BlobService + Clone + Send,
DS: DirectoryService + Clone + Send + 'static,
RN: RootNodes + Clone + 'static,
pub fn new( blob_service: BS, directory_service: DS, root_nodes_provider: RN, list_root: bool, show_xattr: bool, ) -> Self
sourcefn get_inode_for_root_name(&self, name: &PathComponent) -> Option<u64>
fn get_inode_for_root_name(&self, name: &PathComponent) -> Option<u64>
Retrieves the inode for a given root node basename, if present. This obtains a read lock on self.root_nodes.
sourcefn get_directory_children(
&self,
ino: u64,
) -> Result<(B3Digest, Vec<(u64, PathComponent, Node)>)>
fn get_directory_children( &self, ino: u64, ) -> Result<(B3Digest, Vec<(u64, PathComponent, Node)>)>
For a given inode, look up the given directory behind it (from self.inode_tracker), and return its children. The inode_tracker MUST know about this inode already, and it MUST point to a InodeData::Directory. It is ok if it’s a DirectoryInodeData::Sparse - in that case, a lookup in self.directory_service is performed, and self.inode_tracker is updated with the DirectoryInodeData::Populated.
sourcefn name_in_root_to_ino_and_data(
&self,
name: &PathComponent,
) -> Result<(u64, Arc<InodeData>)>
fn name_in_root_to_ino_and_data( &self, name: &PathComponent, ) -> Result<(u64, Arc<InodeData>)>
This will turn a lookup request for a name in the root to a ino and InodeData. It will peek in [self.root_nodes], and then either look it up from [self.inode_tracker], or otherwise fetch from [self.root_nodes], and then insert into [self.inode_tracker]. In the case the name can’t be found, a libc::ENOENT is returned.
Trait Implementations§
source§impl<BS, DS, RN> FileSystem for TvixStoreFs<BS, DS, RN>where
BS: BlobService + Clone + Send + 'static,
DS: DirectoryService + Send + Clone + 'static,
RN: RootNodes + Clone + 'static,
impl<BS, DS, RN> FileSystem for TvixStoreFs<BS, DS, RN>where
BS: BlobService + Clone + Send + 'static,
DS: DirectoryService + Send + Clone + 'static,
RN: RootNodes + Clone + 'static,
§type Inode = u64
type Inode = u64
getattr
and setattr
). Can also be used as the
starting point for looking up paths in the filesystem tree. An Inode
may support operating
directly on the content of the path that to which it points. FileSystem
implementations
that support this should set the FsOptions::ZERO_MESSAGE_OPEN
option in the return value
of the init
function. On linux based systems, an Inode
is equivalent to opening a file
or directory with the libc::O_PATH
flag. Read moresource§fn init(&self, _capable: FsOptions) -> Result<FsOptions>
fn init(&self, _capable: FsOptions) -> Result<FsOptions>
source§fn getattr(
&self,
_ctx: &Context,
inode: Self::Inode,
_handle: Option<Self::Handle>,
) -> Result<(stat64, Duration)>
fn getattr( &self, _ctx: &Context, inode: Self::Inode, _handle: Option<Self::Handle>, ) -> Result<(stat64, Duration)>
source§fn lookup(
&self,
_ctx: &Context,
parent: Self::Inode,
name: &CStr,
) -> Result<Entry>
fn lookup( &self, _ctx: &Context, parent: Self::Inode, name: &CStr, ) -> Result<Entry>
source§fn opendir(
&self,
_ctx: &Context,
inode: Self::Inode,
_flags: u32,
) -> Result<(Option<Self::Handle>, OpenOptions)>
fn opendir( &self, _ctx: &Context, inode: Self::Inode, _flags: u32, ) -> Result<(Option<Self::Handle>, OpenOptions)>
source§fn readdir(
&self,
_ctx: &Context,
inode: Self::Inode,
handle: Self::Handle,
_size: u32,
offset: u64,
add_entry: &mut dyn FnMut(DirEntry<'_>) -> Result<usize>,
) -> Result<()>
fn readdir( &self, _ctx: &Context, inode: Self::Inode, handle: Self::Handle, _size: u32, offset: u64, add_entry: &mut dyn FnMut(DirEntry<'_>) -> Result<usize>, ) -> Result<()>
source§fn readdirplus(
&self,
_ctx: &Context,
inode: Self::Inode,
handle: Self::Handle,
_size: u32,
offset: u64,
add_entry: &mut dyn FnMut(DirEntry<'_>, Entry) -> Result<usize>,
) -> Result<()>
fn readdirplus( &self, _ctx: &Context, inode: Self::Inode, handle: Self::Handle, _size: u32, offset: u64, add_entry: &mut dyn FnMut(DirEntry<'_>, Entry) -> Result<usize>, ) -> Result<()>
source§fn releasedir(
&self,
_ctx: &Context,
inode: Self::Inode,
_flags: u32,
handle: Self::Handle,
) -> Result<()>
fn releasedir( &self, _ctx: &Context, inode: Self::Inode, _flags: u32, handle: Self::Handle, ) -> Result<()>
source§fn open(
&self,
_ctx: &Context,
inode: Self::Inode,
_flags: u32,
_fuse_flags: u32,
) -> Result<(Option<Self::Handle>, OpenOptions, Option<u32>)>
fn open( &self, _ctx: &Context, inode: Self::Inode, _flags: u32, _fuse_flags: u32, ) -> Result<(Option<Self::Handle>, OpenOptions, Option<u32>)>
source§fn release(
&self,
_ctx: &Context,
inode: Self::Inode,
_flags: u32,
handle: Self::Handle,
_flush: bool,
_flock_release: bool,
_lock_owner: Option<u64>,
) -> Result<()>
fn release( &self, _ctx: &Context, inode: Self::Inode, _flags: u32, handle: Self::Handle, _flush: bool, _flock_release: bool, _lock_owner: Option<u64>, ) -> Result<()>
source§fn read(
&self,
_ctx: &Context,
inode: Self::Inode,
handle: Self::Handle,
w: &mut dyn ZeroCopyWriter,
size: u32,
offset: u64,
_lock_owner: Option<u64>,
_flags: u32,
) -> Result<usize>
fn read( &self, _ctx: &Context, inode: Self::Inode, handle: Self::Handle, w: &mut dyn ZeroCopyWriter, size: u32, offset: u64, _lock_owner: Option<u64>, _flags: u32, ) -> Result<usize>
source§fn readlink(&self, _ctx: &Context, inode: Self::Inode) -> Result<Vec<u8>>
fn readlink(&self, _ctx: &Context, inode: Self::Inode) -> Result<Vec<u8>>
source§fn getxattr(
&self,
_ctx: &Context,
inode: Self::Inode,
name: &CStr,
size: u32,
) -> Result<GetxattrReply>
fn getxattr( &self, _ctx: &Context, inode: Self::Inode, name: &CStr, size: u32, ) -> Result<GetxattrReply>
source§fn listxattr(
&self,
_ctx: &Context,
inode: Self::Inode,
size: u32,
) -> Result<ListxattrReply>
fn listxattr( &self, _ctx: &Context, inode: Self::Inode, size: u32, ) -> Result<ListxattrReply>
source§fn forget(&self, ctx: &Context, inode: Self::Inode, count: u64)
fn forget(&self, ctx: &Context, inode: Self::Inode, count: u64)
source§fn batch_forget(&self, ctx: &Context, requests: Vec<(Self::Inode, u64)>)
fn batch_forget(&self, ctx: &Context, requests: Vec<(Self::Inode, u64)>)
source§fn setattr(
&self,
ctx: &Context,
inode: Self::Inode,
attr: stat64,
handle: Option<Self::Handle>,
valid: SetattrValid,
) -> Result<(stat64, Duration), Error>
fn setattr( &self, ctx: &Context, inode: Self::Inode, attr: stat64, handle: Option<Self::Handle>, valid: SetattrValid, ) -> Result<(stat64, Duration), Error>
source§fn symlink(
&self,
ctx: &Context,
linkname: &CStr,
parent: Self::Inode,
name: &CStr,
) -> Result<Entry, Error>
fn symlink( &self, ctx: &Context, linkname: &CStr, parent: Self::Inode, name: &CStr, ) -> Result<Entry, Error>
source§fn mknod(
&self,
ctx: &Context,
inode: Self::Inode,
name: &CStr,
mode: u32,
rdev: u32,
umask: u32,
) -> Result<Entry, Error>
fn mknod( &self, ctx: &Context, inode: Self::Inode, name: &CStr, mode: u32, rdev: u32, umask: u32, ) -> Result<Entry, Error>
source§fn mkdir(
&self,
ctx: &Context,
parent: Self::Inode,
name: &CStr,
mode: u32,
umask: u32,
) -> Result<Entry, Error>
fn mkdir( &self, ctx: &Context, parent: Self::Inode, name: &CStr, mode: u32, umask: u32, ) -> Result<Entry, Error>
source§fn unlink(
&self,
ctx: &Context,
parent: Self::Inode,
name: &CStr,
) -> Result<(), Error>
fn unlink( &self, ctx: &Context, parent: Self::Inode, name: &CStr, ) -> Result<(), Error>
source§fn rmdir(
&self,
ctx: &Context,
parent: Self::Inode,
name: &CStr,
) -> Result<(), Error>
fn rmdir( &self, ctx: &Context, parent: Self::Inode, name: &CStr, ) -> Result<(), Error>
source§fn rename(
&self,
ctx: &Context,
olddir: Self::Inode,
oldname: &CStr,
newdir: Self::Inode,
newname: &CStr,
flags: u32,
) -> Result<(), Error>
fn rename( &self, ctx: &Context, olddir: Self::Inode, oldname: &CStr, newdir: Self::Inode, newname: &CStr, flags: u32, ) -> Result<(), Error>
source§fn link(
&self,
ctx: &Context,
inode: Self::Inode,
newparent: Self::Inode,
newname: &CStr,
) -> Result<Entry, Error>
fn link( &self, ctx: &Context, inode: Self::Inode, newparent: Self::Inode, newname: &CStr, ) -> Result<Entry, Error>
source§fn create(
&self,
ctx: &Context,
parent: Self::Inode,
name: &CStr,
args: CreateIn,
) -> Result<(Entry, Option<Self::Handle>, OpenOptions, Option<u32>), Error>
fn create( &self, ctx: &Context, parent: Self::Inode, name: &CStr, args: CreateIn, ) -> Result<(Entry, Option<Self::Handle>, OpenOptions, Option<u32>), Error>
source§fn write(
&self,
ctx: &Context,
inode: Self::Inode,
handle: Self::Handle,
r: &mut dyn ZeroCopyReader,
size: u32,
offset: u64,
lock_owner: Option<u64>,
delayed_write: bool,
flags: u32,
fuse_flags: u32,
) -> Result<usize, Error>
fn write( &self, ctx: &Context, inode: Self::Inode, handle: Self::Handle, r: &mut dyn ZeroCopyReader, size: u32, offset: u64, lock_owner: Option<u64>, delayed_write: bool, flags: u32, fuse_flags: u32, ) -> Result<usize, Error>
source§fn flush(
&self,
ctx: &Context,
inode: Self::Inode,
handle: Self::Handle,
lock_owner: u64,
) -> Result<(), Error>
fn flush( &self, ctx: &Context, inode: Self::Inode, handle: Self::Handle, lock_owner: u64, ) -> Result<(), Error>
source§fn fsync(
&self,
ctx: &Context,
inode: Self::Inode,
datasync: bool,
handle: Self::Handle,
) -> Result<(), Error>
fn fsync( &self, ctx: &Context, inode: Self::Inode, datasync: bool, handle: Self::Handle, ) -> Result<(), Error>
source§fn fallocate(
&self,
ctx: &Context,
inode: Self::Inode,
handle: Self::Handle,
mode: u32,
offset: u64,
length: u64,
) -> Result<(), Error>
fn fallocate( &self, ctx: &Context, inode: Self::Inode, handle: Self::Handle, mode: u32, offset: u64, length: u64, ) -> Result<(), Error>
source§fn statfs(&self, ctx: &Context, inode: Self::Inode) -> Result<statvfs64, Error>
fn statfs(&self, ctx: &Context, inode: Self::Inode) -> Result<statvfs64, Error>
source§fn setxattr(
&self,
ctx: &Context,
inode: Self::Inode,
name: &CStr,
value: &[u8],
flags: u32,
) -> Result<(), Error>
fn setxattr( &self, ctx: &Context, inode: Self::Inode, name: &CStr, value: &[u8], flags: u32, ) -> Result<(), Error>
source§fn removexattr(
&self,
ctx: &Context,
inode: Self::Inode,
name: &CStr,
) -> Result<(), Error>
fn removexattr( &self, ctx: &Context, inode: Self::Inode, name: &CStr, ) -> Result<(), Error>
source§fn fsyncdir(
&self,
ctx: &Context,
inode: Self::Inode,
datasync: bool,
handle: Self::Handle,
) -> Result<(), Error>
fn fsyncdir( &self, ctx: &Context, inode: Self::Inode, datasync: bool, handle: Self::Handle, ) -> Result<(), Error>
source§fn access(
&self,
ctx: &Context,
inode: Self::Inode,
mask: u32,
) -> Result<(), Error>
fn access( &self, ctx: &Context, inode: Self::Inode, mask: u32, ) -> Result<(), Error>
source§fn lseek(
&self,
ctx: &Context,
inode: Self::Inode,
handle: Self::Handle,
offset: u64,
whence: u32,
) -> Result<u64, Error>
fn lseek( &self, ctx: &Context, inode: Self::Inode, handle: Self::Handle, offset: u64, whence: u32, ) -> Result<u64, Error>
source§fn getlk(
&self,
ctx: &Context,
inode: Self::Inode,
handle: Self::Handle,
owner: u64,
lock: FileLock,
flags: u32,
) -> Result<FileLock, Error>
fn getlk( &self, ctx: &Context, inode: Self::Inode, handle: Self::Handle, owner: u64, lock: FileLock, flags: u32, ) -> Result<FileLock, Error>
source§fn setlk(
&self,
ctx: &Context,
inode: Self::Inode,
handle: Self::Handle,
owner: u64,
lock: FileLock,
flags: u32,
) -> Result<(), Error>
fn setlk( &self, ctx: &Context, inode: Self::Inode, handle: Self::Handle, owner: u64, lock: FileLock, flags: u32, ) -> Result<(), Error>
source§fn setlkw(
&self,
ctx: &Context,
inode: Self::Inode,
handle: Self::Handle,
owner: u64,
lock: FileLock,
flags: u32,
) -> Result<(), Error>
fn setlkw( &self, ctx: &Context, inode: Self::Inode, handle: Self::Handle, owner: u64, lock: FileLock, flags: u32, ) -> Result<(), Error>
source§fn ioctl(
&self,
ctx: &Context,
inode: Self::Inode,
handle: Self::Handle,
flags: u32,
cmd: u32,
data: IoctlData<'_>,
out_size: u32,
) -> Result<IoctlData<'_>, Error>
fn ioctl( &self, ctx: &Context, inode: Self::Inode, handle: Self::Handle, flags: u32, cmd: u32, data: IoctlData<'_>, out_size: u32, ) -> Result<IoctlData<'_>, Error>
source§fn bmap(
&self,
ctx: &Context,
inode: Self::Inode,
block: u64,
blocksize: u32,
) -> Result<u64, Error>
fn bmap( &self, ctx: &Context, inode: Self::Inode, block: u64, blocksize: u32, ) -> Result<u64, Error>
Auto Trait Implementations§
impl<BS, DS, RN> !Freeze for TvixStoreFs<BS, DS, RN>
impl<BS, DS, RN> !RefUnwindSafe for TvixStoreFs<BS, DS, RN>
impl<BS, DS, RN> Send for TvixStoreFs<BS, DS, RN>
impl<BS, DS, RN> Sync for TvixStoreFs<BS, DS, RN>
impl<BS, DS, RN> Unpin for TvixStoreFs<BS, DS, RN>
impl<BS, DS, RN> !UnwindSafe for TvixStoreFs<BS, DS, RN>
Blanket Implementations§
source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
source§impl<T> FutureExt for T
impl<T> FutureExt for T
source§fn with_context(self, otel_cx: Context) -> WithContext<Self>
fn with_context(self, otel_cx: Context) -> WithContext<Self>
source§fn with_current_context(self) -> WithContext<Self>
fn with_current_context(self) -> WithContext<Self>
source§impl<T> FutureExt for T
impl<T> FutureExt for T
source§fn with_context(self, otel_cx: Context) -> WithContext<Self>
fn with_context(self, otel_cx: Context) -> WithContext<Self>
source§fn with_current_context(self) -> WithContext<Self>
fn with_current_context(self) -> WithContext<Self>
source§impl<T> Instrument for T
impl<T> Instrument for T
source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
source§impl<T> IntoEither for T
impl<T> IntoEither for T
source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left
is true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read moresource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left(&self)
returns true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read moresource§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T
in a tonic::Request