* [PATCH v8 1/5] fs: split off do_user_path_at_empty from user_path_at_empty()
2021-12-23 23:51 [PATCH v8 0/5] io_uring: add xattr support Stefan Roesch
@ 2021-12-23 23:51 ` Stefan Roesch
2021-12-23 23:51 ` [PATCH v8 2/5] fs: split off setxattr_copy and do_setxattr function from setxattr Stefan Roesch
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Stefan Roesch @ 2021-12-23 23:51 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 <[email protected]>
Acked-by: Christian Brauner <[email protected]>
---
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] 7+ messages in thread
* [PATCH v8 2/5] fs: split off setxattr_copy and do_setxattr function from setxattr
2021-12-23 23:51 [PATCH v8 0/5] io_uring: add xattr support Stefan Roesch
2021-12-23 23:51 ` [PATCH v8 1/5] fs: split off do_user_path_at_empty from user_path_at_empty() Stefan Roesch
@ 2021-12-23 23:51 ` Stefan Roesch
2021-12-23 23:51 ` [PATCH v8 3/5] fs: split off do_getxattr from getxattr Stefan Roesch
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Stefan Roesch @ 2021-12-23 23:51 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 <[email protected]>
---
fs/internal.h | 22 +++++++++++++
fs/xattr.c | 87 ++++++++++++++++++++++++++++++++++++++-------------
2 files changed, 87 insertions(+), 22 deletions(-)
diff --git a/fs/internal.h b/fs/internal.h
index 432ea3ce76ec..b07df1623de6 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -202,3 +202,25 @@ 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;
+ size_t size;
+ /* Attribute name */
+ struct xattr_name *kname;
+ unsigned int flags;
+};
+
+
+int setxattr_copy(const char __user *name, struct xattr_ctx *ctx,
+ void **xattr_val);
+int do_setxattr(struct user_namespace *mnt_userns, struct dentry *dentry,
+ struct xattr_ctx *ctx, void *xattr_val);
diff --git a/fs/xattr.c b/fs/xattr.c
index 5c8c5175b385..dcb4f0ff7e6e 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,84 @@ 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,
+ void **xattr_val)
{
- int error;
void *kvalue = NULL;
- char kname[XATTR_NAME_MAX + 1];
+ int error;
- 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);
+
+ kvalue = kvmalloc(ctx->size, GFP_KERNEL);
if (!kvalue)
return -ENOMEM;
- if (copy_from_user(kvalue, value, size)) {
- error = -EFAULT;
- goto out;
+
+ if (copy_from_user(kvalue, ctx->value, ctx->size)) {
+ kvfree(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);
+ *xattr_val = kvalue;
+ return 0;
+}
+
+static void setxattr_convert(struct user_namespace *mnt_userns,
+ struct xattr_ctx *ctx, void *xattr_value)
+{
+ 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, xattr_value, ctx->size);
+}
+
+int do_setxattr(struct user_namespace *mnt_userns, struct dentry *dentry,
+ struct xattr_ctx *ctx, void *xattr_value)
+{
+ int error;
+
+ setxattr_convert(mnt_userns, ctx, xattr_value);
+ error = vfs_setxattr(mnt_userns, dentry, ctx->kname->name,
+ xattr_value, ctx->size, ctx->flags);
+
+ return error;
+}
+
+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,
+ .size = size,
+ .kname = &kname,
+ .flags = flags,
+ };
+ void *xattr_value = NULL;
+ int error;
+
+ error = setxattr_copy(name, &ctx, &xattr_value);
+ if (error)
+ return error;
+
+ error = do_setxattr(mnt_userns, d, &ctx, xattr_value);
+ kvfree(xattr_value);
return error;
}
--
2.30.2
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v8 3/5] fs: split off do_getxattr from getxattr
2021-12-23 23:51 [PATCH v8 0/5] io_uring: add xattr support Stefan Roesch
2021-12-23 23:51 ` [PATCH v8 1/5] fs: split off do_user_path_at_empty from user_path_at_empty() Stefan Roesch
2021-12-23 23:51 ` [PATCH v8 2/5] fs: split off setxattr_copy and do_setxattr function from setxattr Stefan Roesch
@ 2021-12-23 23:51 ` Stefan Roesch
2021-12-23 23:51 ` [PATCH v8 4/5] io_uring: add fsetxattr and setxattr support Stefan Roesch
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Stefan Roesch @ 2021-12-23 23:51 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 <[email protected]>
Acked-by: Christian Brauner <[email protected]>
---
fs/internal.h | 6 ++++++
fs/xattr.c | 32 ++++++++++++++++++++------------
2 files changed, 26 insertions(+), 12 deletions(-)
diff --git a/fs/internal.h b/fs/internal.h
index b07df1623de6..420d0283be12 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -220,6 +220,12 @@ struct xattr_ctx {
};
+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,
void **xattr_val);
int do_setxattr(struct user_namespace *mnt_userns, struct dentry *dentry,
diff --git a/fs/xattr.c b/fs/xattr.c
index dcb4f0ff7e6e..51e305db426f 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -684,19 +684,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)
@@ -720,10 +713,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] 7+ messages in thread
* [PATCH v8 4/5] io_uring: add fsetxattr and setxattr support
2021-12-23 23:51 [PATCH v8 0/5] io_uring: add xattr support Stefan Roesch
` (2 preceding siblings ...)
2021-12-23 23:51 ` [PATCH v8 3/5] fs: split off do_getxattr from getxattr Stefan Roesch
@ 2021-12-23 23:51 ` Stefan Roesch
2021-12-23 23:51 ` [PATCH v8 5/5] io_uring: add fgetxattr and getxattr support Stefan Roesch
2021-12-24 12:47 ` [PATCH v8 0/5] io_uring: add xattr support Christian Brauner
5 siblings, 0 replies; 7+ messages in thread
From: Stefan Roesch @ 2021-12-23 23:51 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 <[email protected]>
---
fs/io_uring.c | 166 ++++++++++++++++++++++++++++++++++
include/uapi/linux/io_uring.h | 6 +-
2 files changed, 171 insertions(+), 1 deletion(-)
diff --git a/fs/io_uring.c b/fs/io_uring.c
index c8258c784116..114af3de2120 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,13 @@ struct io_async_rw {
struct wait_page_queue wpq;
};
+struct io_xattr {
+ struct file *file;
+ struct xattr_ctx ctx;
+ void *value;
+ struct filename *filename;
+};
+
enum {
REQ_F_FIXED_FILE_BIT = IOSQE_FIXED_FILE_BIT,
REQ_F_IO_DRAIN_BIT = IOSQE_IO_DRAIN_BIT,
@@ -866,6 +874,7 @@ struct io_kiocb {
struct io_symlink symlink;
struct io_hardlink hardlink;
struct io_getdents getdents;
+ struct io_xattr xattr;
};
u8 opcode;
@@ -1118,6 +1127,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 +3900,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.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, &ix->value);
+ 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, ix->value);
+ 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->value)
+ kvfree(ix->value);
+ 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->value)
+ kvfree(ix->value);
+ 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 +6770,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 +6915,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.value);
+ break;
}
}
if ((req->flags & REQ_F_POLLED) && req->apoll) {
@@ -6909,6 +7068,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 +11442,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] 7+ messages in thread
* [PATCH v8 5/5] io_uring: add fgetxattr and getxattr support
2021-12-23 23:51 [PATCH v8 0/5] io_uring: add xattr support Stefan Roesch
` (3 preceding siblings ...)
2021-12-23 23:51 ` [PATCH v8 4/5] io_uring: add fsetxattr and setxattr support Stefan Roesch
@ 2021-12-23 23:51 ` Stefan Roesch
2021-12-24 12:47 ` [PATCH v8 0/5] io_uring: add xattr support Christian Brauner
5 siblings, 0 replies; 7+ messages in thread
From: Stefan Roesch @ 2021-12-23 23:51 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 <[email protected]>
---
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 114af3de2120..35414b7159d3 100644
--- a/fs/io_uring.c
+++ b/fs/io_uring.c
@@ -1131,6 +1131,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() */
@@ -3900,6 +3904,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)
{
@@ -6774,6 +6906,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",
@@ -6923,6 +7059,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) {
@@ -7074,6 +7217,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] 7+ messages in thread
* Re: [PATCH v8 0/5] io_uring: add xattr support
2021-12-23 23:51 [PATCH v8 0/5] io_uring: add xattr support Stefan Roesch
` (4 preceding siblings ...)
2021-12-23 23:51 ` [PATCH v8 5/5] io_uring: add fgetxattr and getxattr support Stefan Roesch
@ 2021-12-24 12:47 ` Christian Brauner
5 siblings, 0 replies; 7+ messages in thread
From: Christian Brauner @ 2021-12-24 12:47 UTC (permalink / raw)
To: Stefan Roesch; +Cc: io-uring, linux-fsdevel, kernel-team, torvalds
On Thu, Dec 23, 2021 at 03:51:18PM -0800, Stefan Roesch wrote:
> 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.
>
>
> V8: - introduce xattr_name struct as advised by Linus
> - remove kname_sz field in xattr_ctx
I'm fine with the series as is. After this goes in we can cleanup a bit
more in xattr.c as I've mentioned in an earlier thread fairly early on.
The new struct xattr_ctx is quite nice for that, I think.
I would have one last suggestion/question. Can we simply keep the copied
value directly in struct xattr_ctx instead of passing it around
separately? Since we're just moving one argument from struct io_xattr to
struct xattr_ctx nothing changes for io_uring in terms of size:
struct xattr_ctx {
/* Value of attribute */
const void __user *value;
+ void *kvalue;
size_t size;
/* Attribute name */
struct xattr_name *kname;
unsigned int flags;
};
(Ultimately we might want to convert some more helpers to use struct
xattr_ctx instead of separately passing arguments. As part of that
struct xattr_ctx might need to grow an additional size_t ksize argument
specific for kvalue because right now some codepaths can end up changing
the size of kvalue. The way it's done right now is pretty messy because
we just update the size argument. But that's not relevant to get this
basic patchset for io_uring merged. I can take care of that cleanup
after the holidays and once the merge-window closes.)
So on top of what you do right here integrating something like:
From ce2d73d2a5f9820e81f207f2908f74cb11e2660f Mon Sep 17 00:00:00 2001
From: Christian Brauner <[email protected]>
Date: Fri, 24 Dec 2021 13:19:26 +0100
Subject: [PATCH] UNTESTED
---
fs/internal.h | 6 +++---
fs/io_uring.c | 19 ++++++++-----------
fs/xattr.c | 36 ++++++++++++++----------------------
3 files changed, 25 insertions(+), 36 deletions(-)
diff --git a/fs/internal.h b/fs/internal.h
index 420d0283be12..942b2005a2be 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -213,6 +213,7 @@ struct xattr_name {
struct xattr_ctx {
/* Value of attribute */
const void __user *value;
+ void *kvalue;
size_t size;
/* Attribute name */
struct xattr_name *kname;
@@ -226,7 +227,6 @@ ssize_t do_getxattr(struct user_namespace *mnt_userns,
void __user *value,
size_t size);
-int setxattr_copy(const char __user *name, struct xattr_ctx *ctx,
- void **xattr_val);
+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, void *xattr_val);
+ struct xattr_ctx *ctx);
diff --git a/fs/io_uring.c b/fs/io_uring.c
index 93a85cefd69a..b9ecece28cf5 100644
--- a/fs/io_uring.c
+++ b/fs/io_uring.c
@@ -729,7 +729,6 @@ struct io_async_rw {
struct io_xattr {
struct file *file;
struct xattr_ctx ctx;
- void *value;
struct filename *filename;
};
@@ -873,7 +872,7 @@ struct io_kiocb {
struct io_symlink symlink;
struct io_hardlink hardlink;
struct io_getdents getdents;
struct io_xattr xattr;
};
u8 opcode;
@@ -3927,7 +3926,6 @@ static int __io_getxattr_prep(struct io_kiocb *req,
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);
@@ -4064,7 +4062,7 @@ static int __io_setxattr_prep(struct io_kiocb *req,
if (!ix->ctx.kname)
return -ENOMEM;
- ret = setxattr_copy(name, &ix->ctx, &ix->value);
+ ret = setxattr_copy(name, &ix->ctx);
if (ret) {
kfree(ix->ctx.kname);
return ret;
@@ -4110,8 +4108,7 @@ static int __io_setxattr(struct io_kiocb *req, unsigned int issue_flags,
ret = mnt_want_write(path->mnt);
if (!ret) {
- ret = do_setxattr(mnt_user_ns(path->mnt), path->dentry,
- &ix->ctx, ix->value);
+ ret = do_setxattr(mnt_user_ns(path->mnt), path->dentry, &ix->ctx);
mnt_drop_write(path->mnt);
}
@@ -4131,8 +4128,8 @@ static int io_fsetxattr(struct io_kiocb *req, unsigned int issue_flags)
req->flags &= ~REQ_F_NEED_CLEANUP;
kfree(ix->ctx.kname);
- if (ix->value)
- kvfree(ix->value);
+ if (ix->ctx.kvalue)
+ kvfree(ix->ctx.kvalue);
if (ret < 0)
req_set_fail(req);
@@ -4165,8 +4162,8 @@ static int io_setxattr(struct io_kiocb *req, unsigned int issue_flags)
req->flags &= ~REQ_F_NEED_CLEANUP;
kfree(ix->ctx.kname);
- if (ix->value)
- kvfree(ix->value);
+ if (ix->ctx.kvalue)
+ kvfree(ix->ctx.kvalue);
if (ret < 0)
req_set_fail(req);
@@ -7065,7 +7062,7 @@ static void io_clean_op(struct io_kiocb *req)
fallthrough;
case IORING_OP_FSETXATTR:
kfree(req->xattr.ctx.kname);
- kvfree(req->xattr.value);
+ kvfree(req->xattr.ctx.kvalue);
break;
case IORING_OP_GETXATTR:
if (req->xattr.filename)
diff --git a/fs/xattr.c b/fs/xattr.c
index 51e305db426f..32f97e58a125 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -542,10 +542,8 @@ EXPORT_SYMBOL_GPL(vfs_removexattr);
* Extended attribute SET operations
*/
-int setxattr_copy(const char __user *name, struct xattr_ctx *ctx,
- void **xattr_val)
+int setxattr_copy(const char __user *name, struct xattr_ctx *ctx)
{
- void *kvalue = NULL;
int error;
if (ctx->flags & ~(XATTR_CREATE|XATTR_REPLACE))
@@ -562,39 +560,34 @@ int setxattr_copy(const char __user *name, struct xattr_ctx *ctx,
if (ctx->size > XATTR_SIZE_MAX)
return -E2BIG;
- kvalue = kvmalloc(ctx->size, GFP_KERNEL);
- if (!kvalue)
+ ctx->kvalue = kvmalloc(ctx->size, GFP_KERNEL);
+ if (!ctx->kvalue)
return -ENOMEM;
- if (copy_from_user(kvalue, ctx->value, ctx->size)) {
- kvfree(kvalue);
+ if (copy_from_user(ctx->kvalue, ctx->value, ctx->size)) {
+ kvfree(ctx->kvalue);
return -EFAULT;
}
}
- *xattr_val = kvalue;
return 0;
}
static void setxattr_convert(struct user_namespace *mnt_userns,
- struct xattr_ctx *ctx, void *xattr_value)
+ 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, xattr_value, ctx->size);
+ 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, void *xattr_value)
+ struct xattr_ctx *ctx)
{
- int error;
-
- setxattr_convert(mnt_userns, ctx, xattr_value);
- error = vfs_setxattr(mnt_userns, dentry, ctx->kname->name,
- xattr_value, ctx->size, ctx->flags);
-
- return error;
+ setxattr_convert(mnt_userns, ctx);
+ return vfs_setxattr(mnt_userns, dentry, ctx->kname->name,
+ ctx->kvalue, ctx->size, ctx->flags);
}
static long
@@ -609,16 +602,15 @@ setxattr(struct user_namespace *mnt_userns, struct dentry *d,
.kname = &kname,
.flags = flags,
};
- void *xattr_value = NULL;
int error;
- error = setxattr_copy(name, &ctx, &xattr_value);
+ error = setxattr_copy(name, &ctx);
if (error)
return error;
- error = do_setxattr(mnt_userns, d, &ctx, xattr_value);
+ error = do_setxattr(mnt_userns, d, &ctx);
- kvfree(xattr_value);
+ kvfree(ctx.kvalue);
return error;
}
--
2.30.2
^ permalink raw reply related [flat|nested] 7+ messages in thread