summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Roesch <shr@fb.com>2021-12-23 15:51:23 -0800
committerJens Axboe <axboe@kernel.dk>2021-12-24 07:43:02 -0700
commit29e938a1df6bd5a77a2d5fff9a14079a2b30725f (patch)
tree86569a3eb1d0392770de742992ea5074a9dad3e3
parent8fe9559e7b0271bafe5d1ff1bab03dde48dda287 (diff)
io_uring: add fgetxattr and getxattr supportfor-5.17/io_uring-xattr
This adds support to io_uring for the fgetxattr and getxattr API. Signed-off-by: Stefan Roesch <shr@fb.com> Link: https://lore.kernel.org/r/20211223235123.4092764-6-shr@fb.com Signed-off-by: Jens Axboe <axboe@kernel.dk>
-rw-r--r--fs/io_uring.c149
-rw-r--r--include/uapi/linux/io_uring.h2
2 files changed, 151 insertions, 0 deletions
diff --git a/fs/io_uring.c b/fs/io_uring.c
index cde0013e59ed..0e0ea0ed1ac1 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() */
@@ -3869,6 +3873,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->value = 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)
{
@@ -6739,6 +6871,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",
@@ -6888,6 +7024,13 @@ static void io_clean_op(struct io_kiocb *req)
kfree(req->xattr.ctx.kname);
kvfree(req->xattr.value);
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) {
@@ -7039,6 +7182,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,