All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v9 0/5] io_uring: add xattr support
@ 2021-12-28 18:41 Stefan Roesch
  2021-12-28 18:41 ` [PATCH v9 1/5] fs: split off do_user_path_at_empty from user_path_at_empty() Stefan Roesch
                   ` (4 more replies)
  0 siblings, 5 replies; 11+ messages in thread
From: Stefan Roesch @ 2021-12-28 18:41 UTC (permalink / raw)
  To: io-uring, linux-fsdevel, kernel-team; +Cc: torvalds, christian.brauner, shr

This adds the xattr support to io_uring. The intent is to have a more
complete support for file operations in io_uring.

This change adds support for the following functions to io_uring:
- fgetxattr
- fsetxattr
- getxattr
- setxattr

Patch 1: fs: split off do_user_path_at_empty from user_path_at_empty()
  This splits off a new function do_user_path_at_empty from
  user_path_at_empty that is based on filename and not on a
  user-specified string.

Patch 2: fs: split off setxattr_copy and do_setxattr function from setxattr
  Split off the setup part of the setxattr function in the setxattr_copy
  function. Split off the processing part in do_setxattr.

Patch 3: fs: split off do_getxattr from getxattr
  Split of the do_getxattr part from getxattr. This will
  allow it to be invoked it from io_uring.

Patch 4: io_uring: add fsetxattr and setxattr support
  This adds new functions to support the fsetxattr and setxattr
  functions.

Patch 5: io_uring: add fgetxattr and getxattr support
  This adds new functions to support the fgetxattr and getxattr
  functions.


There are two additional patches:
  liburing: Add support for xattr api's.
            This also includes the tests for the new code.
  xfstests: Add support for io_uring xattr support.


V9: - keep kvalue in struct xattr_ctx
V8: - introduce xattr_name struct as advised by Linus
    - remove kname_sz field in xattr_ctx
V7: - split off setxattr in two functions as recommeneded by
      Christian.
V6: - reverted addition of kname array to xattr_ctx structure
      Adding the kname array increases the io_kiocb beyond 64 bytes
      (increases it to 224 bytes). We try hard to limit it to 64 bytes.
      Keeping the original interface also is a bit more efficient.
    - addressed Pavel's reordering comment
    - addressed Pavel's putname comment
    - addressed Pavel's kvfree comment
    - rebased on for-5.17/io_uring-getdents64
V5: - add kname array to xattr_ctx structure
V4: - rebased patch series
V3: - remove req->file checks in prep functions
    - change size parameter in do_xattr
V2: - split off function do_user_path_empty instead of changing
      the function signature of user_path_at
    - Fix datatype size problem in do_getxattr


Stefan Roesch (5):
  fs: split off do_user_path_at_empty from user_path_at_empty()
  fs: split off setxattr_copy and do_setxattr function from setxattr
  fs: split off do_getxattr from getxattr
  io_uring: add fsetxattr and setxattr support
  io_uring: add fgetxattr and getxattr support

 fs/internal.h                 |  28 +++
 fs/io_uring.c                 | 314 ++++++++++++++++++++++++++++++++++
 fs/namei.c                    |  10 +-
 fs/xattr.c                    | 114 ++++++++----
 include/linux/namei.h         |   2 +
 include/uapi/linux/io_uring.h |   8 +-
 6 files changed, 438 insertions(+), 38 deletions(-)


base-commit: b4518682080d3a1cdd6ea45a54ff6772b8b2797a
-- 
2.30.2


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH v9 1/5] fs: split off do_user_path_at_empty from user_path_at_empty()
  2021-12-28 18:41 [PATCH v9 0/5] io_uring: add xattr support Stefan Roesch
@ 2021-12-28 18:41 ` Stefan Roesch
  2021-12-29 14:31   ` Christian Brauner
  2021-12-28 18:41 ` [PATCH v9 2/5] fs: split off setxattr_copy and do_setxattr function from setxattr Stefan Roesch
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 11+ messages in thread
From: Stefan Roesch @ 2021-12-28 18:41 UTC (permalink / raw)
  To: io-uring, linux-fsdevel, kernel-team; +Cc: torvalds, christian.brauner, shr

This splits off a do_user_path_at_empty function from the
user_path_at_empty_function. This is required so it can be
called from io_uring.

Signed-off-by: Stefan Roesch <shr@fb.com>
Acked-by: Christian Brauner <christian.brauner@ubuntu.com>
---
 fs/namei.c            | 10 ++++++++--
 include/linux/namei.h |  2 ++
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 1f9d2187c765..d988e241b32c 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2794,12 +2794,18 @@ int path_pts(struct path *path)
 }
 #endif
 
+int do_user_path_at_empty(int dfd, struct filename *filename, unsigned int flags,
+		       struct path *path)
+{
+	return filename_lookup(dfd, filename, flags, path, NULL);
+}
+
 int user_path_at_empty(int dfd, const char __user *name, unsigned flags,
-		 struct path *path, int *empty)
+		struct path *path, int *empty)
 {
 	struct filename *filename = getname_flags(name, flags, empty);
-	int ret = filename_lookup(dfd, filename, flags, path, NULL);
 
+	int ret = do_user_path_at_empty(dfd, filename, flags, path);
 	putname(filename);
 	return ret;
 }
diff --git a/include/linux/namei.h b/include/linux/namei.h
index e89329bb3134..8f3ef38c057b 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -49,6 +49,8 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT};
 
 extern int path_pts(struct path *path);
 
+extern int do_user_path_at_empty(int dfd, struct filename *filename,
+				unsigned int flags, struct path *path);
 extern int user_path_at_empty(int, const char __user *, unsigned, struct path *, int *empty);
 
 static inline int user_path_at(int dfd, const char __user *name, unsigned flags,
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v9 2/5] fs: split off setxattr_copy and do_setxattr function from setxattr
  2021-12-28 18:41 [PATCH v9 0/5] io_uring: add xattr support Stefan Roesch
  2021-12-28 18:41 ` [PATCH v9 1/5] fs: split off do_user_path_at_empty from user_path_at_empty() Stefan Roesch
