From 41d9adb6ecf068b372f37ae95e012ebbccf243b5 Mon Sep 17 00:00:00 2001 From: Paul Lawrence Date: Tue, 2 Jul 2024 12:11:06 -0700 Subject: [PATCH] ANDROID: fuse-bpf: Support fuse_splice_read/write fully Need to support fuse_splice_read and fuse_splice_write in both fuse bpf and fuse passthrough modes. Bug: 349258571 Test: fuse_test Change-Id: I641b917d3decc4be1eea7ea39fac2b1f50c7e41b Signed-off-by: Paul Lawrence --- fs/fuse/backing.c | 16 ++++++ fs/fuse/file.c | 9 ++- fs/fuse/fuse_i.h | 2 + .../selftests/filesystems/fuse/fuse_test.c | 55 +++++++++++++++++++ 4 files changed, 81 insertions(+), 1 deletion(-) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index 1d7dfd19dfc0..6078a66cbf9f 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -1001,6 +1001,22 @@ ssize_t fuse_splice_read_backing(struct file *in, loff_t *ppos, return ret; } +ssize_t fuse_splice_write_backing(struct pipe_inode_info *pipe, + struct file *out, loff_t *ppos, size_t len, unsigned long flags) +{ + ssize_t ret; + struct fuse_file *ff = out->private_data; + + inode_lock(file_inode(out)); + file_start_write(ff->backing_file); + ret = iter_file_splice_write(pipe, ff->backing_file, ppos, len, flags); + file_end_write(ff->backing_file); + if (ret > 0) + fuse_copyattr(out, ff->backing_file); + inode_unlock(file_inode(out)); + return ret; +} + long fuse_backing_ioctl(struct file *file, unsigned int command, unsigned long arg, int flags) { struct fuse_file *ff = file->private_data; diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 76fe4846a62b..614b3707c063 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -1843,9 +1844,9 @@ static ssize_t fuse_splice_read(struct file *in, loff_t *ppos, struct pipe_inode_info *pipe, size_t len, unsigned int flags) { -#ifdef CONFIG_FUSE_BPF struct fuse_file *ff = in->private_data; +#ifdef CONFIG_FUSE_BPF /* TODO - this is simply passthrough, not a proper BPF filter */ if (ff->backing_file) return fuse_splice_read_backing(in, ppos, pipe, len, flags); @@ -1863,6 +1864,12 @@ static ssize_t fuse_splice_write(struct pipe_inode_info *pipe, struct file *out, { struct fuse_file *ff = out->private_data; +#ifdef CONFIG_FUSE_BPF + /* TODO - this is simply passthrough, not a proper BPF filter */ + if (ff->backing_file) + return fuse_splice_write_backing(pipe, out, ppos, len, flags); +#endif + /* FOPEN_DIRECT_IO overrides FOPEN_PASSTHROUGH */ if (fuse_file_passthrough(ff) && !(ff->open_flags & FOPEN_DIRECT_IO)) return fuse_passthrough_splice_write(pipe, out, ppos, len, flags); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 21a120a6742e..621e0cde1067 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1815,6 +1815,8 @@ void *fuse_file_write_iter_finalize(struct fuse_bpf_args *fa, ssize_t fuse_splice_read_backing(struct file *in, loff_t *ppos, struct pipe_inode_info *pipe, size_t len, unsigned long flags); +ssize_t fuse_splice_write_backing(struct pipe_inode_info *pipe, + struct file *out, loff_t *ppos, size_t len, unsigned long flags); long fuse_backing_ioctl(struct file *file, unsigned int command, unsigned long arg, int flags); diff --git a/tools/testing/selftests/filesystems/fuse/fuse_test.c b/tools/testing/selftests/filesystems/fuse/fuse_test.c index 666763792b67..126ba3617828 100644 --- a/tools/testing/selftests/filesystems/fuse/fuse_test.c +++ b/tools/testing/selftests/filesystems/fuse/fuse_test.c @@ -2114,6 +2114,60 @@ out: return result; } +static int splice_test(const char *mount_dir) +{ + const char *in_name = "in"; + const char *out_name = "out"; + const int splice_size = 4096; + + int result = TEST_FAILURE; + int file_fd = -1; + int src_fd = -1; + int fuse_dev = -1; + int in_fd = -1; + int out_fd = -1; + int pipefd[2] = {-1, -1}; + + TEST(file_fd = s_creat(s_path(s(ft_src), s(in_name)), 0777), + file_fd != -1); + TESTSYSCALL(fallocate(file_fd, 0, 0, splice_size)); + TESTSYSCALL(close(file_fd)); + file_fd = -1; + + TEST(src_fd = open(ft_src, O_DIRECTORY | O_RDONLY | O_CLOEXEC), + src_fd != -1); + TEST(fuse_dev = open("/dev/fuse", O_RDWR | O_CLOEXEC), fuse_dev != -1); + TESTEQUAL(mount_fuse(mount_dir, -1, src_fd, &fuse_dev), 0); + + TESTSYSCALL(pipe(pipefd)); + TEST(in_fd = s_open(s_path(s(mount_dir), s(in_name)), O_RDONLY), + in_fd != -1); + TEST(out_fd = s_creat(s_path(s(mount_dir), s(out_name)), 0777), + out_fd != -1); + TESTEQUAL(splice(in_fd, NULL, pipefd[1], NULL, splice_size, 0), + splice_size); + TESTEQUAL(splice(pipefd[0], NULL, out_fd, NULL, splice_size, 0), + splice_size); + TESTSYSCALL(close(in_fd)); + in_fd = -1; + TESTSYSCALL(close(out_fd)); + out_fd = -1; + TESTSYSCALL(close(pipefd[0])); + pipefd[0] = -1; + TESTSYSCALL(close(pipefd[1])); + pipefd[1] = -1; + result = TEST_SUCCESS; +out: + umount(mount_dir); + close(fuse_dev); + close(src_fd); + close(in_fd); + close(out_fd); + close(pipefd[0]); + close(pipefd[1]); + return result; +} + /** * Test that fuse passthrough correctly traverses a mount point on the lower fs */ @@ -2288,6 +2342,7 @@ int main(int argc, char *argv[]) MAKE_TEST(bpf_test_create_and_remove_bpf), MAKE_TEST(bpf_test_mkdir_and_remove_bpf), MAKE_TEST(bpf_test_readahead), + MAKE_TEST(splice_test), MAKE_TEST(bpf_test_follow_mounts), }; #undef MAKE_TEST