about summary refs log tree commit diff
path: root/tvix/store/src/fs/inode_tracker.rs
diff options
context:
space:
mode:
authorFlorian Klink <flokli@flokli.de>2023-12-16T22·16+0200
committerflokli <flokli@flokli.de>2023-12-22T16·55+0000
commita5167c508cf2ed92f8a39696a6b4376cf25ee872 (patch)
tree5ffb2f8d0d331b6fea1aeb4f6391e0408df6d234 /tvix/store/src/fs/inode_tracker.rs
parent52cad8619511b97c4bcd5768ce9b3579ff665505 (diff)
chore(tvix): move store/fs to castore/fs r/7256
With the recent introduction of the RootNodes trait, there's nothing in
the fs module pulling in tvix-store dependencies, so it can live in
tvix-castore.

This allows other crates to make use of TvixStoreFS, without having to
pull in tvix-store.

For example, a tvix-build using a fuse mountpoint at /nix/store doesn't
need a PathInfoService to hold the root nodes that should be present,
but just a list.

tvix-store now has a pathinfoservice/fs module, which contains the
necessary glue logic to implement the RootNodes trait for a
PathInfoService.

To satisfy Rust orphan rules for trait implementations, we had to add a
small wrapper struct. It's mostly hidden away by the make_fs helper
function returning a TvixStoreFs.

It can't be entirely private, as its still leaking into the concrete
type of TvixStoreFS.

tvix-store still has `fuse` and `virtiofs` features, but they now simply
enable these features in the `tvix-castore` crate they depend on.

The tests for the fuse functionality stay in tvix-store for now, as
they populate the root nodes through a PathInfoService.

Once above mentioned "list of root nodes" implementation exists, we
might want to shuffle this around one more time.

Fixes b/341.