@ 2021-12-28 18:41 ` Stefan Roesch
  2021-12-29 14:48   ` Christian Brauner
  2021-12-28 18:41 ` [PATCH v9 3/5] fs: split off do_getxattr from getxattr Stefan Roesch
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 11+ messages in thread
From: Stefan Roesch @ 2021-12-28 18:41 UTC (permalink / raw)
  To: io-uring, linux-fsdevel, kernel-team; +Cc: torvalds, christian.brauner, shr

This splits of the setup part of the function
setxattr in its own dedicated function called
setxattr_copy. In addition it also exposes a
new function called do_setxattr for making the
setxattr call.

This makes it possible to call these two functions
from io_uring in the processing of an xattr request.

Signed-off-by: Stefan Roesch <shr@fb.com>
---
 fs/internal.h | 21 +++++++++++++
 fs/xattr.c    | 82 ++++++++++++++++++++++++++++++++++++---------------
 2 files changed, 80 insertions(+), 23 deletions(-)

diff --git a/fs/internal.h b/fs/internal.h
index 432ea3ce76ec..00c98b0cd634 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -202,3 +202,24 @@ struct linux_dirent64;
 
 int vfs_getdents(struct file *file, struct linux_dirent64 __user *dirent,
 		 unsigned int count, loff_t *pos);
+
+ /*
+  * fs/xattr.c:
+  */
+struct xattr_name {
+	char name[XATTR_NAME_MAX + 1];
+};
+
+struct xattr_ctx {
+	/* Value of attribute */
+	const void __user *value;
+	void *kvalue;
+	size_t size;
+	/* Attribute name */
+	struct xattr_name *kname;
+	unsigned int flags;
+};
+
+int setxattr_copy(const char __user *name, struct xattr_ctx *ctx);
+int do_setxattr(struct user_namespace *mnt_userns, struct dentry *dentry,
+		struct xattr_ctx *ctx);
diff --git a/fs/xattr.c b/fs/xattr.c
index 5c8c5175b385..923ba944d20e 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -25,6 +25,8 @@
 
 #include <linux/uaccess.h>
 
+#include "internal.h"
+
 static const char *
 strcmp_prefix(const char *a, const char *a_prefix)
 {
@@ -539,43 +541,77 @@ EXPORT_SYMBOL_GPL(vfs_removexattr);
 /*
  * Extended attribute SET operations
  */
-static long
-setxattr(struct user_namespace *mnt_userns, struct dentry *d,
-	 const char __user *name, const void __user *value, size_t size,
-	 int flags)
+
+int setxattr_copy(const char __user *name, struct xattr_ctx *ctx)
 {
 	int error;
-	void *kvalue = NULL;
-	char kname[XATTR_NAME_MAX + 1];
 
-	if (flags & ~(XATTR_CREATE|XATTR_REPLACE))
+	if (ctx->flags & ~(XATTR_CREATE|XATTR_REPLACE))
 		return -EINVAL;
 
-	error = strncpy_from_user(kname, name, sizeof(kname));
-	if (error == 0 || error == sizeof(kname))
-		error = -ERANGE;
+	error = strncpy_from_user(ctx->kname->name, name,
+				sizeof(ctx->kname->name));
+	if (error == 0 || error == sizeof(ctx->kname->name))
+		return  -ERANGE;
 	if (error < 0)
 		return error;
 
-	if (size) {
-		if (size > XATTR_SIZE_MAX)
+	if (ctx->size) {
+		if (ctx->size > XATTR_SIZE_MAX)
 			return -E2BIG;
-		kvalue = kvmalloc(size, GFP_KERNEL);
-		if (!kvalue)
+
+		ctx->kvalue = kvmalloc(ctx->size, GFP_KERNEL);
+		if (!ctx->kvalue)
 			return -ENOMEM;
-		if (copy_from_user(kvalue, value, size)) {
-			error = -EFAULT;
-			goto out;
+
+		if (copy_from_user(ctx->kvalue, ctx->value, ctx->size)) {
+			kvfree(ctx->kvalue);
+			return -EFAULT;
 		}
-		if ((strcmp(kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) ||
-		    (strcmp(kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0))
-			posix_acl_fix_xattr_from_user(mnt_userns, kvalue, size);
 	}
 
-	error = vfs_setxattr(mnt_userns, d, kname, kvalue, size, flags);
-out:
-	kvfree(kvalue);
+	return 0;
+}
+
+static void setxattr_convert(struct user_namespace *mnt_userns,
+			struct xattr_ctx *ctx)
+{
+	if (ctx->size &&
+		((strcmp(ctx->kname->name, XATTR_NAME_POSIX_ACL_ACCESS) == 0) ||
+		(strcmp(ctx->kname->name, XATTR_NAME_POSIX_ACL_DEFAULT) == 0)))
+		posix_acl_fix_xattr_from_user(mnt_userns, ctx->kvalue, ctx->size);
+}
+
+int do_setxattr(struct user_namespace *mnt_userns, struct dentry *dentry,
+		struct xattr_ctx *ctx)
+{
+	setxattr_convert(mnt_userns, ctx);
+	return vfs_setxattr(mnt_userns, dentry, ctx->kname->name,
+			ctx->kvalue, ctx->size, ctx->flags);
+}
+
+static long
+setxattr(struct user_namespace *mnt_userns, struct dentry *d,
+	const char __user *name, const void __user *value, size_t size,
+	int flags)
+{
+	struct xattr_name kname;
+	struct xattr_ctx ctx = {
+		.value    = value,
+		.kvalue   = NULL,
+		.size     = size,
+		.kname    = &kname,
+		.flags    = flags,
+	};
+	int error;
+
+	error = setxattr_copy(name, &ctx);
+	if (error)
+		return error;
+
+	error = do_setxattr(mnt_userns, d, &ctx);
 
+	kvfree(ctx.kvalue);
 	return error;
 }
 
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v9 3/5] fs: split off do_getxattr from getxattr
  2021-12-28 18:41 [PATCH v9 0/5] io_uring: add xattr support Stefan Roesch
  2021-12-28 18:41 ` [PATCH v9 1/5] fs: split off do_user_path_at_empty from user_path_at_empty() Stefan Roesch
  2021-12-28 18:41 ` [PATCH v9 2/5] fs: split off setxattr_copy and do_setxattr function from setxattr Stefan Roesch
@ 2021-12-28 18:41 ` Stefan Roesch
  2021-12-28 18:41 ` [PATCH v9 4/5] io_uring: add fsetxattr and setxattr support Stefan Roesch
  2021-12-28 18:41 ` [PATCH v9 5/5] io_uring: add fgetxattr and getxattr support Stefan Roesch
  4 siblings, 0 replies; 11+ messages in thread
From: Stefan Roesch @ 2021-12-28 18:41 UTC (permalink / raw)
  To: io-uring, linux-fsdevel, kernel-team; +Cc: torvalds, christian.brauner, shr

This splits off do_getxattr function from the getxattr
function. This will allow io_uring to call it from its
io worker.

Signed-off-by: Stefan Roesch <shr@fb.com>
Acked-by: Christian Brauner <christian.brauner@ubuntu.com>
---
 fs/internal.h |  7 +++++++
 fs/xattr.c    | 32 ++++++++++++++++++++------------
 2 files changed, 27 insertions(+), 12 deletions(-)

diff --git a/fs/internal.h b/fs/internal.h
index 00c98b0cd634..942b2005a2be 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -220,6 +220,13 @@ struct xattr_ctx {
 	unsigned int flags;
 };
 
+
+ssize_t do_getxattr(struct user_namespace *mnt_userns,
+		    struct dentry *d,
+		    const char *kname,
+		    void __user *value,
+		    size_t size);
+
 int setxattr_copy(const char __user *name, struct xattr_ctx *ctx);
 int do_setxattr(struct user_namespace *mnt_userns, struct dentry *dentry,
 		struct xattr_ctx *ctx);
diff --git a/fs/xattr.c b/fs/xattr.c
index 923ba944d20e..3b6d683d07b9 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -677,19 +677,12 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
 /*
  * Extended attribute GET operations
  */
-static ssize_t
-getxattr(struct user_namespace *mnt_userns, struct dentry *d,
-	 const char __user *name, void __user *value, size_t size)
+ssize_t
+do_getxattr(struct user_namespace *mnt_userns, struct dentry *d,
+	const char *kname, void __user *value, size_t size)
 {
-	ssize_t error;
 	void *kvalue = NULL;
-	char kname[XATTR_NAME_MAX + 1];
-
-	error = strncpy_from_user(kname, name, sizeof(kname));
-	if (error == 0 || error == sizeof(kname))
-		error = -ERANGE;
-	if (error < 0)
-		return error;
+	ssize_t error;
 
 	if (size) {
 		if (size > XATTR_SIZE_MAX)
@@ -713,10 +706,25 @@ getxattr(struct user_namespace *mnt_userns, struct dentry *d,
 	}
 
 	kvfree(kvalue);
-
 	return error;
 }
 
+static ssize_t
+getxattr(struct user_namespace *mnt_userns, struct dentry *d,
+	 const char __user *name, void __user *value, size_t size)
+{
+	ssize_t error;
+	struct xattr_name kname;
+
+	error = strncpy_from_user(kname.name, name, sizeof(kname.name));
+	if (error == 0 || error == sizeof(kname.name))
+		error = -ERANGE;
+	if (error < 0)
+		return error;
+
+	return do_getxattr(mnt_userns, d, kname.name, value, size);
+}
+
 static ssize_t path_getxattr(const char __user *pathname,
 			     const char __user *name, void __user *value,
 			     size_t size, unsigned int lookup_flags)
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v9 4/5] io_uring: add fsetxattr and setxattr support
  2021-12-28 18:41 [PATCH v9 0/5] io_uring: add xattr support Stefan Roesch
                   ` (2 preceding siblings ...)
  2021-12-28 18:41 ` [PATCH v9 3/5] fs: split off do_getxattr from getxattr Stefan Roesch
