about summary refs log tree commit diff
path: root/tvix/store/src/fuse/tests.rs
diff options
context:
space:
mode:
authorConnor Brewster <cbrewster@hey.com>2023-09-16T15·20-0500
committerConnor Brewster <cbrewster@hey.com>2023-09-20T14·21+0000
commit6b7c936bc50934b45df132f659292e2c45256dea (patch)
tree74b369688e63ed4ff34c9270584ba30b91d7c979 /tvix/store/src/fuse/tests.rs
parent237c0eb415c632ec3480d890084c3d815298ac1f (diff)
refactor(tvix/store/fuse): Switch from fuser to fuse-backend-rs r/6621
This switches the FUSE implementation from fuser to fuse-backend-rs.
fuse-backend-rs is designed to work with both FUSE and virtiofs.
Virtiofs support will make it possible to plug the tvix-store into a
microvm and have `/nix/store` access without having to setup FUSE inside
the guest.

Additionally fuse-backend-rs has nice support for running multiple FUSE
threads and has some async support.

The goal of this commit is to mechanically switch over to
fuse-backend-rs with minimal changes. I did have to add some locks here
and there because fuse-backend-rs uses `&self` on all methods whereas
fuser uses `&mut self`. `&self` is required for concurrent access to the
FUSE server, so this makes sense.

We can consider switching to concurrent maps and use some other
techniques to reduce lock contention and critical section size.

Issue: https://b.tvl.fyi/issues/305

Change-Id: Icde5a58c6eef98f8984c1e04e980b756dfb76b47
Reviewed-on: https://cl.tvl.fyi/c/depot/+/9341
Tested-by: BuildkiteCI
Reviewed-by: flokli <flokli@flokli.de>
Diffstat (limited to 'tvix/store/src/fuse/tests.rs')
-rw-r--r--tvix/store/src/fuse/tests.rs92
1 files changed, 46 insertions, 46 deletions
diff --git a/tvix/store/src/fuse/tests.rs b/tvix/store/src/fuse/tests.rs
index 015e27ee99..81de8b13de 100644
--- a/tvix/store/src/fuse/tests.rs
+++ b/tvix/store/src/fuse/tests.rs
@@ -12,7 +12,7 @@ use crate::pathinfoservice::PathInfoService;
 use crate::proto::{DirectoryNode, FileNode, PathInfo};
 use crate::tests::fixtures;
 use crate::tests::utils::{gen_blob_service, gen_directory_service, gen_pathinfo_service};
-use crate::{proto, FUSE};
+use crate::{proto, FuseDaemon, FUSE};
 
 const BLOB_A_NAME: &str = "00000000000000000000000000000000-test";
 const BLOB_B_NAME: &str = "55555555555555555555555555555555-test";