Change-Id: I989f664827a5a361b23b34368d242d10c157c756
Reviewed-on: https://cl.tvl.fyi/c/depot/+/10378
Autosubmit: flokli <flokli@flokli.de>
Tested-by: BuildkiteCI
Reviewed-by: sterni <sternenseemann@systemli.org>
Diffstat (limited to 'tvix/store/src/fs/inode_tracker.rs')
-rw-r--r--tvix/store/src/fs/inode_tracker.rs207
1 files changed, 0 insertions, 207 deletions
diff --git a/tvix/store/src/fs/inode_tracker.rs b/tvix/store/src/fs/inode_tracker.rs
deleted file mode 100644
index 3cabbbd247b5..000000000000
--- a/tvix/store/src/fs/inode_tracker.rs
+++ /dev/null
@@ -1,207 +0,0 @@
-use std::{collections::HashMap, sync::Arc};
-
-use super::inodes::{DirectoryInodeData, InodeData};
-use tvix_castore::B3Digest;
-
-/// InodeTracker keeps track of inodes, stores data being these inodes and deals
-/// with inode allocation.
-pub struct InodeTracker {
-    data: HashMap<u64, Arc<InodeData>>,
-
-    // lookup table for blobs by their B3Digest
-    blob_digest_to_inode: HashMap<B3Digest, u64>,
-
-    // lookup table for symlinks by their target
-    symlink_target_to_inode: HashMap<bytes::Bytes, u64>,
-
-    // lookup table for directories by their B3Digest.
-    // Note the corresponding directory may not be present in data yet.
-    directory_digest_to_inode: HashMap<B3Digest, u64>,
-
-    // the next inode to allocate
-    next_inode: u64,
-}
-
-impl Default for InodeTracker {
-    fn default() -> Self {
-        Self {
-            data: Default::default(),
-
-            blob_digest_to_inode: Default::default(),
-            symlink_target_to_inode: Default::default(),
-            directory_digest_to_inode: Default::default(),
-
-            next_inode: 2,
-        }
-    }
-}
-
-impl InodeTracker {
-    // Retrieves data for a given inode, if it exists.
-    pub fn get(&self, ino: u64) -> Option<Arc<InodeData>> {
-        self.data.get(&ino).cloned()
-    }
-
-    // Replaces data for a given inode.
-    // Panics if the inode doesn't already exist.
-    pub fn replace(&mut self, ino: u64, data: Arc<InodeData>) {
-        if self.data.insert(ino, data).is_none() {
-            panic!("replace called on unknown inode");
-        }
-    }
-
-    // Stores data and returns the inode for it.
-    // In case an inode has already been allocated for the same data, that inode
-    // is returned, otherwise a new one is allocated.
-    // In case data is a [InodeData::Directory], inodes for all items are looked
-    // up
-    pub fn put(&mut self, data: InodeData) -> u64 {
-        match data {
-            InodeData::Regular(ref digest, _, _) => {
-                match self.blob_digest_to_inode.get(digest) {
-                    Some(found_ino) => {
-                        // We already have it, return the inode.
-                        *found_ino
-                    }
-                    None => self.insert_and_increment(data),
-                }
-            }
-            InodeData::Symlink(ref target) => {
-                match self.symlink_target_to_inode.get(target) {
-                    Some(found_ino) => {
-                        // We already have it, return the inode.
-                        *found_ino
-                    }
-                    None => self.insert_and_increment(data),
-                }
-            }
-            InodeData::Directory(DirectoryInodeData::Sparse(ref digest, _size)) => {
-                // check the lookup table if the B3Digest is known.
-                match self.directory_digest_to_inode.get(digest) {
-                    Some(found_ino) => {
-                        // We already have it, return the inode.
-                        *found_ino
-                    }
-                    None => {
-                        // insert and return the inode
-                        self.insert_and_increment(data)
-                    }
-                }
-            }
-            // Inserting [DirectoryInodeData::Populated] doesn't normally happen,
-            // only via [replace].
-            InodeData::Directory(DirectoryInodeData::Populated(..)) => {
-                unreachable!("should never be called with DirectoryInodeData::Populated")
-            }
-        }
-    }
-
-    // Inserts the data and returns the inode it was stored at, while
-    // incrementing next_inode.
-    fn insert_and_increment(&mut self, data: InodeData) -> u64 {
-        let ino = self.next_inode;
-        // insert into lookup tables
-        match data {
-            InodeData::Regular(ref digest, _, _) => {
-                self.blob_digest_to_inode.insert(digest.clone(), ino);
-            }
-            InodeData::Symlink(ref target) => {
-                self.symlink_target_to_inode.insert(target.clone(), ino);
-            }
-            InodeData::Directory(DirectoryInodeData::Sparse(ref digest, _size)) => {
-                self.directory_digest_to_inode.insert(digest.clone(), ino);
-            }
-            // This is currently not used outside test fixtures.
-            // Usually a [DirectoryInodeData::Sparse] is inserted and later
-            // "upgraded" with more data.
-            // However, as a future optimization, a lookup for a PathInfo could trigger a
-            // [DirectoryService::get_recursive()] request that "forks into
-            // background" and prepopulates all Directories in a closure.
-            InodeData::Directory(DirectoryInodeData::Populated(ref digest, _)) => {
-                self.directory_digest_to_inode.insert(digest.clone(), ino);
-            }
-        }
-        // Insert data
-        self.data.insert(ino, Arc::new(data));
-
-        // increment inode counter and return old inode.
-        self.next_inode += 1;
-        ino
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use crate::tests::fixtures;
-
-    use super::InodeData;
-    use super::InodeTracker;
-
-    /// Getting something non-existent should be none
-    #[test]
-    fn get_nonexistent() {
-        let inode_tracker = InodeTracker::default();
-        assert!(inode_tracker.get(1).is_none());
-    }
-
-    /// Put of a regular file should allocate a uid, which should be the same when inserting again.
-    #[test]
-    fn put_regular() {
-        let mut inode_tracker = InodeTracker::default();
-        let f = InodeData::Regular(
-            fixtures::BLOB_A_DIGEST.clone(),
-            fixtures::BLOB_A.len() as u64,
-            false,
-        );
-
-        // put it in
-        let ino = inode_tracker.put(f.clone());
-
-        // a get should return the right data
-        let data = inode_tracker.get(ino).expect("must be some");
-        match *data {
-            InodeData::Regular(ref digest, _, _) => {
-                assert_eq!(&fixtures::BLOB_A_DIGEST.clone(), digest);
-            }
-            InodeData::Symlink(_) | InodeData::Directory(..) => panic!("wrong type"),
-        }
-
-        // another put should return the same ino
-        assert_eq!(ino, inode_tracker.put(f));
-
-        // inserting another file should return a different ino
-        assert_ne!(
-            ino,
-            inode_tracker.put(InodeData::Regular(
-                fixtures::BLOB_B_DIGEST.clone(),
-                fixtures::BLOB_B.len() as u64,
-                false,
-            ))
-        );
-    }
-
-    // Put of a symlink should allocate a uid, which should be the same when inserting again
-    #[test]
-    fn put_symlink() {
-        let mut inode_tracker = InodeTracker::default();
-        let f = InodeData::Symlink("target".into());
-
-        // put it in
-        let ino = inode_tracker.put(f.clone());
-
-        // a get should return the right data
-        let data = inode_tracker.get(ino).expect("must be some");
-        match *data {
-            InodeData::Symlink(ref target) => {
-                assert_eq!(b"target".to_vec(), *target);
-            }
-            InodeData::Regular(..) | InodeData::Directory(..) => panic!("wrong type"),
-        }
-
-        // another put should return the same ino
-        assert_eq!(ino, inode_tracker.put(f));
-
-        // inserting another file should return a different ino
-        assert_ne!(ino, inode_tracker.put(InodeData::Symlink("target2".into())));
-    }
-}