@ 2021-12-28 18:41 ` Stefan Roesch
  2021-12-29 14:51   ` Christian Brauner
  2021-12-28 18:41 ` [PATCH v9 5/5] io_uring: add fgetxattr and getxattr support Stefan Roesch
  4 siblings, 1 reply; 11+ messages in thread
From: Stefan Roesch @ 2021-12-28 18:41 UTC (permalink / raw)
  To: io-uring, linux-fsdevel, kernel-team; +Cc: torvalds, christian.brauner, shr

This adds support to io_uring for the fsetxattr and setxattr API.

Signed-off-by: Stefan Roesch <shr@fb.com>
---
 fs/io_uring.c                 | 165 ++++++++++++++++++++++++++++++++++
 include/uapi/linux/io_uring.h |   6 +-
 2 files changed, 170 insertions(+), 1 deletion(-)

diff --git a/fs/io_uring.c b/fs/io_uring.c
index c8258c784116..2a0138a2876a 100644
--- a/fs/io_uring.c
+++ b/fs/io_uring.c
@@ -82,6 +82,7 @@
 #include <linux/audit.h>
 #include <linux/security.h>
 #include <linux/atomic-ref.h>
+#include <linux/xattr.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/io_uring.h>
@@ -726,6 +727,12 @@ struct io_async_rw {
 	struct wait_page_queue		wpq;
 };
 
+struct io_xattr {
+	struct file			*file;
+	struct xattr_ctx		ctx;
+	struct filename			*filename;
+};
+
 enum {
 	REQ_F_FIXED_FILE_BIT	= IOSQE_FIXED_FILE_BIT,
 	REQ_F_IO_DRAIN_BIT	= IOSQE_IO_DRAIN_BIT,
@@ -866,6 +873,7 @@ struct io_kiocb {
 		struct io_symlink	symlink;
 		struct io_hardlink	hardlink;
 		struct io_getdents	getdents;
+		struct io_xattr		xattr;
 	};
 
 	u8				opcode;
@@ -1118,6 +1126,10 @@ static const struct io_op_def io_op_defs[] = {
 	[IORING_OP_GETDENTS] = {
 		.needs_file		= 1,
 	},
+	[IORING_OP_FSETXATTR] = {
+		.needs_file = 1
+	},
+	[IORING_OP_SETXATTR] = {},
 };
 
 /* requests with any of those set should undergo io_disarm_next() */
@@ -3887,6 +3899,140 @@ static int io_renameat(struct io_kiocb *req, unsigned int issue_flags)
 	return 0;
 }
 
+static int __io_setxattr_prep(struct io_kiocb *req,
+			const struct io_uring_sqe *sqe)
+{
+	struct io_xattr *ix = &req->xattr;
+	const char __user *name;
+	int ret;
+
+	if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL))
+		return -EINVAL;
+	if (unlikely(sqe->ioprio))
+		return -EINVAL;
+	if (unlikely(req->flags & REQ_F_FIXED_FILE))
+		return -EBADF;
+
+	ix->filename = NULL;
+	name = u64_to_user_ptr(READ_ONCE(sqe->addr));
+	ix->ctx.value = u64_to_user_ptr(READ_ONCE(sqe->addr2));
+	ix->ctx.kvalue = NULL;
+	ix->ctx.size = READ_ONCE(sqe->len);
+	ix->ctx.flags = READ_ONCE(sqe->xattr_flags);
+
+	ix->ctx.kname = kmalloc(sizeof(*ix->ctx.kname), GFP_KERNEL);
+	if (!ix->ctx.kname)
+		return -ENOMEM;
+
+	ret = setxattr_copy(name, &ix->ctx);
+	if (ret) {
+		kfree(ix->ctx.kname);
+		return ret;
+	}
+
+	req->flags |= REQ_F_NEED_CLEANUP;
+	return 0;
+}
+
+static int io_setxattr_prep(struct io_kiocb *req,
+			const struct io_uring_sqe *sqe)
+{
+	struct io_xattr *ix = &req->xattr;
+	const char __user *path;
+	int ret;
+
+	ret = __io_setxattr_prep(req, sqe);
+	if (ret)
+		return ret;
+
+	path = u64_to_user_ptr(READ_ONCE(sqe->addr3));
+
+	ix->filename = getname_flags(path, LOOKUP_FOLLOW, NULL);
+	if (IS_ERR(ix->filename)) {
+		ret = PTR_ERR(ix->filename);
+		ix->filename = NULL;
+	}
+
+	return ret;
+}
+
+static int io_fsetxattr_prep(struct io_kiocb *req,
+			const struct io_uring_sqe *sqe)
+{
+	return __io_setxattr_prep(req, sqe);
+}
+
+static int __io_setxattr(struct io_kiocb *req, unsigned int issue_flags,
+			struct path *path)
+{
+	struct io_xattr *ix = &req->xattr;
+	int ret;
+
+	ret = mnt_want_write(path->mnt);
+	if (!ret) {
+		ret = do_setxattr(mnt_user_ns(path->mnt), path->dentry, &ix->ctx);
+		mnt_drop_write(path->mnt);
+	}
+
+	return ret;
+}
+
+static int io_fsetxattr(struct io_kiocb *req, unsigned int issue_flags)
+{
+	struct io_xattr *ix = &req->xattr;
+	int ret;
+
+	if (issue_flags & IO_URING_F_NONBLOCK)
+		return -EAGAIN;
+
+	ret = __io_setxattr(req, issue_flags, &req->file->f_path);
+
+	req->flags &= ~REQ_F_NEED_CLEANUP;
+	kfree(ix->ctx.kname);
+
+	if (ix->ctx.kvalue)
+		kvfree(ix->ctx.kvalue);
+	if (ret < 0)
+		req_set_fail(req);
+
+	io_req_complete(req, ret);
+	return 0;
+}
+
+static int io_setxattr(struct io_kiocb *req, unsigned int issue_flags)
+{
+	struct io_xattr *ix = &req->xattr;
+	unsigned int lookup_flags = LOOKUP_FOLLOW;
+	struct path path;
+	int ret;
+
+	if (issue_flags & IO_URING_F_NONBLOCK)
+		return -EAGAIN;
+
+retry:
+	ret = do_user_path_at_empty(AT_FDCWD, ix->filename, lookup_flags, &path);
+	if (!ret) {
+		ret = __io_setxattr(req, issue_flags, &path);
+		path_put(&path);
+		if (retry_estale(ret, lookup_flags)) {
+			lookup_flags |= LOOKUP_REVAL;
+			goto retry;
+		}
+	}
+	putname(ix->filename);
+
+	req->flags &= ~REQ_F_NEED_CLEANUP;
+	kfree(ix->ctx.kname);
+
+	if (ix->ctx.kvalue)
+		kvfree(ix->ctx.kvalue);
+	if (ret < 0)
+		req_set_fail(req);
+
+	io_req_complete(req, ret);
+	return 0;
+}
+
 static int io_unlinkat_prep(struct io_kiocb *req,
 			    const struct io_uring_sqe *sqe)
 {
@@ -6623,6 +6769,10 @@ static int io_req_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
 		return io_linkat_prep(req, sqe);
 	case IORING_OP_GETDENTS:
 		return io_getdents_prep(req, sqe);
+	case IORING_OP_FSETXATTR:
+		return io_fsetxattr_prep(req, sqe);
+	case IORING_OP_SETXATTR:
+		return io_setxattr_prep(req, sqe);
 	}
 
 	printk_once(KERN_WARNING "io_uring: unhandled opcode %d\n",
@@ -6764,6 +6914,14 @@ static void io_clean_op(struct io_kiocb *req)
 			putname(req->hardlink.oldpath);
 			putname(req->hardlink.newpath);
 			break;
+		case IORING_OP_SETXATTR:
+			if (req->xattr.filename)
+				putname(req->xattr.filename);
+			fallthrough;
+		case IORING_OP_FSETXATTR:
+			kfree(req->xattr.ctx.kname);
+			kvfree(req->xattr.ctx.kvalue);
+			break;
 		}
 	}
 	if ((req->flags & REQ_F_POLLED) && req->apoll) {
@@ -6909,6 +7067,12 @@ static int io_issue_sqe(struct io_kiocb *req, unsigned int issue_flags)
 	case IORING_OP_GETDENTS:
 		ret = io_getdents(req, issue_flags);
 		break;
+	case IORING_OP_FSETXATTR:
+		ret = io_fsetxattr(req, issue_flags);
+		break;
+	case IORING_OP_SETXATTR:
+		ret = io_setxattr(req, issue_flags);
+		break;
 	default:
 		ret = -EINVAL;
 		break;
@@ -11277,6 +11441,7 @@ static int __init io_uring_init(void)
 	BUILD_BUG_SQE_ELEM(42, __u16,  personality);
 	BUILD_BUG_SQE_ELEM(44, __s32,  splice_fd_in);
 	BUILD_BUG_SQE_ELEM(44, __u32,  file_index);
+	BUILD_BUG_SQE_ELEM(48, __u64,  addr3);
 
 	BUILD_BUG_ON(sizeof(struct io_uring_files_update) !=
 		     sizeof(struct io_uring_rsrc_update));
diff --git a/include/uapi/linux/io_uring.h b/include/uapi/linux/io_uring.h
index 57dc88db5793..c62a8bec8cd4 100644
--- a/include/uapi/linux/io_uring.h
+++ b/include/uapi/linux/io_uring.h
@@ -45,6 +45,7 @@ struct io_uring_sqe {
 		__u32		rename_flags;
 		__u32		unlink_flags;
 		__u32		hardlink_flags;
+		__u32		xattr_flags;
 	};
 	__u64	user_data;	/* data to be passed back at completion time */
 	/* pack this to avoid bogus arm OABI complaints */
@@ -60,7 +61,8 @@ struct io_uring_sqe {
 		__s32	splice_fd_in;
 		__u32	file_index;
 	};
-	__u64	__pad2[2];
+	__u64	addr3;
+	__u64	__pad2[1];
 };
 
 enum {
@@ -144,6 +146,8 @@ enum {
 	IORING_OP_SYMLINKAT,
 	IORING_OP_LINKAT,
 	IORING_OP_GETDENTS,
+	IORING_OP_FSETXATTR,
+	IORING_OP_SETXATTR,
 
 	/* this goes last, obviously */
 	IORING_OP_LAST,
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v9 5/5] io_uring: add fgetxattr and getxattr support
  2021-12-28 18:41 [PATCH v9 0/5] io_uring: add xattr support Stefan Roesch
                   ` (3 preceding siblings ...)
  2021-12-28 18:41 ` [PATCH v9 4/5] io_uring: add fsetxattr and setxattr support Stefan Roesch
@ 2021-12-28 18:41 ` Stefan Roesch
  4 siblings, 0 replies; 11+ messages in thread
From: Stefan Roesch @ 2021-12-28 18:41 UTC (permalink / raw)
  To: io-uring, linux-fsdevel, kernel-team; +Cc: torvalds, christian.brauner, shr

This adds support to io_uring for the fgetxattr and getxattr API.

