ANDROID: fuse-bpf: Fix recursion in fuse_copy_file_range
fuse_copy_file_range would recurse if the input file was backed, but the output file was not Simply return EXDEV to signal to the vfs layer to try alternate routes Bug: 414328228 Test: fuse_test (note causes identical recursion without the kernel fix) Change-Id: I1b62bc69f57ce00639b283e1d9269e2a9468bd14 Signed-off-by: Paul Lawrence <paullawrence@google.com>
This commit is contained in:
committed by
Treehugger Robot
parent
5838b5ac0a
commit
88680fe19e
@@ -496,9 +496,7 @@ int fuse_copy_file_range_backing(struct fuse_bpf_args *fa, struct file *file_in,
|
||||
if (backing_file_out)
|
||||
return vfs_copy_file_range(backing_file_in, fci->off_in, backing_file_out,
|
||||
fci->off_out, fci->len, fci->flags);
|
||||
else
|
||||
return vfs_copy_file_range(file_in, pos_in, file_out, pos_out, len,
|
||||
flags);
|
||||
return -EXDEV;
|
||||
}
|
||||
|
||||
void *fuse_copy_file_range_finalize(struct fuse_bpf_args *fa, struct file *file_in, loff_t pos_in,
|
||||
|
||||
@@ -2212,6 +2212,180 @@ out:
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that fuse passthrough correctly traverses a mount point on the lower fs
|
||||
*/
|
||||
static int copy_file_range_test(const char *mount_dir)
|
||||
{
|
||||
const char *backing = "backing";
|
||||
const char *user = "user";
|
||||
const char *src = "source";
|
||||
const char *dst = "dest";
|
||||
int fd = -1;
|
||||
char buf[4096] = {};
|
||||
int src_fd = -1;
|
||||
int bpf_fd = -1;
|
||||
int fuse_dev = -1;
|
||||
int result = TEST_FAILURE;
|
||||
int backing_src = -1;
|
||||
int backing_dst = -1;
|
||||
int user_src = -1;
|
||||
int user_dst = -1;
|
||||
FUSE_DECLARE_DAEMON;
|
||||
|
||||
TESTSYSCALL(s_mkdir(s_path(s(ft_src), s(backing)), 0777));
|
||||
TESTSYSCALL(s_mkdir(s_path(s(ft_src), s(user)), 0777));
|
||||
TEST(fd = s_creat(s_pathn(3, s(ft_src), s(backing), s(src)), 0777),
|
||||
fd != -1);
|
||||
TESTEQUAL(write(fd, buf, sizeof(buf)), sizeof(buf));
|
||||
TESTSYSCALL(close(fd));
|
||||
TEST(fd = s_creat(s_pathn(3, s(ft_src), s(backing), s(dst)), 0777),
|
||||
fd != -1);
|
||||
TESTEQUAL(write(fd, buf, sizeof(buf)), sizeof(buf));
|
||||
TESTSYSCALL(close(fd));
|
||||
fd = -1;
|
||||
|
||||
TEST(src_fd = open(ft_src, O_DIRECTORY | O_RDONLY | O_CLOEXEC),
|
||||
src_fd != -1);
|
||||
TESTEQUAL(install_elf_bpf("test_bpf.bpf", "copy_file_range_test",
|
||||
&bpf_fd, NULL, NULL), 0);
|
||||
TESTEQUAL(mount_fuse(mount_dir, bpf_fd, src_fd, &fuse_dev), 0);
|
||||
|
||||
FUSE_START_DAEMON();
|
||||
if (action) {
|
||||
off_t off_in, off_out;
|
||||
|
||||
TEST(backing_src = s_open(s_pathn(3, s(mount_dir), s(backing),
|
||||
s(src)),
|
||||
O_RDONLY),
|
||||
backing_src != -1);
|
||||
TEST(backing_dst = s_open(s_pathn(3, s(mount_dir), s(backing),
|
||||
s(dst)),
|
||||
O_RDWR),
|
||||
backing_dst != -1);
|
||||
TEST(user_src = s_open(s_pathn(3, s(mount_dir), s(user),
|
||||
s(src)),
|
||||
O_RDONLY),
|
||||
user_src != -1);
|
||||
TEST(user_dst = s_open(s_pathn(3, s(mount_dir), s(user),
|
||||
s(dst)),
|
||||
O_RDWR),
|
||||
user_dst != -1);
|
||||
off_in = 0; off_out = 0;
|
||||
TESTEQUAL(copy_file_range(backing_src, &off_in, backing_dst,
|
||||
&off_out, sizeof(buf), 0),
|
||||
sizeof(buf));
|
||||
off_in = 0; off_out = 0;
|
||||
TESTEQUAL(copy_file_range(user_src, &off_in,
|
||||
backing_dst, &off_out, sizeof(buf), 0),
|
||||
sizeof(buf));
|
||||
off_in = 0; off_out = 0;
|
||||
TESTEQUAL(copy_file_range(backing_src, &off_in, user_dst,
|
||||
&off_out, sizeof(buf), 0),
|
||||
sizeof(buf));
|
||||
off_in = 0; off_out = 0;
|
||||
TESTEQUAL(copy_file_range(user_src, &off_in, user_dst, &off_out,
|
||||
sizeof(buf), 0),
|
||||
sizeof(buf));
|
||||
TESTSYSCALL(close(backing_src));
|
||||
backing_src = -1;
|
||||
TESTSYSCALL(close(backing_dst));
|
||||
backing_dst = -1;
|
||||
TESTSYSCALL(close(user_src));
|
||||
user_src = -1;
|
||||
TESTSYSCALL(close(user_dst));
|
||||
user_dst = -1;
|
||||
} else {
|
||||
DECL_FUSE_IN(open);
|
||||
DECL_FUSE_IN(copy_file_range);
|
||||
DECL_FUSE_IN(read);
|
||||
DECL_FUSE_IN(getxattr);
|
||||
DECL_FUSE_IN(write);
|
||||
DECL_FUSE_IN(flush);
|
||||
DECL_FUSE_IN(release);
|
||||
|
||||
TESTFUSELOOKUP(src, 0);
|
||||
TESTFUSEOUT1(fuse_entry_out, ((struct fuse_entry_out) {
|
||||
.nodeid = 2,
|
||||
.generation = 1,
|
||||
.attr.ino = 100,
|
||||
.attr.size = 4096,
|
||||
.attr.blksize = 512,
|
||||
.attr.mode = S_IFREG | 0777,
|
||||
}));
|
||||
|
||||
TESTFUSEIN(FUSE_OPEN, open_in);
|
||||
TESTFUSEOUT1(fuse_open_out, ((struct fuse_open_out) {
|
||||
.fh = 2,
|
||||
.open_flags = open_in->flags,
|
||||
}));
|
||||
|
||||
TESTFUSEINNULL(FUSE_CANONICAL_PATH);
|
||||
TESTFUSEOUTREAD("ignored", 7);
|
||||
|
||||
TESTFUSELOOKUP(dst, 0);
|
||||
TESTFUSEOUT1(fuse_entry_out, ((struct fuse_entry_out) {
|
||||
.nodeid = 3,
|
||||
.generation = 1,
|
||||
.attr.ino = 100,
|
||||
.attr.size = 4096,
|
||||
.attr.blksize = 512,
|
||||
.attr.mode = S_IFREG | 0777,
|
||||
}));
|
||||
|
||||
TESTFUSEIN(FUSE_OPEN, open_in);
|
||||
TESTFUSEOUT1(fuse_open_out, ((struct fuse_open_out) {
|
||||
.fh = 3,
|
||||
.open_flags = open_in->flags,
|
||||
}));
|
||||
|
||||
TESTFUSEINNULL(FUSE_CANONICAL_PATH);
|
||||
TESTFUSEOUTREAD("ignored", 7);
|
||||
|
||||
TESTFUSEIN(FUSE_COPY_FILE_RANGE, copy_file_range_in);
|
||||
TESTEQUAL(copy_file_range_in->fh_in, 2);
|
||||
TESTEQUAL(copy_file_range_in->fh_out, 0);
|
||||
TESTFUSEOUTERROR(-EXDEV);
|
||||
TESTFUSEIN(FUSE_READ, read_in);
|
||||
TESTFUSEOUTREAD(buf, sizeof(buf));
|
||||
|
||||
TESTFUSEINEXT(FUSE_GETXATTR, getxattr_in, 20);
|
||||
TESTFUSEDIROUTREAD(&((struct fuse_getxattr_out) {.size = 0}),
|
||||
"", 0);
|
||||
TESTFUSEINEXT(FUSE_WRITE, write_in, sizeof(buf));
|
||||
TESTFUSEOUT1(fuse_write_out, ((struct fuse_write_out) {
|
||||
.size = sizeof(buf)
|
||||
}));
|
||||
|
||||
TESTFUSEINEXT(FUSE_GETXATTR, getxattr_in, 20);
|
||||
TESTFUSEDIROUTREAD(&((struct fuse_getxattr_out) {.size = 0}),
|
||||
"", 0);
|
||||
TESTFUSEIN(FUSE_COPY_FILE_RANGE, copy_file_range_in);
|
||||
TESTEQUAL(copy_file_range_in->fh_in, 2);
|
||||
TESTEQUAL(copy_file_range_in->fh_out, 3);
|
||||
TESTFUSEOUT1(fuse_write_out, ((struct fuse_write_out) {
|
||||
.size = sizeof(buf)
|
||||
}));
|
||||
|
||||
TESTFUSEIN(FUSE_FLUSH, flush_in);
|
||||
TESTFUSEOUTEMPTY();
|
||||
TESTFUSEIN(FUSE_RELEASE, release_in);
|
||||
TESTFUSEOUTEMPTY();
|
||||
TESTFUSEIN(FUSE_FLUSH, flush_in);
|
||||
TESTFUSEOUTEMPTY();
|
||||
TESTFUSEIN(FUSE_RELEASE, release_in);
|
||||
TESTFUSEOUTEMPTY();
|
||||
exit(TEST_SUCCESS);
|
||||
}
|
||||
FUSE_END_DAEMON();
|
||||
umount(mount_dir);
|
||||
close(bpf_fd);
|
||||
close(src_fd);
|
||||
close(fuse_dev);
|
||||
close(fd);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void parse_range(const char *ranges, bool *run_test, size_t tests)
|
||||
{
|
||||
size_t i;
|
||||
@@ -2344,6 +2518,7 @@ int main(int argc, char *argv[])
|
||||
MAKE_TEST(bpf_test_readahead),
|
||||
MAKE_TEST(splice_test),
|
||||
MAKE_TEST(bpf_test_follow_mounts),
|
||||
MAKE_TEST(copy_file_range_test),
|
||||
};
|
||||
#undef MAKE_TEST
|
||||
|
||||
|
||||
@@ -553,3 +553,29 @@ int mkdirremovebpf_test(struct fuse_bpf_args *fa)
|
||||
return FUSE_BPF_BACKING;
|
||||
}
|
||||
}
|
||||
|
||||
SEC("copy_file_range_test")
|
||||
int copy_file_range_test_filter(struct fuse_bpf_args *fa)
|
||||
{
|
||||
switch (fa->opcode) {
|
||||
case FUSE_LOOKUP | FUSE_PREFILTER:
|
||||
return FUSE_BPF_BACKING | FUSE_BPF_POST_FILTER;
|
||||
|
||||
case FUSE_LOOKUP | FUSE_POSTFILTER: {
|
||||
struct fuse_entry_bpf_out *febo = fa->out_args[1].value;
|
||||
const char *name = fa->in_args[0].value;
|
||||
|
||||
bpf_printk("lookup postfilter %s %d", name, fa->error_in);
|
||||
if (strcmp(name, "user") == 0) {
|
||||
bpf_printk("lookup postfilter user");
|
||||
febo->bpf_action = FUSE_ACTION_REMOVE;
|
||||
febo->backing_action = FUSE_ACTION_REMOVE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
default:
|
||||
return FUSE_BPF_BACKING;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user