Re: [PATCH v9 4/5] io_uring: add fsetxattr and setxattr support
From: Christian Brauner <hidden>
Date: 2021-12-29 14:52:07
Also in:
linux-fsdevel
Subsystem:
filesystems (vfs and infrastructure), the rest · Maintainers:
Alexander Viro, Christian Brauner, Linus Torvalds
On Tue, Dec 28, 2021 at 10:41:44AM -0800, Stefan Roesch wrote:
quoted hunk ↗ jump to hunk
This adds support to io_uring for the fsetxattr and setxattr API. Signed-off-by: Stefan Roesch <redacted> --- 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 <redacted> 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 <redacted> 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