Signed-off-by: Stefan Roesch <shr@fb.com>
---
 fs/io_uring.c                 | 149 ++++++++++++++++++++++++++++++++++
 include/uapi/linux/io_uring.h |   2 +
 2 files changed, 151 insertions(+)

diff --git a/fs/io_uring.c b/fs/io_uring.c
index 2a0138a2876a..5b8370a62547 100644
--- a/fs/io_uring.c
+++ b/fs/io_uring.c
@@ -1130,6 +1130,10 @@ static const struct io_op_def io_op_defs[] = {
 		.needs_file = 1
 	},
 	[IORING_OP_SETXATTR] = {},
+	[IORING_OP_FGETXATTR] = {
+		.needs_file = 1
+	},
+	[IORING_OP_GETXATTR] = {},
 };
 
 /* requests with any of those set should undergo io_disarm_next() */
@@ -3899,6 +3903,134 @@ static int io_renameat(struct io_kiocb *req, unsigned int issue_flags)
 	return 0;
 }
 
+static int __io_getxattr_prep(struct io_kiocb *req,
+			      const struct io_uring_sqe *sqe)
+{
+	struct io_xattr *ix = &req->xattr;
+	const char __user *name;
+	int ret;
+
+	if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL))
+		return -EINVAL;
+	if (unlikely(sqe->ioprio))
+		return -EINVAL;
+	if (unlikely(req->flags & REQ_F_FIXED_FILE))
+		return -EBADF;
+
+	ix->filename = NULL;
+	ix->ctx.kvalue = NULL;
+	name = u64_to_user_ptr(READ_ONCE(sqe->addr));
+	ix->ctx.value = u64_to_user_ptr(READ_ONCE(sqe->addr2));
+	ix->ctx.size = READ_ONCE(sqe->len);
+	ix->ctx.flags = READ_ONCE(sqe->xattr_flags);
+
+	if (ix->ctx.flags)
+		return -EINVAL;
+
+	ix->ctx.kname = kmalloc(sizeof(*ix->ctx.kname), GFP_KERNEL);
+	if (!ix->ctx.kname)
+		return -ENOMEM;
+
+	ret = strncpy_from_user(ix->ctx.kname->name, name,
+				sizeof(ix->ctx.kname->name));
+	if (!ret || ret == sizeof(ix->ctx.kname->name))
+		ret = -ERANGE;
+	if (ret < 0) {
+		kfree(ix->ctx.kname);
+		return ret;
+	}
+
+	req->flags |= REQ_F_NEED_CLEANUP;
+	return 0;
+}
+
+static int io_fgetxattr_prep(struct io_kiocb *req,
+			     const struct io_uring_sqe *sqe)
+{
+	return __io_getxattr_prep(req, sqe);
+}
+
+static int io_getxattr_prep(struct io_kiocb *req,
+			    const struct io_uring_sqe *sqe)
+{
+	struct io_xattr *ix = &req->xattr;
+	const char __user *path;
+	int ret;
+
+	ret = __io_getxattr_prep(req, sqe);
+	if (ret)
+		return ret;
+
+	path = u64_to_user_ptr(READ_ONCE(sqe->addr3));
+
+	ix->filename = getname_flags(path, LOOKUP_FOLLOW, NULL);
+	if (IS_ERR(ix->filename)) {
+		ret = PTR_ERR(ix->filename);
+		ix->filename = NULL;
+	}
+
+	return ret;
+}
+
+static int io_fgetxattr(struct io_kiocb *req, unsigned int issue_flags)
+{
+	struct io_xattr *ix = &req->xattr;
+	int ret;
+
+	if (issue_flags & IO_URING_F_NONBLOCK)
+		return -EAGAIN;
+
+	ret = do_getxattr(mnt_user_ns(req->file->f_path.mnt),
+			req->file->f_path.dentry,
+			ix->ctx.kname->name,
+			(void __user *)ix->ctx.value,
+			ix->ctx.size);
+
+	req->flags &= ~REQ_F_NEED_CLEANUP;
+	kfree(ix->ctx.kname);
+	if (ret < 0)
+		req_set_fail(req);
+
+	io_req_complete(req, ret);
+	return 0;
+}
+
+static int io_getxattr(struct io_kiocb *req, unsigned int issue_flags)
+{
+	struct io_xattr *ix = &req->xattr;
+	unsigned int lookup_flags = LOOKUP_FOLLOW;
+	struct path path;
+	int ret;
+
+	if (issue_flags & IO_URING_F_NONBLOCK)
+		return -EAGAIN;
+
+retry:
+	ret = do_user_path_at_empty(AT_FDCWD, ix->filename, lookup_flags, &path);
+	if (!ret) {
+		ret = do_getxattr(mnt_user_ns(path.mnt),
+				path.dentry,
+				ix->ctx.kname->name,
+				(void __user *)ix->ctx.value,
+				ix->ctx.size);
+
+		path_put(&path);
+		if (retry_estale(ret, lookup_flags)) {
+			lookup_flags |= LOOKUP_REVAL;
+			goto retry;
+		}
+	}
+	putname(ix->filename);
+
+	req->flags &= ~REQ_F_NEED_CLEANUP;
+	kfree(ix->ctx.kname);
+	if (ret < 0)
+		req_set_fail(req);
+
+	io_req_complete(req, ret);
+	return 0;
+}
+
 static int __io_setxattr_prep(struct io_kiocb *req,
 			const struct io_uring_sqe *sqe)
 {
@@ -6773,6 +6905,10 @@ static int io_req_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
 		return io_fsetxattr_prep(req, sqe);
 	case IORING_OP_SETXATTR:
 		return io_setxattr_prep(req, sqe);
+	case IORING_OP_FGETXATTR:
+		return io_fgetxattr_prep(req, sqe);
+	case IORING_OP_GETXATTR:
+		return io_getxattr_prep(req, sqe);
 	}
 
 	printk_once(KERN_WARNING "io_uring: unhandled opcode %d\n",
@@ -6922,6 +7058,13 @@ static void io_clean_op(struct io_kiocb *req)
 			kfree(req->xattr.ctx.kname);
 			kvfree(req->xattr.ctx.kvalue);
 			break;
+		case IORING_OP_GETXATTR:
+			if (req->xattr.filename)
+				putname(req->xattr.filename);
+			fallthrough;
+		case IORING_OP_FGETXATTR:
+			kfree(req->xattr.ctx.kname);
+			break;
 		}
 	}
 	if ((req->flags & REQ_F_POLLED) && req->apoll) {
@@ -7073,6 +7216,12 @@ static int io_issue_sqe(struct io_kiocb *req, unsigned int issue_flags)
 	case IORING_OP_SETXATTR:
 		ret = io_setxattr(req, issue_flags);
 		break;
+	case IORING_OP_FGETXATTR:
+		ret = io_fgetxattr(req, issue_flags);
+		break;
+	case IORING_OP_GETXATTR:
+		ret = io_getxattr(req, issue_flags);
+		break;
 	default:
 		ret = -EINVAL;
 		break;
diff --git a/include/uapi/linux/io_uring.h b/include/uapi/linux/io_uring.h
index c62a8bec8cd4..efc7ac9b3a6b 100644
--- a/include/uapi/linux/io_uring.h
+++ b/include/uapi/linux/io_uring.h
@@ -148,6 +148,8 @@ enum {
 	IORING_OP_GETDENTS,
 	IORING_OP_FSETXATTR,
 	IORING_OP_SETXATTR,
+	IORING_OP_FGETXATTR,
+	IORING_OP_GETXATTR,
 
 	/* this goes last, obviously */
 	IORING_OP_LAST,
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH v9 1/5] fs: split off do_user_path_at_empty from user_path_at_empty()
  2021-12-28 18:41 ` [PATCH v9 1/5] fs: split off do_user_path_at_empty from user_path_at_empty() Stefan Roesch
@ 2021-12-29 14:31   ` Christian Brauner
  2021-12-29 20:34     ` Stefan Roesch
  0 siblings, 1 reply; 11+ messages in thread
From: Christian Brauner @ 2021-12-29 14:31 UTC (permalink / raw)
  To: Stefan Roesch; +Cc: io-uring, linux-fsdevel, kernel-team, torvalds

On Tue, Dec 28, 2021 at 10:41:41AM -0800, Stefan Roesch wrote:
> This splits off a do_user_path_at_empty function from the
> user_path_at_empty_function. This is required so it can be
> called from io_uring.
> 
> Signed-off-by: Stefan Roesch <shr@fb.com>
> Acked-by: Christian Brauner <christian.brauner@ubuntu.com>
> ---
>  fs/namei.c            | 10 ++++++++--
>  include/linux/namei.h |  2 ++
>  2 files changed, 10 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/namei.c b/fs/namei.c
> index 1f9d2187c765..d988e241b32c 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -2794,12 +2794,18 @@ int path_pts(struct path *path)
>  }
>  #endif
>  
> +int do_user_path_at_empty(int dfd, struct filename *filename, unsigned int flags,
> +		       struct path *path)
> +{
> +	return filename_lookup(dfd, filename, flags, path, NULL);
> +}
> +
>  int user_path_at_empty(int dfd, const char __user *name, unsigned flags,
> -		 struct path *path, int *empty)
> +		struct path *path, int *empty)
>  {
>  	struct filename *filename = getname_flags(name, flags, empty);
> -	int ret = filename_lookup(dfd, filename, flags, path, NULL);
>  
> +	int ret = do_user_path_at_empty(dfd, filename, flags, path);
>  	putname(filename);
>  	return ret;
>  }
> diff --git a/include/linux/namei.h b/include/linux/namei.h
> index e89329bb3134..8f3ef38c057b 100644
> --- a/include/linux/namei.h
> +++ b/include/linux/namei.h
> @@ -49,6 +49,8 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT};
>  
>  extern int path_pts(struct path *path);
>  
> +extern int do_user_path_at_empty(int dfd, struct filename *filename,
> +				unsigned int flags, struct path *path);

Sorry, just seeing this now but this wants to live in internal.h not in
namei.h similar to all the other io_uring specific exports we added over
the last releases. There's no need to make this a kernel-wide thing if
we can avoid it, imho. With that changed:
Acked-by: Christian Brauner <christian.brauner@ubuntu.com>

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v9 2/5] fs: split off setxattr_copy and do_setxattr function from setxattr
  2021-12-28 18:41 ` [PATCH v9 2/5] fs: split off setxattr_copy and do_setxattr function from setxattr Stefan Roesch
@ 2021-12-29 14:48   ` Christian Brauner
  0 siblings, 0 replies; 11+ messages in thread
From: Christian Brauner @ 2021-12-29 14:48 UTC (permalink / raw)
  To: Stefan Roesch; +Cc: io-uring, linux-fsdevel, kernel-team, torvalds

On Tue, Dec 28, 2021 at 10:41:42AM -0800, Stefan Roesch wrote:
> This splits of the setup part of the function
> setxattr in its own dedicated function called
> setxattr_copy. In addition it also exposes a
> new function called do_setxattr for making the
> setxattr call.
> 
> This makes it possible to call these two functions
> from io_uring in the processing of an xattr request.
> 
> Signed-off-by: Stefan Roesch <shr@fb.com>
> ---

(One completely optional thing below.)

Looks good,
Acked-by: Christian Brauner <christian.brauner@ubuntu.com>

>  fs/internal.h | 21 +++++++++++++
>  fs/xattr.c    | 82 ++++++++++++++++++++++++++++++++++++---------------
>  2 files changed, 80 insertions(+), 23 deletions(-)
> 
> diff --git a/fs/internal.h b/fs/internal.h
> index 432ea3ce76ec..00c98b0cd634 100644
> --- a/fs/internal.h
> +++ b/fs/internal.h
> @@ -202,3 +202,24 @@ struct linux_dirent64;
>  
>  int vfs_getdents(struct file *file, struct linux_dirent64 __user *dirent,
>  		 unsigned int count, loff_t *pos);
> +
> + /*
> +  * fs/xattr.c:
> +  */
> +struct xattr_name {
> +	char name[XATTR_NAME_MAX + 1];
> +};

Fwiw, one additional idea is to implement this similar to struct
filename and have it keep the __user name together with the kernel name:

struct xattr_name {
	const __user char	*uname;	/* original userland pointer */
	char name[XATTR_NAME_MAX + 1];
};

and then sm like:

From 3d85d31eb65f007e48e838fce776f16811732fc0 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner@ubuntu.com>
Date: Wed, 29 Dec 2021 15:43:46 +0100
Subject: [PATCH] UNTESTED

---
 fs/internal.h |  3 ++-
 fs/io_uring.c | 18 ++++++++----------
 fs/xattr.c    | 20 +++++++++++---------
 3 files changed, 21 insertions(+), 20 deletions(-)

diff --git a/fs/internal.h b/fs/internal.h
index 942b2005a2be..bb97042ebd04 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -207,7 +207,8 @@ int vfs_getdents(struct file *file, struct linux_dirent64 __user *dirent,
   * fs/xattr.c:
   */
 struct xattr_name {
-	char name[XATTR_NAME_MAX + 1];
+	const char __user *uname;
+	char kname[XATTR_NAME_MAX + 1];
 };
 
 struct xattr_ctx {
diff --git a/fs/io_uring.c b/fs/io_uring.c
index c88916b8cccc..55ad854d3c64 100644
--- a/fs/io_uring.c
+++ b/fs/io_uring.c
@@ -3916,7 +3916,6 @@ static int __io_getxattr_prep(struct io_kiocb *req,
 			      const struct io_uring_sqe *sqe)
 {
 	struct io_xattr *ix = &req->xattr;
-	const char __user *name;
 	int ret;
 
 	if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL))
@@ -3928,7 +3927,7 @@ static int __io_getxattr_prep(struct io_kiocb *req,
 
 	ix->filename = NULL;
 	ix->ctx.kvalue = NULL;
-	name = u64_to_user_ptr(READ_ONCE(sqe->addr));
+	ix->ctx.name = u64_to_user_ptr(READ_ONCE(sqe->addr));
 	ix->ctx.value = u64_to_user_ptr(READ_ONCE(sqe->addr2));
 	ix->ctx.size = READ_ONCE(sqe->len);
 	ix->ctx.flags = READ_ONCE(sqe->xattr_flags);
@@ -3940,9 +3939,9 @@ static int __io_getxattr_prep(struct io_kiocb *req,
 	if (!ix->ctx.kname)
 		return -ENOMEM;
 
-	ret = strncpy_from_user(ix->ctx.kname->name, name,
-				sizeof(ix->ctx.kname->name));
-	if (!ret || ret == sizeof(ix->ctx.kname->name))
+	ret = strncpy_from_user(ix->ctx.kname->kname, ix->ctx.name,
+				sizeof(ix->ctx.kname->kname));
+	if (!ret || ret == sizeof(ix->ctx.kname->kname))
 		ret = -ERANGE;
 	if (ret < 0) {
 		kfree(ix->ctx.kname);
@@ -3991,7 +3990,7 @@ static int io_fgetxattr(struct io_kiocb *req, unsigned int issue_flags)
 
 	ret = do_getxattr(mnt_user_ns(req->file->f_path.mnt),
 			req->file->f_path.dentry,
-			ix->ctx.kname->name,
+			ix->ctx.kname->kname,
 			(void __user *)ix->ctx.value,
 			ix->ctx.size);
 
@@ -4019,7 +4018,7 @@ static int io_getxattr(struct io_kiocb *req, unsigned int issue_flags)
 	if (!ret) {
 		ret = do_getxattr(mnt_user_ns(path.mnt),
 				path.dentry,
-				ix->ctx.kname->name,
+				ix->ctx.kname->kname,
 				(void __user *)ix->ctx.value,
 				ix->ctx.size);
 
@@ -4044,7 +4043,6 @@ static int __io_setxattr_prep(struct io_kiocb *req,
 			const struct io_uring_sqe *sqe)
 {
 	struct io_xattr *ix = &req->xattr;
-	const char __user *name;
 	int ret;
 
 	if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL))
@@ -4055,7 +4053,7 @@ static int __io_setxattr_prep(struct io_kiocb *req,
 		return -EBADF;
 
 	ix->filename = NULL;
-	name = u64_to_user_ptr(READ_ONCE(sqe->addr));
+	ix->ctx.name = u64_to_user_ptr(READ_ONCE(sqe->addr));
 	ix->ctx.value = u64_to_user_ptr(READ_ONCE(sqe->addr2));
 	ix->ctx.kvalue = NULL;
 	ix->ctx.size = READ_ONCE(sqe->len);
@@ -4065,7 +4063,7 @@ static int __io_setxattr_prep(struct io_kiocb *req,
 	if (!ix->ctx.kname)
 		return -ENOMEM;
 
-	ret = setxattr_copy(name, &ix->ctx);
+	ret = setxattr_copy(&ix->ctx);
 	if (ret) {
 		kfree(ix->ctx.kname);
 		return ret;
diff --git a/fs/xattr.c b/fs/xattr.c
index 3b6d683d07b9..27c64bb0ca65 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -542,16 +542,16 @@ EXPORT_SYMBOL_GPL(vfs_removexattr);
  * Extended attribute SET operations
  */
 
-int setxattr_copy(const char __user *name, struct xattr_ctx *ctx)
+int setxattr_copy(struct xattr_ctx *ctx)
 {
 	int error;
 
 	if (ctx->flags & ~(XATTR_CREATE|XATTR_REPLACE))
 		return -EINVAL;
 
-	error = strncpy_from_user(ctx->kname->name, name,
-				sizeof(ctx->kname->name));
-	if (error == 0 || error == sizeof(ctx->kname->name))
+	error = strncpy_from_user(ctx->kname->kname, ctx->kname->name,
+				sizeof(ctx->kname->kname));
+	if (error == 0 || error == sizeof(ctx->kname->kname))
 		return  -ERANGE;
 	if (error < 0)
 		return error;
@@ -577,8 +577,8 @@ static void setxattr_convert(struct user_namespace *mnt_userns,
 			struct xattr_ctx *ctx)
 {
 	if (ctx->size &&
-		((strcmp(ctx->kname->name, XATTR_NAME_POSIX_ACL_ACCESS) == 0) ||
-		(strcmp(ctx->kname->name, XATTR_NAME_POSIX_ACL_DEFAULT) == 0)))
+		((strcmp(ctx->kname->kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) ||
+		(strcmp(ctx->kname->kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0)))
 		posix_acl_fix_xattr_from_user(mnt_userns, ctx->kvalue, ctx->size);
 }
 
@@ -586,7 +586,7 @@ int do_setxattr(struct user_namespace *mnt_userns, struct dentry *dentry,
 		struct xattr_ctx *ctx)
 {
 	setxattr_convert(mnt_userns, ctx);
-	return vfs_setxattr(mnt_userns, dentry, ctx->kname->name,
+	return vfs_setxattr(mnt_userns, dentry, ctx->kname->kname,
 			ctx->kvalue, ctx->size, ctx->flags);
 }
 
@@ -595,7 +595,9 @@ setxattr(struct user_namespace *mnt_userns, struct dentry *d,
 	const char __user *name, const void __user *value, size_t size,
 	int flags)
 {
-	struct xattr_name kname;
+	struct xattr_name kname = {
+		.uname = name;
+	};
 	struct xattr_ctx ctx = {
 		.value    = value,
 		.kvalue   = NULL,
@@ -605,7 +607,7 @@ setxattr(struct user_namespace *mnt_userns, struct dentry *d,
 	};
 	int error;
 
-	error = setxattr_copy(name, &ctx);
+	error = setxattr_copy(&ctx);
 	if (error)
 		return error;
 
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH v9 4/5] io_uring: add fsetxattr and setxattr support
  2021-12-28 18:41 ` [PATCH v9 4/5] io_uring: add fsetxattr and setxattr support Stefan Roesch
@ 2021-12-29 14:51   ` Christian Brauner
  2021-12-29 20:35     ` Stefan Roesch
  0 siblings, 1 reply; 11+ messages in thread
From: Christian Brauner @ 2021-12-29 14:51 UTC (permalink / raw)
  To: Stefan Roesch; +Cc: io-uring, linux-fsdevel, kernel-team, torvalds

On Tue, Dec 28, 2021 at 10:41:44AM -0800, Stefan Roesch wrote:
> This adds support to io_uring for the fsetxattr and setxattr API.
> 
> Signed-off-by: Stefan Roesch <shr@fb.com>
> ---
>  fs/io_uring.c                 | 165 ++++++++++++++++++++++++++++++++++
>  include/uapi/linux/io_uring.h |   6 +-
>  2 files changed, 170 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/io_uring.c b/fs/io_uring.c
> index c8258c784116..2a0138a2876a 100644
> --- a/fs/io_uring.c
> +++ b/fs/io_uring.c
> @@ -82,6 +82,7 @@
>  #include <linux/audit.h>
>  #include <linux/security.h>
>  #include <linux/atomic-ref.h>
> +#include <linux/xattr.h>
>  
>  #define CREATE_TRACE_POINTS
>  #include <trace/events/io_uring.h>
> @@ -726,6 +727,12 @@ struct io_async_rw {
>  	struct wait_page_queue		wpq;
>  };
>  
> +struct io_xattr {
> +	struct file			*file;
> +	struct xattr_ctx		ctx;
> +	struct filename			*filename;
> +};
> +
>  enum {
>  	REQ_F_FIXED_FILE_BIT	= IOSQE_FIXED_FILE_BIT,
>  	REQ_F_IO_DRAIN_BIT	= IOSQE_IO_DRAIN_BIT,
> @@ -866,6 +873,7 @@ struct io_kiocb {
>  		struct io_symlink	symlink;
>  		struct io_hardlink	hardlink;
>  		struct io_getdents	getdents;
> +		struct io_xattr		xattr;
>  	};
>  
>  	u8				opcode;
> @@ -1118,6 +1126,10 @@ static const struct io_op_def io_op_defs[] = {
>  	[IORING_OP_GETDENTS] = {
>  		.needs_file		= 1,
>  	},
> +	[IORING_OP_FSETXATTR] = {
> +		.needs_file = 1
> +	},
> +	[IORING_OP_SETXATTR] = {},
>  };
>  
>  /* requests with any of those set should undergo io_disarm_next() */
> @@ -3887,6 +3899,140 @@ static int io_renameat(struct io_kiocb *req, unsigned int issue_flags)
>  	return 0;
>  }
>  
> +static int __io_setxattr_prep(struct io_kiocb *req,
> +			const struct io_uring_sqe *sqe)
> +{
> +	struct io_xattr *ix = &req->xattr;
> +	const char __user *name;
> +	int ret;
> +
> +	if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL))
> +		return -EINVAL;
> +	if (unlikely(sqe->ioprio))
> +		return -EINVAL;
> +	if (unlikely(req->flags & REQ_F_FIXED_FILE))
> +		return -EBADF;
> +
> +	ix->filename = NULL;
> +	name = u64_to_user_ptr(READ_ONCE(sqe->addr));
> +	ix->ctx.value = u64_to_user_ptr(READ_ONCE(sqe->addr2));
> +	ix->ctx.kvalue = NULL;
> +	ix->ctx.size = READ_ONCE(sqe->len);
> +	ix->ctx.flags = READ_ONCE(sqe->xattr_flags);
> +
> +	ix->ctx.kname = kmalloc(sizeof(*ix->ctx.kname), GFP_KERNEL);
> +	if (!ix->ctx.kname)
> +		return -ENOMEM;
> +
> +	ret = setxattr_copy(name, &ix->ctx);
> +	if (ret) {
> +		kfree(ix->ctx.kname);
> +		return ret;
> +	}
> +
> +	req->flags |= REQ_F_NEED_CLEANUP;
> +	return 0;
> +}
> +
> +static int io_setxattr_prep(struct io_kiocb *req,
> +			const struct io_uring_sqe *sqe)
> +{
> +	struct io_xattr *ix = &req->xattr;
> +	const char __user *path;
> +	int ret;
> +
> +	ret = __io_setxattr_prep(req, sqe);
> +	if (ret)
> +		return ret;
> +
> +	path = u64_to_user_ptr(READ_ONCE(sqe->addr3));
> +
> +	ix->filename = getname_flags(path, LOOKUP_FOLLOW, NULL);
> +	if (IS_ERR(ix->filename)) {
> +		ret = PTR_ERR(ix->filename);
> +		ix->filename = NULL;
> +	}
> +
> +	return ret;
> +}
> +
> +static int io_fsetxattr_prep(struct io_kiocb *req,
> +			const struct io_uring_sqe *sqe)
> +{
> +	return __io_setxattr_prep(req, sqe);
> +}
> +
> +static int __io_setxattr(struct io_kiocb *req, unsigned int issue_flags,
> +			struct path *path)
> +{
> +	struct io_xattr *ix = &req->xattr;
> +	int ret;
> +
> +	ret = mnt_want_write(path->mnt);
> +	if (!ret) {
> +		ret = do_setxattr(mnt_user_ns(path->mnt), path->dentry, &ix->ctx);
> +		mnt_drop_write(path->mnt);
> +	}
> +
> +	return ret;
> +}
> +
> +static int io_fsetxattr(struct io_kiocb *req, unsigned int issue_flags)
> +{
> +	struct io_xattr *ix = &req->xattr;
> +	int ret;
> +
> +	if (issue_flags & IO_URING_F_NONBLOCK)
> +		return -EAGAIN;
> +
> +	ret = __io_setxattr(req, issue_flags, &req->file->f_path);
> +
> +	req->flags &= ~REQ_F_NEED_CLEANUP;
> +	kfree(ix->ctx.kname);
> +
> +	if (ix->ctx.kvalue)
> +		kvfree(ix->ctx.kvalue);
> +	if (ret < 0)
> +		req_set_fail(req);
> +
> +	io_req_complete(req, ret);
> +	return 0;
> +}
> +
> +static int io_setxattr(struct io_kiocb *req, unsigned int issue_flags)
> +{
> +	struct io_xattr *ix = &req->xattr;
> +	unsigned int lookup_flags = LOOKUP_FOLLOW;
> +	struct path path;
> +	int ret;
> +
> +	if (issue_flags & IO_URING_F_NONBLOCK)
> +		return -EAGAIN;
> +
> +retry:
> +	ret = do_user_path_at_empty(AT_FDCWD, ix->filename, lookup_flags, &path);
> +	if (!ret) {
> +		ret = __io_setxattr(req, issue_flags, &path);
> +		path_put(&path);
> +		if (retry_estale(ret, lookup_flags)) {
> +			lookup_flags |= LOOKUP_REVAL;
> +			goto retry;
> +		}
> +	}
> +	putname(ix->filename);
> +
> +	req->flags &= ~REQ_F_NEED_CLEANUP;
> +	kfree(ix->ctx.kname);
> +
> +	if (ix->ctx.kvalue)
> +		kvfree(ix->ctx.kvalue);
> +	if (ret < 0)
> +		req_set_fail(req);
> +
> +	io_req_complete(req, ret);
> +	return 0;
> +}

