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
+1
-3
@@ -496,9 +496,7 @@ int fuse_copy_file_range_backing(struct fuse_bpf_args *fa, struct file *file_in,
|
|||||||
if (backing_file_out)
|
if (backing_file_out)
|
||||||
return vfs_copy_file_range(backing_file_in, fci->off_in, backing_file_out,
|
return vfs_copy_file_range(backing_file_in, fci->off_in, backing_file_out,
|
||||||
fci->off_out, fci->len, fci->flags);
|
fci->off_out, fci->len, fci->flags);
|
||||||
else
|
return -EXDEV;
|
||||||
return vfs_copy_file_range(file_in, pos_in, file_out, pos_out, len,
|
|
||||||
flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void *fuse_copy_file_range_finalize(struct fuse_bpf_args *fa, struct file *file_in, loff_t pos_in,
|
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;
|
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)
|
static void parse_range(const char *ranges, bool *run_test, size_t tests)
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
@@ -2344,6 +2518,7 @@ int main(int argc, char *argv[])
|
|||||||
MAKE_TEST(bpf_test_readahead),
|
MAKE_TEST(bpf_test_readahead),
|
||||||
MAKE_TEST(splice_test),
|
MAKE_TEST(splice_test),
|
||||||
MAKE_TEST(bpf_test_follow_mounts),
|
MAKE_TEST(bpf_test_follow_mounts),
|
||||||
|
MAKE_TEST(copy_file_range_test),
|
||||||
};
|
};
|
||||||
#undef MAKE_TEST
|
#undef MAKE_TEST
|
||||||
|
|
||||||
|
|||||||
@@ -553,3 +553,29 @@ int mkdirremovebpf_test(struct fuse_bpf_args *fa)
|
|||||||
return FUSE_BPF_BACKING;
|
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