Replies: 11 comments
-
Has anyone reported this to apple? |
Beta Was this translation helpful? Give feedback.
-
It has been reported and evidently fixed in 15? rust-lang/docker-rust#161 |
Beta Was this translation helpful? Give feedback.
-
Okay after a bit of digging I actually found out what the docker devs did (Huge Thank You 🙏). It's basically this kernel patch: [PATCH 9/9] hardlinks: drop the cache of the existing entry.From 85589340b82880f5e52bee98f96ef11ae412dbb5 Mon Sep 17 00:00:00 2001
From: Docker <[email protected]>
Date: Sun, 11 Feb 2024 09:06:12 +0000
Subject: [PATCH 9/9] hardlinks: drop the cache of the existing entry.
Under virtualization.framework's virtiofs, the inode numbers are synthetic.
When the hardlink is created, the inode of the original file sometimes
changes too which means cached lookups will fail.
Work around by dropping the cache of the existing entry.
This doesn't affect grpcfuse as the host inode numbers are exposed in the VM.
Tested with a reproducer like this:
```
touch a
mkdir b
cat > main.c << EOT
int one(){
int ret;
struct stat st;
ret = stat("b/b", &st);
if (ret == 0) {
unlink("b/b");
}
if (!stat("b/b", &st)){
fprintf(stderr, "b/b exists\n");
return 0;
}
if (stat("a", &st)){
fprintf(stderr, "a does not exist\n");
return 0;
}
struct timespec begin, end;
clock_gettime(CLOCK_MONOTONIC_RAW, &begin);
ret = link("a", "b/b");
if(ret == -1){
perror("link");
if (stat("a", &st)){
fprintf(stderr, "a does not exist\n");
} else {
fprintf(stderr, "a exists\n");
}
if (stat("b", &st)){
fprintf(stderr, "b does not exist\n");
} else {
fprintf(stderr, "b exists\n");
}
if (stat("b/b", &st)){
fprintf(stderr, "b/b does not exist\n");
} else {
fprintf(stderr, "b/b exists\n");
}
int retries = 0;
do {
ret = link("a", "b/b");
if (ret == 0) {
break;
}
retries++;
} while (1);
clock_gettime(CLOCK_MONOTONIC_RAW, &end);
double time_spent = (end.tv_nsec - begin.tv_nsec) / 1000000000.0 + (end.tv_sec - begin.tv_sec);
fprintf(stderr, "link succeeded after %d retries in %f seconds\n", retries, time_spent);
return 0;
}
int fd = open("b/b", O_RDONLY);
if(fd == -1){
perror("open");
return 0;
}
sendfile(1, fd, NULL, 16777216);
close(fd);
ret = unlink("b/b");
if(ret == -1){
perror("unlink");
return 0;
}
return 1;
}
void main(){
for (int i = 0;; i++){
if (!one()){
fprintf(stdout, "\nFailed on iteration %d\n", i);
break;
};
fprintf(stdout, ".");
fflush(stdout);
}
}
EOT
Signed-off-by: Docker <[email protected]>
---
fs/fuse/dir.c | 20 +++++++++++++++++---
fs/fuse/readdir.c | 3 +++
2 files changed, 20 insertions(+), 3 deletions(-)
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index d707e6987..05fd0fa27 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -258,6 +258,10 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
fuse_change_attributes(inode, &outarg.attr, NULL,
ATTR_TIMEOUT(&outarg),
attr_version);
+ if ((inode->i_nlink > 1) && (!S_ISDIR(inode->i_mode))){
+ /* This case happens a lot when using hardlinks */
+ outarg.entry_valid = 0;
+ }
fuse_change_entry_timeout(entry, &outarg);
} else if (inode) {
fi = get_fuse_inode(inode);
@@ -442,9 +446,12 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
goto out_err;
entry = newent ? newent : entry;
- if (outarg_valid)
+ if (outarg_valid) {
+ if (inode && (inode->i_nlink > 1) && (!S_ISDIR(inode->i_mode))){
+ outarg.entry_valid = 0;
+ }
fuse_change_entry_timeout(entry, &outarg);
- else
+ } else
fuse_invalidate_entry_cache(entry);
if (inode)
@@ -690,6 +697,9 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
}
kfree(forget);
d_instantiate(entry, inode);
+ if ((inode->i_nlink > 1) && (!S_ISDIR(inode->i_mode))){
+ outentry.entry_valid = 0;
+ }
fuse_change_entry_timeout(entry, &outentry);
fuse_dir_changed(dir);
err = finish_open(file, entry, generic_file_open);
@@ -819,7 +829,9 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
d = d_splice_alias(inode, entry);
if (IS_ERR(d))
return PTR_ERR(d);
-
+ if (args->opcode == FUSE_LINK){
+ outarg.entry_valid = 0;
+ }
if (d) {
fuse_change_entry_timeout(d, &outarg);
dput(d);
@@ -1101,6 +1113,7 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
struct fuse_link_in inarg;
struct inode *inode = d_inode(entry);
struct fuse_mount *fm = get_fuse_mount(inode);
+ struct fuse_entry_out not_valid = {0,0};
FUSE_ARGS(args);
memset(&inarg, 0, sizeof(inarg));
@@ -1111,6 +1124,7 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
args.in_args[0].value = &inarg;
args.in_args[1].size = newent->d_name.len + 1;
args.in_args[1].value = newent->d_name.name;
+ fuse_change_entry_timeout(entry, ¬_valid);
err = create_new_entry(fm, &args, newdir, newent, inode->i_mode);
if (!err)
fuse_update_ctime_in_cache(inode);
diff --git a/fs/fuse/readdir.c b/fs/fuse/readdir.c
index 9e6d587b3..ab282724f 100644
--- a/fs/fuse/readdir.c
+++ b/fs/fuse/readdir.c
@@ -256,6 +256,9 @@ static int fuse_direntplus_link(struct file *file,
}
if (fc->readdirplus_auto)
set_bit(FUSE_I_INIT_RDPLUS, &get_fuse_inode(inode)->state);
+ if ((inode->i_nlink > 1) && (!S_ISDIR(inode->i_mode))) {
+ o->entry_valid = 0;
+ }
fuse_change_entry_timeout(dentry, o);
dput(dentry);
--
2.43.0 |
Beta Was this translation helpful? Give feedback.
-
Quick update: I've built a local kernel with that patch applied (applies cleanly on 6.9.6) and it seems that it works as a temporary workaround. |
Beta Was this translation helpful? Give feedback.
-
@cynecx do you have a link to that git commit or mailing list entry? i can't find it anywhere |
Beta Was this translation helpful? Give feedback.
-
@kasperk81 You can find all the patches embedded in the docker vm itself under |
Beta Was this translation helpful? Give feedback.
-
For what it's worth, I was not able to reproduce with these steps on my mac, but I also did not get a cross build as in your logs |
Beta Was this translation helpful? Give feedback.
-
@cfergeau Oh yeah sorry about that. I was just being lazy and pasted another very related test-case's build output. But it's still really just a hello world with a single |
Beta Was this translation helpful? Give feedback.
-
A friendly reminder that this issue had no activity for 30 days. |
Beta Was this translation helpful? Give feedback.
-
I move this to a discussion as this is not a podman bug and we just ship the default fedora kernel so we have no way of patching the kernel. |
Beta Was this translation helpful? Give feedback.
-
I've just upgraded to macos Sequoia and tried to reproduce this again without any success. I've also tried compiling a larger rust project which also run without any issues. I guess I'll still continue to observe the current situation but it seems like Apple has actually fixed this! |
Beta Was this translation helpful? Give feedback.
-
Issue Description
It seems like there is a virtiofs issue somewhere (probably a bug in Virtualization.framework's virtiofs implementation) that causes file operations in the virtiofs mount to fail with "No such file or directory" errors.
Docker-for-mac had this same issue: docker/for-mac#7059. They've apparently fixed this somehow.
Steps to reproduce the issue
Steps to reproduce the issue
cargo new hello && cd hello
podman image pull rust:1-bookworm
podman run --rm -it -v $(pwd):/app -w /app rust:1-bookworm
cargo clean && cargo build
Describe the results you received
Describe the results you expected
The build commands should all run successfully. The build artifacts should be properly "visible".
podman info output
Beta Was this translation helpful? Give feedback.
All reactions