diff mbox series

[21/26] fuse-bpf: Add xattr support

Message ID 20220926231822.994383-22-drosen@google.com (mailing list archive)
State New, archived
Headers show
Series FUSE BPF: A Stacked Filesystem Extension for FUSE | expand

Commit Message

Daniel Rosenberg Sept. 26, 2022, 11:18 p.m. UTC
Signed-off-by: Daniel Rosenberg <drosen@google.com>
Signed-off-by: Paul Lawrence <paullawrence@google.com>
---
 fs/fuse/backing.c | 257 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/fuse/fuse_i.h  |  55 ++++++++++
 fs/fuse/xattr.c   |  36 +++++++
 3 files changed, 348 insertions(+)
diff mbox series

Patch

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;