about summary refs log tree commit diff
path: root/tvix/store/src
diff options
context:
space:
mode:
authorFlorian Klink <flokli@flokli.de>2023-09-17T11·36+0300
committerflokli <flokli@flokli.de>2023-09-18T10·33+0000
commitbf2fe88a5c0c7f778502f87ddacb5072919d455e (patch)
treef51d69afa6821ffab0f141ff39b5fd3ba337f148 /tvix/store/src
parentda6cbb4a459d02111c44a67d3d0dd7e654abff23 (diff)
fix(tvix/store/fuse): fix executable bit r/6607
We were blindly returning 0o444 for all regular files, but regular files
with executable bit need to be 0o555.

This wasn't spotted because stat'ing executable files was not part of
the test suite, it's now added.

Change-Id: I04c69784053e7e43d838c01bb288f2df48f40b4e
Reviewed-on: https://cl.tvl.fyi/c/depot/+/9345
Tested-by: BuildkiteCI
Reviewed-by: raitobezarius <tvl@lahfa.xyz>
Reviewed-by: Connor Brewster <cbrewster@hey.com>
Diffstat (limited to 'tvix/store/src')
-rw-r--r--tvix/store/src/fuse/file_attr.rs3
-rw-r--r--tvix/store/src/fuse/tests.rs36
2 files changed, 38 insertions, 1 deletions
diff --git a/tvix/store/src/fuse/file_attr.rs b/tvix/store/src/fuse/file_attr.rs
index b2b971d9e409..25cfd28dd1f9 100644
--- a/tvix/store/src/fuse/file_attr.rs
+++ b/tvix/store/src/fuse/file_attr.rs
@@ -43,7 +43,8 @@ pub fn gen_file_attr(inode_data: &InodeData, inode: u64) -> FileAttr {
         crtime: SystemTime::UNIX_EPOCH,
         kind: inode_data.into(),
         perm: match inode_data {
-            InodeData::Regular(..) => 0o444,
+            InodeData::Regular(_, _, false) => 0o444, // no-executable files
+            InodeData::Regular(_, _, true) => 0o555,  // executable files
             InodeData::Symlink(_) => 0o444,
             InodeData::Directory(..) => 0o555,
         },
diff --git a/tvix/store/src/fuse/tests.rs b/tvix/store/src/fuse/tests.rs
index 29856433b38c..015e27ee9988 100644
--- a/tvix/store/src/fuse/tests.rs
+++ b/tvix/store/src/fuse/tests.rs
@@ -16,6 +16,7 @@ use crate::{proto, FUSE};
 
 const BLOB_A_NAME: &str = "00000000000000000000000000000000-test";
 const BLOB_B_NAME: &str = "55555555555555555555555555555555-test";
+const HELLOWORLD_BLOB_NAME: &str = "66666666666666666666666666666666-test";
 const SYMLINK_NAME: &str = "11111111111111111111111111111111-test";
 const SYMLINK_NAME2: &str = "44444444444444444444444444444444-test";
 const DIRECTORY_WITH_KEEP_NAME: &str = "22222222222222222222222222222222-test";
@@ -103,6 +104,37 @@ async fn populate_blob_b(
     path_info_service.put(path_info).expect("must succeed");
 }
 
+/// adds a blob containing helloworld and marks it as executable
+async fn populate_helloworld_blob(
+    blob_service: &Arc<dyn BlobService>,
+    _directory_service: &Arc<dyn DirectoryService>,
+    path_info_service: &Arc<dyn PathInfoService>,
+) {
+    // Upload BLOB_B
+    let mut bw = blob_service.open_write().await;
+    tokio::io::copy(
+        &mut Cursor::new(fixtures::HELLOWORLD_BLOB_CONTENTS.to_vec()),
+        &mut bw,
+    )
+    .await
+    .expect("must succeed uploading");
+    bw.close().await.expect("must succeed closing");
+
+    // Create a PathInfo for it
+    let path_info = PathInfo {
+        node: Some(proto::Node {
+            node: Some(proto::node::Node::File(FileNode {
+                name: HELLOWORLD_BLOB_NAME.into(),
+                digest: fixtures::HELLOWORLD_BLOB_DIGEST.clone().into(),
+                size: fixtures::HELLOWORLD_BLOB_CONTENTS.len() as u32,
+                executable: true,
+            })),
+        }),
+        ..Default::default()
+    };
+    path_info_service.put(path_info).expect("must succeed");
+}
+
 fn populate_symlink(
     _blob_service: &Arc<dyn BlobService>,
     _directory_service: &Arc<dyn DirectoryService>,
@@ -760,6 +792,7 @@ async fn check_attributes() {
     populate_blob_a(&blob_service, &directory_service, &path_info_service).await;
     populate_directory_with_keep(&blob_service, &directory_service, &path_info_service).await;
     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(
         blob_service,
@@ -773,14 +806,17 @@ async fn check_attributes() {
     let p_file = tmpdir.path().join(BLOB_A_NAME);
     let p_directory = tmpdir.path().join(DIRECTORY_WITH_KEEP_NAME);
     let p_symlink = tmpdir.path().join(SYMLINK_NAME);
+    let p_executable_file = tmpdir.path().join(HELLOWORLD_BLOB_NAME);
 
     // peek at metadata. We use symlink_metadata to ensure we don't traverse a symlink by accident.
     let metadata_file = fs::symlink_metadata(&p_file).expect("must succeed");
+    let metadata_executable_file = fs::symlink_metadata(&p_executable_file).expect("must succeed");
     let metadata_directory = fs::symlink_metadata(&p_directory).expect("must succeed");
     let metadata_symlink = fs::symlink_metadata(&p_symlink).expect("must succeed");
 
     // modes should match. We & with 0o777 to remove any higher bits.
     assert_eq!(0o444, metadata_file.mode() & 0o777);
+    assert_eq!(0o555, metadata_executable_file.mode() & 0o777);
     assert_eq!(0o555, metadata_directory.mode() & 0o777);
     assert_eq!(0o444, metadata_symlink.mode() & 0o777);