(One suggestin below.)

Looks good,
Acked-by: Christian Brauner <christian.brauner@ubuntu.com>

You could minimize the redudancy by implementing a simple helper
callable from both io_fsetxattr() and io_setxattr() if you think it's
worth with. So sm like:

From 2f837aa2a19b5cd8e73fffb9b87b6e6b22c5cae7 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner@ubuntu.com>
Date: Wed, 29 Dec 2021 15:22:34 +0100
Subject: [PATCH] UNTESTED

---
 fs/io_uring.c | 36 +++++++++++++++++-------------------
 1 file changed, 17 insertions(+), 19 deletions(-)

diff --git a/fs/io_uring.c b/fs/io_uring.c
index 7204b8d593e4..c88916b8cccc 100644
--- a/fs/io_uring.c
+++ b/fs/io_uring.c
@@ -4118,6 +4118,21 @@ static int __io_setxattr(struct io_kiocb *req, unsigned int issue_flags,
 	return ret;
 }
 
+static void __io_setxattr_finish(struct io_kiocb *req, int ret)
+{
+	struct xattr_ctx *ctx = &req->xattr.ctx;
+
+	req->flags &= ~REQ_F_NEED_CLEANUP;
+
+	kfree(ctx->kname);
+	if (ctx->kvalue)
+		kvfree(ctx->kvalue);
+
+	if (ret < 0)
+		req_set_fail(req);
+
+	io_req_complete(req, ret);
+}
+
 static int io_fsetxattr(struct io_kiocb *req, unsigned int issue_flags)
 {
 	struct io_xattr *ix = &req->xattr;
@@ -4127,16 +4142,7 @@ static int io_fsetxattr(struct io_kiocb *req, unsigned int issue_flags)
 		return -EAGAIN;
 
 	ret = __io_setxattr(req, issue_flags, &req->file->f_path);
-
-	req->flags &= ~REQ_F_NEED_CLEANUP;
-	kfree(ix->ctx.kname);
-
-	if (ix->ctx.kvalue)
-		kvfree(ix->ctx.kvalue);
-	if (ret < 0)
-		req_set_fail(req);
-
-	io_req_complete(req, ret);
+	__io_setxattr_finish(req, ret);
 	return 0;
 }
 
