Commit e9621e2b authored by Stefan Roesch's avatar Stefan Roesch Committed by Jens Axboe
Browse files

io_uring: add fsetxattr and setxattr support



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

Signed-off-by: default avatarStefan Roesch <shr@fb.com>
Acked-by: default avatarChristian Brauner <christian.brauner@ubuntu.com>
Link: https://lore.kernel.org/r/20220323154420.3301504-4-shr@fb.com


Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent c975cad9
Loading
Loading
Loading
Loading
+165 −0
Original line number Diff line number Diff line
@@ -80,6 +80,7 @@
#include <linux/io_uring.h>
#include <linux/audit.h>
#include <linux/security.h>
#include <linux/xattr.h>

#define CREATE_TRACE_POINTS
#include <trace/events/io_uring.h>
@@ -779,6 +780,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,
@@ -943,6 +950,7 @@ struct io_kiocb {
		struct io_symlink	symlink;
		struct io_hardlink	hardlink;
		struct io_msg		msg;
		struct io_xattr		xattr;
	};

	u8				opcode;
@@ -1211,6 +1219,10 @@ static const struct io_op_def io_op_defs[] = {
	[IORING_OP_MSG_RING] = {
		.needs_file		= 1,
	},
	[IORING_OP_FSETXATTR] = {
		.needs_file = 1
	},
	[IORING_OP_SETXATTR] = {},
};

/* requests with any of those set should undergo io_disarm_next() */
@@ -4190,6 +4202,144 @@ static int io_renameat(struct io_kiocb *req, unsigned int issue_flags)
	return 0;
}

static inline void __io_xattr_finish(struct io_kiocb *req)
{
	struct io_xattr *ix = &req->xattr;

	if (ix->filename)
		putname(ix->filename);

	kfree(ix->ctx.kname);
	kvfree(ix->ctx.kvalue);
}

static void io_xattr_finish(struct io_kiocb *req, int ret)
{
	req->flags &= ~REQ_F_NEED_CLEANUP;

	__io_xattr_finish(req);
	if (ret < 0)
		req_set_fail(req);

	io_req_complete(req, ret);
}

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.cvalue = 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)
{
	int ret;

	if (issue_flags & IO_URING_F_NONBLOCK)
		return -EAGAIN;

	ret = __io_setxattr(req, issue_flags, &req->file->f_path);
	io_xattr_finish(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 = filename_lookup(AT_FDCWD, ix->filename, lookup_flags, &path, NULL);
	if (!ret) {
		ret = __io_setxattr(req, issue_flags, &path);
		path_put(&path);
		if (retry_estale(ret, lookup_flags)) {
			lookup_flags |= LOOKUP_REVAL;
			goto retry;
		}
	}

	io_xattr_finish(req, ret);
	return 0;
}

static int io_unlinkat_prep(struct io_kiocb *req,
			    const struct io_uring_sqe *sqe)
{
@@ -7151,6 +7301,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_MSG_RING:
		return io_msg_ring_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",
@@ -7295,6 +7449,10 @@ static void io_clean_op(struct io_kiocb *req)
			if (req->statx.filename)
				putname(req->statx.filename);
			break;
		case IORING_OP_SETXATTR:
		case IORING_OP_FSETXATTR:
			__io_xattr_finish(req);
			break;
		}
	}
	if ((req->flags & REQ_F_POLLED) && req->apoll) {
@@ -7451,6 +7609,12 @@ static int io_issue_sqe(struct io_kiocb *req, unsigned int issue_flags)
	case IORING_OP_MSG_RING:
		ret = io_msg_ring(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;
@@ -12012,6 +12176,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));
+5 −1
Original line number Diff line number Diff line
@@ -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 {
@@ -145,6 +147,8 @@ enum {
	IORING_OP_SYMLINKAT,
	IORING_OP_LINKAT,
	IORING_OP_MSG_RING,
	IORING_OP_FSETXATTR,
	IORING_OP_SETXATTR,

	/* this goes last, obviously */
	IORING_OP_LAST,