@@ -40,14 +40,14 @@ fn do_mount<P: AsRef<Path>>(
     path_info_service: Arc<dyn PathInfoService>,
     mountpoint: P,
     list_root: bool,
-) -> io::Result<fuser::BackgroundSession> {
+) -> io::Result<FuseDaemon> {
     let fs = FUSE::new(
         blob_service,
         directory_service,
         path_info_service,
         list_root,
     );
-    fuser::spawn_mount2(fs, mountpoint, &[])
+    FuseDaemon::new(fs, mountpoint.as_ref(), 4)
 }
 
 async fn populate_blob_a(
@@ -294,7 +294,7 @@ async fn mount() {
     let tmpdir = TempDir::new().unwrap();
 
     let (blob_service, directory_service, path_info_service) = gen_svcs();
-    let fuser_session = do_mount(
+    let mut fuse_daemon = do_mount(
         blob_service,
         directory_service,
         path_info_service,
@@ -303,7 +303,7 @@ async fn mount() {
     )
     .expect("must succeed");
 
-    fuser_session.join()
+    fuse_daemon.unmount().expect("unmount");
 }
 
 /// Ensure listing the root isn't allowed
@@ -317,7 +317,7 @@ async fn root() {
     let tmpdir = TempDir::new().unwrap();
 
     let (blob_service, directory_service, path_info_service) = gen_svcs();
-    let fuser_session = do_mount(
+    let mut fuse_daemon = do_mount(
         blob_service,
         directory_service,
         path_info_service,
@@ -334,7 +334,7 @@ async fn root() {
         assert_eq!(std::io::ErrorKind::PermissionDenied, err.kind());
     }
 
-    fuser_session.join()
+    fuse_daemon.unmount().expect("unmount");
 }
 
 /// Ensure listing the root is allowed if configured explicitly
@@ -350,7 +350,7 @@ async fn root_with_listing() {
     let (blob_service, directory_service, path_info_service) = gen_svcs();
     populate_blob_a(&blob_service, &directory_service, &path_info_service).await;
 
-    let fuser_session = do_mount(
+    let mut fuse_daemon = do_mount(
         blob_service,
         directory_service,
         path_info_service,
@@ -371,7 +371,7 @@ async fn root_with_listing() {
         assert_eq!(fixtures::BLOB_A.len() as u64, metadata.len());
     }
 
-    fuser_session.join()
+    fuse_daemon.unmount().expect("unmount");
 }
 
 /// Ensure we can stat a file at the root
@@ -387,7 +387,7 @@ async fn stat_file_at_root() {
     let (blob_service, directory_service, path_info_service) = gen_svcs();
     populate_blob_a(&blob_service, &directory_service, &path_info_service).await;
 
-    let fuser_session = do_mount(
+    let mut fuse_daemon = do_mount(
         blob_service,
         directory_service,
         path_info_service,
@@ -405,7 +405,7 @@ async fn stat_file_at_root() {
     assert!(metadata.permissions().readonly());
     assert_eq!(fixtures::BLOB_A.len() as u64, metadata.len());
 
-    fuser_session.join()
+    fuse_daemon.unmount().expect("unmount");
 }
 
 /// Ensure we can read a file at the root
@@ -421,7 +421,7 @@ async fn read_file_at_root() {
     let (blob_service, directory_service, path_info_service) = gen_svcs();
     populate_blob_a(&blob_service, &directory_service, &path_info_service).await;
 
-    let fuser_session = do_mount(
+    let mut fuse_daemon = do_mount(
         blob_service,
         directory_service,
         path_info_service,
@@ -439,7 +439,7 @@ async fn read_file_at_root() {
     assert_eq!(fixtures::BLOB_A.len(), data.len());
     assert_eq!(fixtures::BLOB_A.to_vec(), data);
 
-    fuser_session.join()
+    fuse_daemon.unmount().expect("unmount");
 }
 
 /// Ensure we can read a large file at the root
@@ -455,7 +455,7 @@ async fn read_large_file_at_root() {
     let (blob_service, directory_service, path_info_service) = gen_svcs();
     populate_blob_b(&blob_service, &directory_service, &path_info_service).await;
 
-    let fuser_session = do_mount(
+    let mut fuse_daemon = do_mount(
         blob_service,
         directory_service,
         path_info_service,
@@ -481,7 +481,7 @@ async fn read_large_file_at_root() {
     assert_eq!(fixtures::BLOB_B.len(), data.len());
     assert_eq!(fixtures::BLOB_B.to_vec(), data);
 
-    fuser_session.join()
+    fuse_daemon.unmount().expect("unmount");
 }
 
 /// Read the target of a symlink
@@ -497,7 +497,7 @@ async fn symlink_readlink() {
     let (blob_service, directory_service, path_info_service) = gen_svcs();
     populate_symlink(&blob_service, &directory_service, &path_info_service);
 
-    let fuser_session = do_mount(
+    let mut fuse_daemon = do_mount(
         blob_service,
         directory_service,
         path_info_service,
@@ -524,7 +524,7 @@ async fn symlink_readlink() {
     let e = fs::read(p).expect_err("must fail");
     assert_eq!(std::io::ErrorKind::NotFound, e.kind());
 
-    fuser_session.join()
+    fuse_daemon.unmount().expect("unmount");
 }
 
 /// Read and stat a regular file through a symlink pointing to it.
@@ -541,7 +541,7 @@ async fn read_stat_through_symlink() {
     populate_blob_a(&blob_service, &directory_service, &path_info_service).await;
     populate_symlink(&blob_service, &directory_service, &path_info_service);
 
-    let fuser_session = do_mount(
+    let mut fuse_daemon = do_mount(
         blob_service,
         directory_service,
         path_info_service,
@@ -567,7 +567,7 @@ async fn read_stat_through_symlink() {
         std::fs::read(p_symlink).expect("must succeed"),
     );
 
-    fuser_session.join()
+    fuse_daemon.unmount().expect("unmount");
 }
 
 /// Read a directory in the root, and validate some attributes.
@@ -583,7 +583,7 @@ async fn read_stat_directory() {
     let (blob_service, directory_service, path_info_service) = gen_svcs();
     populate_directory_with_keep(&blob_service, &directory_service, &path_info_service).await;
 
-    let fuser_session = do_mount(
+    let mut fuse_daemon = do_mount(
         blob_service,
         directory_service,
         path_info_service,
@@ -599,7 +599,7 @@ async fn read_stat_directory() {
     assert!(metadata.is_dir());
     assert!(metadata.permissions().readonly());
 
-    fuser_session.join()
+    fuse_daemon.unmount().expect("unmount");
 }
 
 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
@@ -615,7 +615,7 @@ async fn read_blob_inside_dir() {
     let (blob_service, directory_service, path_info_service) = gen_svcs();
     populate_directory_with_keep(&blob_service, &directory_service, &path_info_service).await;
 
-    let fuser_session = do_mount(
+    let mut fuse_daemon = do_mount(
         blob_service,
         directory_service,
         path_info_service,
@@ -635,7 +635,7 @@ async fn read_blob_inside_dir() {
     let data = fs::read(&p).expect("must succeed");
     assert_eq!(fixtures::EMPTY_BLOB_CONTENTS.to_vec(), data);
 
-    fuser_session.join()
+    fuse_daemon.unmount().expect("unmount");
 }
 
 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
@@ -652,7 +652,7 @@ async fn read_blob_deep_inside_dir() {
     let (blob_service, directory_service, path_info_service) = gen_svcs();
     populate_directory_complicated(&blob_service, &directory_service, &path_info_service).await;
 
-    let fuser_session = do_mount(
+    let mut fuse_daemon = do_mount(
         blob_service,
         directory_service,
         path_info_service,
@@ -676,7 +676,7 @@ async fn read_blob_deep_inside_dir() {
     let data = fs::read(&p).expect("must succeed");
     assert_eq!(fixtures::EMPTY_BLOB_CONTENTS.to_vec(), data);
 
-    fuser_session.join()
+    fuse_daemon.unmount().expect("unmount");
 }
 
 /// Ensure readdir works.
@@ -692,7 +692,7 @@ async fn readdir() {
     let (blob_service, directory_service, path_info_service) = gen_svcs();
     populate_directory_complicated(&blob_service, &directory_service, &path_info_service).await;
 
-    let fuser_session = do_mount(
+    let mut fuse_daemon = do_mount(
         blob_service,
         directory_service,
         path_info_service,
@@ -732,7 +732,7 @@ async fn readdir() {
         assert!(e.file_type().expect("must succeed").is_dir());
     }
 
-    fuser_session.join()
+    fuse_daemon.unmount().expect("unmount");
 }
 
 #[tokio::test]
@@ -748,7 +748,7 @@ async fn readdir_deep() {
     let (blob_service, directory_service, path_info_service) = gen_svcs();
     populate_directory_complicated(&blob_service, &directory_service, &path_info_service).await;
 
-    let fuser_session = do_mount(
+    let mut fuse_daemon = do_mount(
         blob_service,
         directory_service,
         path_info_service,
@@ -775,7 +775,7 @@ async fn readdir_deep() {
         assert_eq!(0, e.metadata().expect("must succeed").len());
     }
 
-    fuser_session.join()
+    fuse_daemon.unmount().expect("unmount");
 }
 
 /// Check attributes match how they show up in /nix/store normally.
@@ -794,7 +794,7 @@ async fn check_attributes() {
     populate_symlink(&blob_service, &directory_service, &path_info_service);
     populate_helloworld_blob(&blob_service, &directory_service, &path_info_service).await;
 
-    let fuser_session = do_mount(
+    let mut fuse_daemon = do_mount(
         blob_service,
         directory_service,
         path_info_service,
@@ -840,7 +840,7 @@ async fn check_attributes() {
         // crtime seems MacOS only
     }
 
-    fuser_session.join()
+    fuse_daemon.unmount().expect("unmount");
 }
 
 #[tokio::test]
@@ -858,7 +858,7 @@ async fn compare_inodes_directories() {
     populate_directory_with_keep(&blob_service, &directory_service, &path_info_service).await;
     populate_directory_complicated(&blob_service, &directory_service, &path_info_service).await;
 
-    let fuser_session = do_mount(
+    let mut fuse_daemon = do_mount(
         blob_service,
         directory_service,
         path_info_service,
@@ -876,7 +876,7 @@ async fn compare_inodes_directories() {
         fs::metadata(p_sibling_dir).expect("must succeed").ino()
     );
 
-    fuser_session.join()
+    fuse_daemon.unmount().expect("unmount");
 }
 
 /// Ensure we allocate the same inodes for the same directory contents.
@@ -893,7 +893,7 @@ async fn compare_inodes_files() {
     let (blob_service, directory_service, path_info_service) = gen_svcs();
     populate_directory_complicated(&blob_service, &directory_service, &path_info_service).await;
 
-    let fuser_session = do_mount(
+    let mut fuse_daemon = do_mount(
         blob_service,
         directory_service,
         path_info_service,
@@ -915,7 +915,7 @@ async fn compare_inodes_files() {
         fs::metadata(p_keep2).expect("must succeed").ino()
     );
 
-    fuser_session.join()
+    fuse_daemon.unmount().expect("unmount");
 }
 
 /// Ensure we allocate the same inode for symlinks pointing to the same targets.
@@ -933,7 +933,7 @@ async fn compare_inodes_symlinks() {
     populate_directory_complicated(&blob_service, &directory_service, &path_info_service).await;
     populate_symlink2(&blob_service, &directory_service, &path_info_service);
 
-    let fuser_session = do_mount(
+    let mut fuse_daemon = do_mount(
         blob_service,
         directory_service,
         path_info_service,
@@ -951,7 +951,7 @@ async fn compare_inodes_symlinks() {
         fs::symlink_metadata(p2).expect("must succeed").ino()
     );
 
-    fuser_session.join()
+    fuse_daemon.unmount().expect("unmount");
 }
 
 /// Check we match paths exactly.
@@ -967,7 +967,7 @@ async fn read_wrong_paths_in_root() {
     let (blob_service, directory_service, path_info_service) = gen_svcs();
     populate_blob_a(&blob_service, &directory_service, &path_info_service).await;
 
-    let fuser_session = do_mount(
+    let mut fuse_daemon = do_mount(
         blob_service,
         directory_service,
         path_info_service,
@@ -1000,7 +1000,7 @@ async fn read_wrong_paths_in_root() {
         .join("00000000000000000000000000000000-tes")
         .exists());
 
-    fuser_session.join()
+    fuse_daemon.unmount().expect("unmount");
 }
 
 /// Make sure writes are not allowed
@@ -1016,7 +1016,7 @@ async fn disallow_writes() {
 
     let (blob_service, directory_service, path_info_service) = gen_svcs();
 
-    let fuser_session = do_mount(
+    let mut fuse_daemon = do_mount(
         blob_service,
         directory_service,
         path_info_service,
@@ -1028,9 +1028,9 @@ async fn disallow_writes() {
     let p = tmpdir.path().join(BLOB_A_NAME);
     let e = std::fs::File::create(p).expect_err("must fail");
 
-    assert_eq!(std::io::ErrorKind::Unsupported, e.kind());
+    assert_eq!(Some(libc::EROFS), e.raw_os_error());
 
-    fuser_session.join()
+    fuse_daemon.unmount().expect("unmount");
 }
 
 #[tokio::test]
@@ -1045,7 +1045,7 @@ async fn missing_directory() {
     let (blob_service, directory_service, path_info_service) = gen_svcs();
     populate_pathinfo_without_directory(&blob_service, &directory_service, &path_info_service);
 
-    let fuser_session = do_mount(
+    let mut fuse_daemon = do_mount(
         blob_service,
         directory_service,
         path_info_service,
@@ -1073,7 +1073,7 @@ async fn missing_directory() {
         fs::metadata(p.join(".keep")).expect_err("must fail");
     }
 
-    fuser_session.join()
+    fuse_daemon.unmount().expect("unmount");
 }
 
 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
@@ -1088,7 +1088,7 @@ async fn missing_blob() {
     let (blob_service, directory_service, path_info_service) = gen_svcs();
     populate_blob_a_without_blob(&blob_service, &directory_service, &path_info_service);
 
-    let fuser_session = do_mount(
+    let mut fuse_daemon = do_mount(
         blob_service,
         directory_service,
         path_info_service,
@@ -1109,5 +1109,5 @@ async fn missing_blob() {
         fs::read(p).expect_err("must fail");
     }
 
-    fuser_session.join()
+    fuse_daemon.unmount().expect("unmount");
 }