@@ -4162,15 +4168,7 @@ static int io_setxattr(struct io_kiocb *req, unsigned int issue_flags)
 	}
 	putname(ix->filename);
 
-	req->flags &= ~REQ_F_NEED_CLEANUP;
-	kfree(ix->ctx.kname);
-
-	if (ix->ctx.kvalue)
-		kvfree(ix->ctx.kvalue);
-	if (ret < 0)
-		req_set_fail(req);
-
-	io_req_complete(req, ret);
+	__io_setxattr_finish(req, ret);
 	return 0;
 }
 
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH v9 1/5] fs: split off do_user_path_at_empty from user_path_at_empty()
  2021-12-29 14:31   ` Christian Brauner
@ 2021-12-29 20:34     ` Stefan Roesch
  0 siblings, 0 replies; 11+ messages in thread
From: Stefan Roesch @ 2021-12-29 20:34 UTC (permalink / raw)
  To: Christian Brauner; +Cc: io-uring, linux-fsdevel, kernel-team, torvalds



On 12/29/21 6:31 AM, Christian Brauner wrote:
> On Tue, Dec 28, 2021 at 10:41:41AM -0800, Stefan Roesch wrote:
>> This splits off a do_user_path_at_empty function from the
>> user_path_at_empty_function. This is required so it can be
>> called from io_uring.
>>
>> Signed-off-by: Stefan Roesch <shr@fb.com>
>> Acked-by: Christian Brauner <christian.brauner@ubuntu.com>
>> ---
>>  fs/namei.c            | 10 ++++++++--
>>  include/linux/namei.h |  2 ++
>>  2 files changed, 10 insertions(+), 2 deletions(-)
>>
>> diff --git a/fs/namei.c b/fs/namei.c
>> index 1f9d2187c765..d988e241b32c 100644
>> --- a/fs/namei.c
>> +++ b/fs/namei.c
>> @@ -2794,12 +2794,18 @@ int path_pts(struct path *path)
>>  }
>>  #endif
>>  
>> +int do_user_path_at_empty(int dfd, struct filename *filename, unsigned int flags,
>> +		       struct path *path)
>> +{
>> +	return filename_lookup(dfd, filename, flags, path, NULL);
>> +}
>> +
>>  int user_path_at_empty(int dfd, const char __user *name, unsigned flags,
>> -		 struct path *path, int *empty)
>> +		struct path *path, int *empty)
>>  {
>>  	struct filename *filename = getname_flags(name, flags, empty);
>> -	int ret = filename_lookup(dfd, filename, flags, path, NULL);
>>  
>> +	int ret = do_user_path_at_empty(dfd, filename, flags, path);
>>  	putname(filename);
>>  	return ret;
>>  }
>> diff --git a/include/linux/namei.h b/include/linux/namei.h
>> index e89329bb3134..8f3ef38c057b 100644
>> --- a/include/linux/namei.h
>> +++ b/include/linux/namei.h
>> @@ -49,6 +49,8 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT};
>>  
>>  extern int path_pts(struct path *path);
>>  
>> +extern int do_user_path_at_empty(int dfd, struct filename *filename,
>> +				unsigned int flags, struct path *path);
> 
> Sorry, just seeing this now but this wants to live in internal.h not in
> namei.h similar to all the other io_uring specific exports we added over
> the last releases. There's no need to make this a kernel-wide thing if
> we can avoid it, imho. With that changed:
> Acked-by: Christian Brauner <christian.brauner@ubuntu.com>

