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,

source

pub fn new( blob_service: BS, directory_service: DS, root_nodes_provider: RN, list_root: bool, show_xattr: bool, ) -> Self

source

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.

source

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.

source

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,

source§

type Handle = u64

Represents a file or directory that is open for reading/writing.
source§

type Inode = u64

Represents a location in the filesystem tree and can be used to perform operations that act on the metadata of a file/directory (e.g., 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 more
source§

fn init(&self, _capable: FsOptions) -> Result<FsOptions>

Initialize the file system. Read more
source§

fn getattr( &self, _ctx: &Context, inode: Self::Inode, _handle: Option<Self::Handle>, ) -> Result<(stat64, Duration)>

Get attributes for a file / directory. Read more
source§

fn lookup( &self, _ctx: &Context, parent: Self::Inode, name: &CStr, ) -> Result<Entry>

Look up a directory entry by name and get its attributes. Read more
source§

fn opendir( &self, _ctx: &Context, inode: Self::Inode, _flags: u32, ) -> Result<(Option<Self::Handle>, OpenOptions)>

Open a directory for reading. Read more
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<()>

Read a directory. Read more
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<()>

Read a directory with entry attributes. Read more
source§

fn releasedir( &self, _ctx: &Context, inode: Self::Inode, _flags: u32, handle: Self::Handle, ) -> Result<()>

Release an open directory. Read more
source§

fn open( &self, _ctx: &Context, inode: Self::Inode, _flags: u32, _fuse_flags: u32, ) -> Result<(Option<Self::Handle>, OpenOptions, Option<u32>)>

Open a file. Read more
source§

fn release( &self, _ctx: &Context, inode: Self::Inode, _flags: u32, handle: Self::Handle, _flush: bool, _flock_release: bool, _lock_owner: Option<u64>, ) -> Result<()>

Release an open file. Read more
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>

Read data from a file. Read more
Read a symbolic link.
source§

fn getxattr( &self, _ctx: &Context, inode: Self::Inode, name: &CStr, size: u32, ) -> Result<GetxattrReply>

Get an extended attribute. Read more
source§

fn listxattr( &self, _ctx: &Context, inode: Self::Inode, size: u32, ) -> Result<ListxattrReply>

List extended attribute names. Read more
source§

fn destroy(&self)

Clean up the file system. Read more
source§

fn forget(&self, ctx: &Context, inode: Self::Inode, count: u64)

Forget about an inode. Read more
source§

fn batch_forget(&self, ctx: &Context, requests: Vec<(Self::Inode, u64)>)

Forget about multiple inodes. Read more
source§

fn setattr( &self, ctx: &Context, inode: Self::Inode, attr: stat64, handle: Option<Self::Handle>, valid: SetattrValid, ) -> Result<(stat64, Duration), Error>

Set attributes for a file / directory. Read more
Create a symbolic link. Read more
source§

fn mknod( &self, ctx: &Context, inode: Self::Inode, name: &CStr, mode: u32, rdev: u32, umask: u32, ) -> Result<Entry, Error>

Create a file node. Read more
source§

fn mkdir( &self, ctx: &Context, parent: Self::Inode, name: &CStr, mode: u32, umask: u32, ) -> Result<Entry, Error>

Create a directory. Read more
Remove a file. Read more
source§

fn rmdir( &self, ctx: &Context, parent: Self::Inode, name: &CStr, ) -> Result<(), Error>

Remove a directory. Read more
source§

fn rename( &self, ctx: &Context, olddir: Self::Inode, oldname: &CStr, newdir: Self::Inode, newname: &CStr, flags: u32, ) -> Result<(), Error>

Rename a file / directory. Read more
Create a hard link. Read more
source§

fn create( &self, ctx: &Context, parent: Self::Inode, name: &CStr, args: CreateIn, ) -> Result<(Entry, Option<Self::Handle>, OpenOptions, Option<u32>), Error>

Create and open a file. Read more
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>

Write data to a file. Read more
source§

fn flush( &self, ctx: &Context, inode: Self::Inode, handle: Self::Handle, lock_owner: u64, ) -> Result<(), Error>

Flush the contents of a file. Read more
source§

fn fsync( &self, ctx: &Context, inode: Self::Inode, datasync: bool, handle: Self::Handle, ) -> Result<(), Error>

Synchronize file contents. Read more
source§

fn fallocate( &self, ctx: &Context, inode: Self::Inode, handle: Self::Handle, mode: u32, offset: u64, length: u64, ) -> Result<(), Error>

Allocate requested space for file data. Read more
source§

fn statfs(&self, ctx: &Context, inode: Self::Inode) -> Result<statvfs64, Error>

Get information about the file system.
source§

fn setxattr( &self, ctx: &Context, inode: Self::Inode, name: &CStr, value: &[u8], flags: u32, ) -> Result<(), Error>

Set an extended attribute. Read more
source§

fn removexattr( &self, ctx: &Context, inode: Self::Inode, name: &CStr, ) -> Result<(), Error>

Remove an extended attribute. Read more
source§

fn fsyncdir( &self, ctx: &Context, inode: Self::Inode, datasync: bool, handle: Self::Handle, ) -> Result<(), Error>

Synchronize the contents of a directory. Read more
source§

fn access( &self, ctx: &Context, inode: Self::Inode, mask: u32, ) -> Result<(), Error>

Check file access permissions. Read more
source§

fn lseek( &self, ctx: &Context, inode: Self::Inode, handle: Self::Handle, offset: u64, whence: u32, ) -> Result<u64, Error>

Reposition read/write file offset.
source§

fn getlk( &self, ctx: &Context, inode: Self::Inode, handle: Self::Handle, owner: u64, lock: FileLock, flags: u32, ) -> Result<FileLock, Error>

Query file lock status
source§

fn setlk( &self, ctx: &Context, inode: Self::Inode, handle: Self::Handle, owner: u64, lock: FileLock, flags: u32, ) -> Result<(), Error>

Grab a file read lock
source§

fn setlkw( &self, ctx: &Context, inode: Self::Inode, handle: Self::Handle, owner: u64, lock: FileLock, flags: u32, ) -> Result<(), Error>

Grab a file write lock
source§

fn ioctl( &self, ctx: &Context, inode: Self::Inode, handle: Self::Handle, flags: u32, cmd: u32, data: IoctlData<'_>, out_size: u32, ) -> Result<IoctlData<'_>, Error>

send ioctl to the file
source§

fn bmap( &self, ctx: &Context, inode: Self::Inode, block: u64, blocksize: u32, ) -> Result<u64, Error>

Query a file’s block mapping info
source§

fn poll( &self, ctx: &Context, inode: Self::Inode, handle: Self::Handle, khandle: Self::Handle, flags: u32, events: u32, ) -> Result<u32, Error>

Poll a file’s events
source§

fn notify_reply(&self) -> Result<(), Error>

TODO: support this
source§

fn id_remap(&self, ctx: &mut Context) -> Result<(), Error>

Remap the external IDs in context to internal IDs.

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>
where BS: Send, DS: Send, RN: Send,

§

impl<BS, DS, RN> Sync for TvixStoreFs<BS, DS, RN>
where BS: Sync, DS: Sync, RN: Sync,

§

impl<BS, DS, RN> Unpin for TvixStoreFs<BS, DS, RN>
where BS: Unpin, DS: Unpin, RN: Unpin,

§

impl<BS, DS, RN> !UnwindSafe for TvixStoreFs<BS, DS, RN>

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T> FutureExt for T

source§

fn with_context(self, otel_cx: Context) -> WithContext<Self>

Attaches the provided Context to this type, returning a WithContext wrapper. Read more
source§

fn with_current_context(self) -> WithContext<Self>

Attaches the current Context to this type, returning a WithContext wrapper. Read more
source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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 more
source§

impl<T> IntoRequest<T> for T

source§

fn into_request(self) -> Request<T>

Wrap the input message T in a tonic::Request
source§

impl<T> Pointable for T

source§

const ALIGN: usize = _

The alignment of pointer.
source§

type Init = T

The type for initializers.
source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
source§

impl<T> Same for T

source§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

source§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

source§

fn vzip(self) -> V

source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more