Signed-off-by: Daniel Rosenberg <drosen@xxxxxxxxxx> Signed-off-by: Paul Lawrence <paullawrence@xxxxxxxxxx> --- fs/fuse/backing.c | 257 ++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/fuse_i.h | 55 ++++++++++ fs/fuse/xattr.c | 36 +++++++ 3 files changed, 348 insertions(+) diff --git a/fs/fuse/backing.c b/fs/fuse/backing.c index 8fd5cbfdd4fa..d8c86234f253 100644 --- a/fs/fuse/backing.c +++ b/fs/fuse/backing.c @@ -695,6 +695,263 @@ int fuse_dir_fsync_initialize_out(struct bpf_fuse_args *fa, struct fuse_fsync_in return 0; } +int fuse_getxattr_initialize_in(struct bpf_fuse_args *fa, + struct fuse_getxattr_io *fgio, + struct dentry *dentry, const char *name, void *value, + size_t size) +{ + *fgio = (struct fuse_getxattr_io) { + .fgi.size = size, + }; + + *fa = (struct bpf_fuse_args) { + .nodeid = get_fuse_inode(dentry->d_inode)->nodeid, + .opcode = FUSE_GETXATTR, + .in_numargs = 2, + .in_args[0] = (struct bpf_fuse_arg) { + .size = sizeof(fgio->fgi), + .value = &fgio->fgi, + }, + .in_args[1] = (struct bpf_fuse_arg) { + .size = strlen(name) + 1, + .max_size = XATTR_NAME_MAX + 1, + .flags = BPF_FUSE_MUST_ALLOCATE | BPF_FUSE_VARIABLE_SIZE, + .value = (void *) name, + }, + }; + + return 0; +} + +int fuse_getxattr_initialize_out(struct bpf_fuse_args *fa, + struct fuse_getxattr_io *fgio, + struct dentry *dentry, const char *name, void *value, + size_t size) +{ + fa->flags = size ? FUSE_BPF_OUT_ARGVAR : 0; + fa->out_numargs = 1; + if (size) { + fa->out_args[0].size = size; + fa->out_args[0].max_size = size; + fa->out_args[0].flags = BPF_FUSE_VARIABLE_SIZE; + fa->out_args[0].value = value; + } else { + fa->out_args[0].size = sizeof(fgio->fgo); + fa->out_args[0].value = &fgio->fgo; + } + return 0; +} + +int fuse_getxattr_backing(struct bpf_fuse_args *fa, int *out, + struct dentry *dentry, const char *name, void *value, + size_t size) +{ + ssize_t ret = vfs_getxattr(&init_user_ns, + get_fuse_dentry(dentry)->backing_path.dentry, + fa->in_args[1].value, value, size); + + if (fa->flags & FUSE_BPF_OUT_ARGVAR) + fa->out_args[0].size = ret; + else + ((struct fuse_getxattr_out *)fa->out_args[0].value)->size = ret; + + return 0; +} + +int fuse_getxattr_finalize(struct bpf_fuse_args *fa, int *out, + struct dentry *dentry, const char *name, void *value, + size_t size) +{ + struct fuse_getxattr_out *fgo; + + if (fa->flags & FUSE_BPF_OUT_ARGVAR) { + *out = fa->out_args[0].size; + return 0; + } + + fgo = fa->out_args[0].value; + + *out = fgo->size; + return 0; +} + +int fuse_listxattr_initialize_in(struct bpf_fuse_args *fa, + struct fuse_getxattr_io *fgio, + struct dentry *dentry, char *list, size_t size) +{ + *fgio = (struct fuse_getxattr_io) { + .fgi.size = size, + }; + + *fa = (struct bpf_fuse_args) { + .nodeid = get_fuse_inode(dentry->d_inode)->nodeid, + .opcode = FUSE_LISTXATTR, + .in_numargs = 1, + .in_args[0] = + (struct bpf_fuse_arg) { + .size = sizeof(fgio->fgi), + .value = &fgio->fgi, + }, + }; + + return 0; +} + +int fuse_listxattr_initialize_out(struct bpf_fuse_args *fa, + struct fuse_getxattr_io *fgio, + struct dentry *dentry, char *list, size_t size) +{ + fa->out_numargs = 1; + + if (size) { + fa->flags = FUSE_BPF_OUT_ARGVAR; + fa->out_args[0].size = size; + fa->out_args[0].max_size = size; + fa->out_args[0].flags = BPF_FUSE_VARIABLE_SIZE; + fa->out_args[0].value = (void *)list; + } else { + fa->out_args[0].size = sizeof(fgio->fgo); + fa->out_args[0].value = &fgio->fgo; + } + return 0; +} + +int fuse_listxattr_backing(struct bpf_fuse_args *fa, ssize_t *out, struct dentry *dentry, + char *list, size_t size) +{ + *out = vfs_listxattr(get_fuse_dentry(dentry)->backing_path.dentry, list, size); + + if (*out < 0) + return *out; + + if (fa->flags & FUSE_BPF_OUT_ARGVAR) + fa->out_args[0].size = *out; + else + ((struct fuse_getxattr_out *)fa->out_args[0].value)->size = *out; + + return 0; +} + +int fuse_listxattr_finalize(struct bpf_fuse_args *fa, ssize_t *out, struct dentry *dentry, + char *list, size_t size) +{ + struct fuse_getxattr_out *fgo; + + if (fa->error_in) + return 0; + + if (fa->flags & FUSE_BPF_OUT_ARGVAR) { + *out = fa->out_args[0].size; + return 0; + } + + fgo = fa->out_args[0].value; + *out = fgo->size; + return 0; +} + +int fuse_setxattr_initialize_in(struct bpf_fuse_args *fa, + struct fuse_setxattr_in *fsxi, + struct dentry *dentry, const char *name, + const void *value, size_t size, int flags) +{ + *fsxi = (struct fuse_setxattr_in) { + .size = size, + .flags = flags, + }; + + *fa = (struct bpf_fuse_args) { + .nodeid = get_fuse_inode(dentry->d_inode)->nodeid, + .opcode = FUSE_SETXATTR, + .in_numargs = 3, + .in_args[0] = (struct bpf_fuse_arg) { + .size = sizeof(*fsxi), + .value = fsxi, + }, + .in_args[1] = (struct bpf_fuse_arg) { + .size = strlen(name) + 1, + .max_size = XATTR_NAME_MAX + 1, + .flags = BPF_FUSE_VARIABLE_SIZE | BPF_FUSE_MUST_ALLOCATE, + .value = (void *) name, + }, + .in_args[2] = (struct bpf_fuse_arg) { + .size = size, + .max_size = XATTR_SIZE_MAX, + .flags = BPF_FUSE_VARIABLE_SIZE | BPF_FUSE_MUST_ALLOCATE, + .value = (void *) value, + }, + }; + + return 0; +} + +int fuse_setxattr_initialize_out(struct bpf_fuse_args *fa, + struct fuse_setxattr_in *fsxi, + struct dentry *dentry, const char *name, + const void *value, size_t size, int flags) +{ + return 0; +} + +int fuse_setxattr_backing(struct bpf_fuse_args *fa, int *out, struct dentry *dentry, + const char *name, const void *value, size_t size, + int flags) +{ + *out = vfs_setxattr(&init_user_ns, + get_fuse_dentry(dentry)->backing_path.dentry, name, + (void *) value, size, flags); + return 0; +} + +int fuse_setxattr_finalize(struct bpf_fuse_args *fa, int *out, struct dentry *dentry, + const char *name, const void *value, size_t size, + int flags) +{ + return 0; +} + +int fuse_removexattr_initialize_in(struct bpf_fuse_args *fa, + struct fuse_dummy_io *unused, + struct dentry *dentry, const char *name) +{ + *fa = (struct bpf_fuse_args) { + .nodeid = get_fuse_inode(dentry->d_inode)->nodeid, + .opcode = FUSE_REMOVEXATTR, + .in_numargs = 1, + .in_args[0] = (struct bpf_fuse_arg) { + .size = strlen(name) + 1, + .max_size = XATTR_NAME_MAX + 1, + .flags = BPF_FUSE_VARIABLE_SIZE | BPF_FUSE_MUST_ALLOCATE, + .value = (void *) name, + }, + }; + + return 0; +} + +int fuse_removexattr_initialize_out(struct bpf_fuse_args *fa, + struct fuse_dummy_io *unused, + struct dentry *dentry, const char *name) +{ + return 0; +} + +int fuse_removexattr_backing(struct bpf_fuse_args *fa, int *out, + struct dentry *dentry, const char *name) +{ + struct path *backing_path = &get_fuse_dentry(dentry)->backing_path; + + /* TODO account for changes of the name by prefilter */ + *out = vfs_removexattr(&init_user_ns, backing_path->dentry, name); + return 0; +} + +int fuse_removexattr_finalize(struct bpf_fuse_args *fa, int *out, + struct dentry *dentry, const char *name) +{ + return 0; +} + static inline void fuse_bpf_aio_put(struct fuse_bpf_aio_req *aio_req) { if (refcount_dec_and_test(&aio_req->ref)) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 370fe944387e..b313a45c7774 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1578,6 +1578,61 @@ int fuse_dir_fsync_initialize_in(struct bpf_fuse_args *fa, struct fuse_fsync_in int fuse_dir_fsync_initialize_out(struct bpf_fuse_args *fa, struct fuse_fsync_in *ffi, struct file *file, loff_t start, loff_t end, int datasync); +struct fuse_getxattr_io { + struct fuse_getxattr_in fgi; + struct fuse_getxattr_out fgo; +}; + +int fuse_getxattr_initialize_in(struct bpf_fuse_args *fa, struct fuse_getxattr_io *fgio, + struct dentry *dentry, const char *name, void *value, + size_t size); +int fuse_getxattr_initialize_out(struct bpf_fuse_args *fa, struct fuse_getxattr_io *fgio, + struct dentry *dentry, const char *name, void *value, + size_t size); +int fuse_getxattr_backing(struct bpf_fuse_args *fa, int *out, + struct dentry *dentry, const char *name, void *value, + size_t size); +int fuse_getxattr_finalize(struct bpf_fuse_args *fa, int *out, + struct dentry *dentry, const char *name, void *value, + size_t size); + +int fuse_listxattr_initialize_in(struct bpf_fuse_args *fa, + struct fuse_getxattr_io *fgio, + struct dentry *dentry, char *list, size_t size); +int fuse_listxattr_initialize_out(struct bpf_fuse_args *fa, + struct fuse_getxattr_io *fgio, + struct dentry *dentry, char *list, size_t size); +int fuse_listxattr_backing(struct bpf_fuse_args *fa, ssize_t *out, struct dentry *dentry, + char *list, size_t size); +int fuse_listxattr_finalize(struct bpf_fuse_args *fa, ssize_t *out, struct dentry *dentry, + char *list, size_t size); + +int fuse_setxattr_initialize_in(struct bpf_fuse_args *fa, + struct fuse_setxattr_in *fsxi, + struct dentry *dentry, const char *name, + const void *value, size_t size, int flags); +int fuse_setxattr_initialize_out(struct bpf_fuse_args *fa, + struct fuse_setxattr_in *fsxi, + struct dentry *dentry, const char *name, + const void *value, size_t size, int flags); +int fuse_setxattr_backing(struct bpf_fuse_args *fa, int *out, struct dentry *dentry, + const char *name, const void *value, size_t size, + int flags); +int fuse_setxattr_finalize(struct bpf_fuse_args *fa, int *out, struct dentry *dentry, + const char *name, const void *value, size_t size, + int flags); + +int fuse_removexattr_initialize_in(struct bpf_fuse_args *fa, + struct fuse_dummy_io *unused, + struct dentry *dentry, const char *name); +int fuse_removexattr_initialize_out(struct bpf_fuse_args *fa, + struct fuse_dummy_io *unused, + struct dentry *dentry, const char *name); +int fuse_removexattr_backing(struct bpf_fuse_args *fa, int *out, + struct dentry *dentry, const char *name); +int fuse_removexattr_finalize(struct bpf_fuse_args *fa, int *out, + struct dentry *dentry, const char *name); + struct fuse_read_iter_out { uint64_t ret; }; diff --git a/fs/fuse/xattr.c b/fs/fuse/xattr.c index 0d3e7177fce0..96728bd907ce 100644 --- a/fs/fuse/xattr.c +++ b/fs/fuse/xattr.c @@ -115,6 +115,14 @@ ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size) struct fuse_getxattr_out outarg; ssize_t ret; +#ifdef CONFIG_FUSE_BPF + if (fuse_bpf_backing(inode, struct fuse_getxattr_io, ret, + fuse_listxattr_initialize_in, fuse_listxattr_initialize_out, + fuse_listxattr_backing, fuse_listxattr_finalize, + entry, list, size)) + return ret; +#endif + if (fuse_is_bad(inode)) return -EIO; @@ -182,6 +190,16 @@ static int fuse_xattr_get(const struct xattr_handler *handler, struct dentry *dentry, struct inode *inode, const char *name, void *value, size_t size) { +#ifdef CONFIG_FUSE_BPF + int err; + + if (fuse_bpf_backing(inode, struct fuse_getxattr_io, err, + fuse_getxattr_initialize_in, fuse_getxattr_initialize_out, + fuse_getxattr_backing, fuse_getxattr_finalize, + dentry, name, value, size)) + return err; +#endif + if (fuse_is_bad(inode)) return -EIO; @@ -194,6 +212,24 @@ static int fuse_xattr_set(const struct xattr_handler *handler, const char *name, const void *value, size_t size, int flags) { +#ifdef CONFIG_FUSE_BPF + int err; + bool handled; + + if (value) + handled = fuse_bpf_backing(inode, struct fuse_setxattr_in, err, + fuse_setxattr_initialize_in, fuse_setxattr_initialize_out, + fuse_setxattr_backing, fuse_setxattr_finalize, + dentry, name, value, size, flags); + else + handled = fuse_bpf_backing(inode, struct fuse_dummy_io, err, + fuse_removexattr_initialize_in, fuse_removexattr_initialize_out, + fuse_removexattr_backing, fuse_removexattr_finalize, + dentry, name); + if (handled) + return err; +#endif + if (fuse_is_bad(inode)) return -EIO; -- 2.37.3.998.g577e59143f-goog