I moved the declaration to fs/internal.h.

> 

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v9 4/5] io_uring: add fsetxattr and setxattr support
  2021-12-29 14:51   ` Christian Brauner
@ 2021-12-29 20:35     ` Stefan Roesch
  0 siblings, 0 replies; 11+ messages in thread
From: Stefan Roesch @ 2021-12-29 20:35 UTC (permalink / raw)
  To: Christian Brauner; +Cc: io-uring, linux-fsdevel, kernel-team, torvalds



On 12/29/21 6:51 AM, Christian Brauner wrote:
> On Tue, Dec 28, 2021 at 10:41:44AM -0800, Stefan Roesch wrote:
>> This adds support to io_uring for the fsetxattr and setxattr API.
>>
>> Signed-off-by: Stefan Roesch <shr@fb.com>
>> ---
>>  fs/io_uring.c                 | 165 ++++++++++++++++++++++++++++++++++
>>  include/uapi/linux/io_uring.h |   6 +-
>>  2 files changed, 170 insertions(+), 1 deletion(-)
>>
>> diff --git a/fs/io_uring.c b/fs/io_uring.c
>> index c8258c784116..2a0138a2876a 100644
>> --- a/fs/io_uring.c
>> +++ b/fs/io_uring.c
>> @@ -82,6 +82,7 @@
>>  #include <linux/audit.h>
>>  #include <linux/security.h>
>>  #include <linux/atomic-ref.h>
>> +#include <linux/xattr.h>
>>  
>>  #define CREATE_TRACE_POINTS
>>  #include <trace/events/io_uring.h>
>> @@ -726,6 +727,12 @@ struct io_async_rw {
>>  	struct wait_page_queue		wpq;
>>  };
>>  
>> +struct io_xattr {
>> +	struct file			*file;
>> +	struct xattr_ctx		ctx;
>> +	struct filename			*filename;
>> +};
>> +
>>  enum {
>>  	REQ_F_FIXED_FILE_BIT	= IOSQE_FIXED_FILE_BIT,
>>  	REQ_F_IO_DRAIN_BIT	= IOSQE_IO_DRAIN_BIT,
>> @@ -866,6 +873,7 @@ struct io_kiocb {
>>  		struct io_symlink	symlink;
>>  		struct io_hardlink	hardlink;
>>  		struct io_getdents	getdents;
>> +		struct io_xattr		xattr;
>>  	};
>>  
>>  	u8				opcode;
>> @@ -1118,6 +1126,10 @@ static const struct io_op_def io_op_defs[] = {
>>  	[IORING_OP_GETDENTS] = {
>>  		.needs_file		= 1,
>>  	},
>> +	[IORING_OP_FSETXATTR] = {
>> +		.needs_file = 1
>> +	},
>> +	[IORING_OP_SETXATTR] = {},
>>  };
>>  
>>  /* requests with any of those set should undergo io_disarm_next() */
>> @@ -3887,6 +3899,140 @@ static int io_renameat(struct io_kiocb *req, unsigned int issue_flags)
>>  	return 0;
>>  }
>>  
>> +static int __io_setxattr_prep(struct io_kiocb *req,
>> +			const struct io_uring_sqe *sqe)
>> +{
>> +	struct io_xattr *ix = &req->xattr;
>> +	const char __user *name;
>> +	int ret;
>> +
>> +	if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL))
>> +		return -EINVAL;
>> +	if (unlikely(sqe->ioprio))
>> +		return -EINVAL;
>> +	if (unlikely(req->flags & REQ_F_FIXED_FILE))
>> +		return -EBADF;
>> +
>> +	ix->filename = NULL;
>> +	name = u64_to_user_ptr(READ_ONCE(sqe->addr));
>> +	ix->ctx.value = u64_to_user_ptr(READ_ONCE(sqe->addr2));
>> +	ix->ctx.kvalue = NULL;
>> +	ix->ctx.size = READ_ONCE(sqe->len);
>> +	ix->ctx.flags = READ_ONCE(sqe->xattr_flags);
>> +
>> +	ix->ctx.kname = kmalloc(sizeof(*ix->ctx.kname), GFP_KERNEL);
>> +	if (!ix->ctx.kname)
>> +		return -ENOMEM;
>> +
>> +	ret = setxattr_copy(name, &ix->ctx);
>> +	if (ret) {
>> +		kfree(ix->ctx.kname);
>> +		return ret;
>> +	}
>> +
>> +	req->flags |= REQ_F_NEED_CLEANUP;
>> +	return 0;
>> +}
>> +
>> +static int io_setxattr_prep(struct io_kiocb *req,
>> +			const struct io_uring_sqe *sqe)
>> +{
>> +	struct io_xattr *ix = &req->xattr;
>> +	const char __user *path;
>> +	int ret;
>> +
>> +	ret = __io_setxattr_prep(req, sqe);
>> +	if (ret)
>> +		return ret;
>> +
>> +	path = u64_to_user_ptr(READ_ONCE(sqe->addr3));
>> +
>> +	ix->filename = getname_flags(path, LOOKUP_FOLLOW, NULL);
>> +	if (IS_ERR(ix->filename)) {
>> +		ret = PTR_ERR(ix->filename);
>> +		ix->filename = NULL;
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static int io_fsetxattr_prep(struct io_kiocb *req,
>> +			const struct io_uring_sqe *sqe)
>> +{
>> +	return __io_setxattr_prep(req, sqe);
>> +}
>> +
>> +static int __io_setxattr(struct io_kiocb *req, unsigned int issue_flags,
>> +			struct path *path)
>> +{
>> +	struct io_xattr *ix = &req->xattr;
>> +	int ret;
>> +
>> +	ret = mnt_want_write(path->mnt);
>> +	if (!ret) {
>> +		ret = do_setxattr(mnt_user_ns(path->mnt), path->dentry, &ix->ctx);
>> +		mnt_drop_write(path->mnt);
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static int io_fsetxattr(struct io_kiocb *req, unsigned int issue_flags)
>> +{
>> +	struct io_xattr *ix = &req->xattr;
>> +	int ret;
>> +
>> +	if (issue_flags & IO_URING_F_NONBLOCK)
>> +		return -EAGAIN;
>> +
>> +	ret = __io_setxattr(req, issue_flags, &req->file->f_path);
>> +
>> +	req->flags &= ~REQ_F_NEED_CLEANUP;
>> +	kfree(ix->ctx.kname);
>> +
>> +	if (ix->ctx.kvalue)
>> +		kvfree(ix->ctx.kvalue);
>> +	if (ret < 0)
>> +		req_set_fail(req);
>> +
>> +	io_req_complete(req, ret);
>> +	return 0;
>> +}
>> +
>> +static int io_setxattr(struct io_kiocb *req, unsigned int issue_flags)
>> +{
>> +	struct io_xattr *ix = &req->xattr;
>> +	unsigned int lookup_flags = LOOKUP_FOLLOW;
>> +	struct path path;
>> +	int ret;
>> +
>> +	if (issue_flags & IO_URING_F_NONBLOCK)
>> +		return -EAGAIN;
>> +
>> +retry:
>> +	ret = do_user_path_at_empty(AT_FDCWD, ix->filename, lookup_flags, &path);
>> +	if (!ret) {
>> +		ret = __io_setxattr(req, issue_flags, &path);
>> +		path_put(&path);
>> +		if (retry_estale(ret, lookup_flags)) {
>> +			lookup_flags |= LOOKUP_REVAL;
>> +			goto retry;
>> +		}
>> +	}
>> +	putname(ix->filename);
>> +
>> +	req->flags &= ~REQ_F_NEED_CLEANUP;
>> +	kfree(ix->ctx.kname);
>> +
>> +	if (ix->ctx.kvalue)
>> +		kvfree(ix->ctx.kvalue);
>> +	if (ret < 0)
>> +		req_set_fail(req);
>> +
>> +	io_req_complete(req, ret);
>> +	return 0;
>> +}
> 
> (One suggestin below.)
> 
> Looks good,
> Acked-by: Christian Brauner <christian.brauner@ubuntu.com>
> 
> You could minimize the redudancy by implementing a simple helper
> callable from both io_fsetxattr() and io_setxattr() if you think it's
> worth with. So sm like:
> 
> From 2f837aa2a19b5cd8e73fffb9b87b6e6b22c5cae7 Mon Sep 17 00:00:00 2001
> From: Christian Brauner <christian.brauner@ubuntu.com>
> Date: Wed, 29 Dec 2021 15:22:34 +0100
> Subject: [PATCH] UNTESTED
> 

I added the helper function. I made a similar change for the get case.


> ---
>  fs/io_uring.c | 36 +++++++++++++++++-------------------
>  1 file changed, 17 insertions(+), 19 deletions(-)
> 
> diff --git a/fs/io_uring.c b/fs/io_uring.c
> index 7204b8d593e4..c88916b8cccc 100644
> --- a/fs/io_uring.c
> +++ b/fs/io_uring.c
> @@ -4118,6 +4118,21 @@ static int __io_setxattr(struct io_kiocb *req, unsigned int issue_flags,
>  	return ret;
>  }
>  
> +static void __io_setxattr_finish(struct io_kiocb *req, int ret)
> +{
> +	struct xattr_ctx *ctx = &req->xattr.ctx;
> +
> +	req->flags &= ~REQ_F_NEED_CLEANUP;
> +
> +	kfree(ctx->kname);
> +	if (ctx->kvalue)
> +		kvfree(ctx->kvalue);
> +
> +	if (ret < 0)
> +		req_set_fail(req);
> +
> +	io_req_complete(req, ret);
> +}
> +
>  static int io_fsetxattr(struct io_kiocb *req, unsigned int issue_flags)
>  {
>  	struct io_xattr *ix = &req->xattr;
> @@ -4127,16 +4142,7 @@ static int io_fsetxattr(struct io_kiocb *req, unsigned int issue_flags)
>  		return -EAGAIN;
>  
>  	ret = __io_setxattr(req, issue_flags, &req->file->f_path);
> -
> -	req->flags &= ~REQ_F_NEED_CLEANUP;
> -	kfree(ix->ctx.kname);
> -
> -	if (ix->ctx.kvalue)
> -		kvfree(ix->ctx.kvalue);
> -	if (ret < 0)
> -		req_set_fail(req);
> -
> -	io_req_complete(req, ret);
> +	__io_setxattr_finish(req, ret);
>  	return 0;
>  }
>  
> @@ -4162,15 +4168,7 @@ static int io_setxattr(struct io_kiocb *req, unsigned int issue_flags)
>  	}
>  	putname(ix->filename);
>  
> -	req->flags &= ~REQ_F_NEED_CLEANUP;
> -	kfree(ix->ctx.kname);
> -
> -	if (ix->ctx.kvalue)
> -		kvfree(ix->ctx.kvalue);
> -	if (ret < 0)
> -		req_set_fail(req);
> -
> -	io_req_complete(req, ret);
> +	__io_setxattr_finish(req, ret);
>  	return 0;
>  }
>  
> 

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2021-12-29 20:35 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-28 18:41 [PATCH v9 0/5] io_uring: add xattr support Stefan Roesch
2021-12-28 18:41 ` [PATCH v9 1/5] fs: split off do_user_path_at_empty from user_path_at_empty() Stefan Roesch
2021-12-29 14:31   ` Christian Brauner
2021-12-29 20:34     ` Stefan Roesch
2021-12-28 18:41 ` [PATCH v9 2/5] fs: split off setxattr_copy and do_setxattr function from setxattr Stefan Roesch
2021-12-29 14:48   ` Christian Brauner
2021-12-28 18:41 ` [PATCH v9 3/5] fs: split off do_getxattr from getxattr Stefan Roesch
2021-12-28 18:41 ` [PATCH v9 4/5] io_uring: add fsetxattr and setxattr support Stefan Roesch
2021-12-29 14:51   ` Christian Brauner
2021-12-29 20:35     ` Stefan Roesch
2021-12-28 18:41 ` [PATCH v9 5/5] io_uring: add fgetxattr and getxattr support Stefan Roesch

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.