xref: /openbmc/linux/io_uring/xattr.c (revision e81f574d)
15e2a18d9SJens Axboe // SPDX-License-Identifier: GPL-2.0
25e2a18d9SJens Axboe #include <linux/kernel.h>
35e2a18d9SJens Axboe #include <linux/errno.h>
45e2a18d9SJens Axboe #include <linux/fs.h>
55e2a18d9SJens Axboe #include <linux/file.h>
65e2a18d9SJens Axboe #include <linux/mm.h>
75e2a18d9SJens Axboe #include <linux/slab.h>
85e2a18d9SJens Axboe #include <linux/namei.h>
95e2a18d9SJens Axboe #include <linux/io_uring.h>
105e2a18d9SJens Axboe #include <linux/xattr.h>
115e2a18d9SJens Axboe 
125e2a18d9SJens Axboe #include <uapi/linux/io_uring.h>
135e2a18d9SJens Axboe 
145e2a18d9SJens Axboe #include "../fs/internal.h"
155e2a18d9SJens Axboe 
165e2a18d9SJens Axboe #include "io_uring.h"
175e2a18d9SJens Axboe #include "xattr.h"
185e2a18d9SJens Axboe 
195e2a18d9SJens Axboe struct io_xattr {
205e2a18d9SJens Axboe 	struct file			*file;
215e2a18d9SJens Axboe 	struct xattr_ctx		ctx;
225e2a18d9SJens Axboe 	struct filename			*filename;
235e2a18d9SJens Axboe };
245e2a18d9SJens Axboe 
255e2a18d9SJens Axboe void io_xattr_cleanup(struct io_kiocb *req)
265e2a18d9SJens Axboe {
27f2ccb5aeSStefan Metzmacher 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
285e2a18d9SJens Axboe 
295e2a18d9SJens Axboe 	if (ix->filename)
305e2a18d9SJens Axboe 		putname(ix->filename);
315e2a18d9SJens Axboe 
325e2a18d9SJens Axboe 	kfree(ix->ctx.kname);
335e2a18d9SJens Axboe 	kvfree(ix->ctx.kvalue);
345e2a18d9SJens Axboe }
355e2a18d9SJens Axboe 
365e2a18d9SJens Axboe static void io_xattr_finish(struct io_kiocb *req, int ret)
375e2a18d9SJens Axboe {
385e2a18d9SJens Axboe 	req->flags &= ~REQ_F_NEED_CLEANUP;
395e2a18d9SJens Axboe 
405e2a18d9SJens Axboe 	io_xattr_cleanup(req);
415e2a18d9SJens Axboe 	io_req_set_res(req, ret, 0);
425e2a18d9SJens Axboe }
435e2a18d9SJens Axboe 
445e2a18d9SJens Axboe static int __io_getxattr_prep(struct io_kiocb *req,
455e2a18d9SJens Axboe 			      const struct io_uring_sqe *sqe)
465e2a18d9SJens Axboe {
47f2ccb5aeSStefan Metzmacher 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
485e2a18d9SJens Axboe 	const char __user *name;
495e2a18d9SJens Axboe 	int ret;
505e2a18d9SJens Axboe 
515e2a18d9SJens Axboe 	if (unlikely(req->flags & REQ_F_FIXED_FILE))
525e2a18d9SJens Axboe 		return -EBADF;
535e2a18d9SJens Axboe 
545e2a18d9SJens Axboe 	ix->filename = NULL;
555e2a18d9SJens Axboe 	ix->ctx.kvalue = NULL;
565e2a18d9SJens Axboe 	name = u64_to_user_ptr(READ_ONCE(sqe->addr));
575e2a18d9SJens Axboe 	ix->ctx.cvalue = u64_to_user_ptr(READ_ONCE(sqe->addr2));
585e2a18d9SJens Axboe 	ix->ctx.size = READ_ONCE(sqe->len);
595e2a18d9SJens Axboe 	ix->ctx.flags = READ_ONCE(sqe->xattr_flags);
605e2a18d9SJens Axboe 
615e2a18d9SJens Axboe 	if (ix->ctx.flags)
625e2a18d9SJens Axboe 		return -EINVAL;
635e2a18d9SJens Axboe 
645e2a18d9SJens Axboe 	ix->ctx.kname = kmalloc(sizeof(*ix->ctx.kname), GFP_KERNEL);
655e2a18d9SJens Axboe 	if (!ix->ctx.kname)
665e2a18d9SJens Axboe 		return -ENOMEM;
675e2a18d9SJens Axboe 
685e2a18d9SJens Axboe 	ret = strncpy_from_user(ix->ctx.kname->name, name,
695e2a18d9SJens Axboe 				sizeof(ix->ctx.kname->name));
705e2a18d9SJens Axboe 	if (!ret || ret == sizeof(ix->ctx.kname->name))
715e2a18d9SJens Axboe 		ret = -ERANGE;
725e2a18d9SJens Axboe 	if (ret < 0) {
735e2a18d9SJens Axboe 		kfree(ix->ctx.kname);
745e2a18d9SJens Axboe 		return ret;
755e2a18d9SJens Axboe 	}
765e2a18d9SJens Axboe 
775e2a18d9SJens Axboe 	req->flags |= REQ_F_NEED_CLEANUP;
785e2a18d9SJens Axboe 	return 0;
795e2a18d9SJens Axboe }
805e2a18d9SJens Axboe 
815e2a18d9SJens Axboe int io_fgetxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
825e2a18d9SJens Axboe {
835e2a18d9SJens Axboe 	return __io_getxattr_prep(req, sqe);
845e2a18d9SJens Axboe }
855e2a18d9SJens Axboe 
865e2a18d9SJens Axboe int io_getxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
875e2a18d9SJens Axboe {
88f2ccb5aeSStefan Metzmacher 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
895e2a18d9SJens Axboe 	const char __user *path;
905e2a18d9SJens Axboe 	int ret;
915e2a18d9SJens Axboe 
925e2a18d9SJens Axboe 	ret = __io_getxattr_prep(req, sqe);
935e2a18d9SJens Axboe 	if (ret)
945e2a18d9SJens Axboe 		return ret;
955e2a18d9SJens Axboe 
965e2a18d9SJens Axboe 	path = u64_to_user_ptr(READ_ONCE(sqe->addr3));
975e2a18d9SJens Axboe 
985e2a18d9SJens Axboe 	ix->filename = getname_flags(path, LOOKUP_FOLLOW, NULL);
995e2a18d9SJens Axboe 	if (IS_ERR(ix->filename)) {
1005e2a18d9SJens Axboe 		ret = PTR_ERR(ix->filename);
1015e2a18d9SJens Axboe 		ix->filename = NULL;
1025e2a18d9SJens Axboe 	}
1035e2a18d9SJens Axboe 
1045e2a18d9SJens Axboe 	return ret;
1055e2a18d9SJens Axboe }
1065e2a18d9SJens Axboe 
1075e2a18d9SJens Axboe int io_fgetxattr(struct io_kiocb *req, unsigned int issue_flags)
1085e2a18d9SJens Axboe {
109f2ccb5aeSStefan Metzmacher 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
1105e2a18d9SJens Axboe 	int ret;
1115e2a18d9SJens Axboe 
1125e2a18d9SJens Axboe 	if (issue_flags & IO_URING_F_NONBLOCK)
1135e2a18d9SJens Axboe 		return -EAGAIN;
1145e2a18d9SJens Axboe 
1155e2a18d9SJens Axboe 	ret = do_getxattr(mnt_user_ns(req->file->f_path.mnt),
1165e2a18d9SJens Axboe 			req->file->f_path.dentry,
1175e2a18d9SJens Axboe 			&ix->ctx);
1185e2a18d9SJens Axboe 
1195e2a18d9SJens Axboe 	io_xattr_finish(req, ret);
1205e2a18d9SJens Axboe 	return IOU_OK;
1215e2a18d9SJens Axboe }
1225e2a18d9SJens Axboe 
1235e2a18d9SJens Axboe int io_getxattr(struct io_kiocb *req, unsigned int issue_flags)
1245e2a18d9SJens Axboe {
125f2ccb5aeSStefan Metzmacher 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
1265e2a18d9SJens Axboe 	unsigned int lookup_flags = LOOKUP_FOLLOW;
1275e2a18d9SJens Axboe 	struct path path;
1285e2a18d9SJens Axboe 	int ret;
1295e2a18d9SJens Axboe 
1305e2a18d9SJens Axboe 	if (issue_flags & IO_URING_F_NONBLOCK)
1315e2a18d9SJens Axboe 		return -EAGAIN;
1325e2a18d9SJens Axboe 
1335e2a18d9SJens Axboe retry:
1345e2a18d9SJens Axboe 	ret = filename_lookup(AT_FDCWD, ix->filename, lookup_flags, &path, NULL);
1355e2a18d9SJens Axboe 	if (!ret) {
1365e2a18d9SJens Axboe 		ret = do_getxattr(mnt_user_ns(path.mnt),
1375e2a18d9SJens Axboe 				path.dentry,
1385e2a18d9SJens Axboe 				&ix->ctx);
1395e2a18d9SJens Axboe 
1405e2a18d9SJens Axboe 		path_put(&path);
1415e2a18d9SJens Axboe 		if (retry_estale(ret, lookup_flags)) {
1425e2a18d9SJens Axboe 			lookup_flags |= LOOKUP_REVAL;
1435e2a18d9SJens Axboe 			goto retry;
1445e2a18d9SJens Axboe 		}
1455e2a18d9SJens Axboe 	}
1465e2a18d9SJens Axboe 
1475e2a18d9SJens Axboe 	io_xattr_finish(req, ret);
1485e2a18d9SJens Axboe 	return IOU_OK;
1495e2a18d9SJens Axboe }
1505e2a18d9SJens Axboe 
1515e2a18d9SJens Axboe static int __io_setxattr_prep(struct io_kiocb *req,
1525e2a18d9SJens Axboe 			const struct io_uring_sqe *sqe)
1535e2a18d9SJens Axboe {
154f2ccb5aeSStefan Metzmacher 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
1555e2a18d9SJens Axboe 	const char __user *name;
1565e2a18d9SJens Axboe 	int ret;
1575e2a18d9SJens Axboe 
1585e2a18d9SJens Axboe 	if (unlikely(req->flags & REQ_F_FIXED_FILE))
1595e2a18d9SJens Axboe 		return -EBADF;
1605e2a18d9SJens Axboe 
1615e2a18d9SJens Axboe 	ix->filename = NULL;
1625e2a18d9SJens Axboe 	name = u64_to_user_ptr(READ_ONCE(sqe->addr));
1635e2a18d9SJens Axboe 	ix->ctx.cvalue = u64_to_user_ptr(READ_ONCE(sqe->addr2));
1645e2a18d9SJens Axboe 	ix->ctx.kvalue = NULL;
1655e2a18d9SJens Axboe 	ix->ctx.size = READ_ONCE(sqe->len);
1665e2a18d9SJens Axboe 	ix->ctx.flags = READ_ONCE(sqe->xattr_flags);
1675e2a18d9SJens Axboe 
1685e2a18d9SJens Axboe 	ix->ctx.kname = kmalloc(sizeof(*ix->ctx.kname), GFP_KERNEL);
1695e2a18d9SJens Axboe 	if (!ix->ctx.kname)
1705e2a18d9SJens Axboe 		return -ENOMEM;
1715e2a18d9SJens Axboe 
1725e2a18d9SJens Axboe 	ret = setxattr_copy(name, &ix->ctx);
1735e2a18d9SJens Axboe 	if (ret) {
1745e2a18d9SJens Axboe 		kfree(ix->ctx.kname);
1755e2a18d9SJens Axboe 		return ret;
1765e2a18d9SJens Axboe 	}
1775e2a18d9SJens Axboe 
1785e2a18d9SJens Axboe 	req->flags |= REQ_F_NEED_CLEANUP;
1795e2a18d9SJens Axboe 	return 0;
1805e2a18d9SJens Axboe }
1815e2a18d9SJens Axboe 
1825e2a18d9SJens Axboe int io_setxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
1835e2a18d9SJens Axboe {
184f2ccb5aeSStefan Metzmacher 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
1855e2a18d9SJens Axboe 	const char __user *path;
1865e2a18d9SJens Axboe 	int ret;
1875e2a18d9SJens Axboe 
1885e2a18d9SJens Axboe 	ret = __io_setxattr_prep(req, sqe);
1895e2a18d9SJens Axboe 	if (ret)
1905e2a18d9SJens Axboe 		return ret;
1915e2a18d9SJens Axboe 
1925e2a18d9SJens Axboe 	path = u64_to_user_ptr(READ_ONCE(sqe->addr3));
1935e2a18d9SJens Axboe 
1945e2a18d9SJens Axboe 	ix->filename = getname_flags(path, LOOKUP_FOLLOW, NULL);
1955e2a18d9SJens Axboe 	if (IS_ERR(ix->filename)) {
1965e2a18d9SJens Axboe 		ret = PTR_ERR(ix->filename);
1975e2a18d9SJens Axboe 		ix->filename = NULL;
1985e2a18d9SJens Axboe 	}
1995e2a18d9SJens Axboe 
2005e2a18d9SJens Axboe 	return ret;
2015e2a18d9SJens Axboe }
2025e2a18d9SJens Axboe 
2035e2a18d9SJens Axboe int io_fsetxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
2045e2a18d9SJens Axboe {
2055e2a18d9SJens Axboe 	return __io_setxattr_prep(req, sqe);
2065e2a18d9SJens Axboe }
2075e2a18d9SJens Axboe 
2085e2a18d9SJens Axboe static int __io_setxattr(struct io_kiocb *req, unsigned int issue_flags,
209*e81f574dSAl Viro 			const struct path *path)
2105e2a18d9SJens Axboe {
211f2ccb5aeSStefan Metzmacher 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
2125e2a18d9SJens Axboe 	int ret;
2135e2a18d9SJens Axboe 
2145e2a18d9SJens Axboe 	ret = mnt_want_write(path->mnt);
2155e2a18d9SJens Axboe 	if (!ret) {
2165e2a18d9SJens Axboe 		ret = do_setxattr(mnt_user_ns(path->mnt), path->dentry, &ix->ctx);
2175e2a18d9SJens Axboe 		mnt_drop_write(path->mnt);
2185e2a18d9SJens Axboe 	}
2195e2a18d9SJens Axboe 
2205e2a18d9SJens Axboe 	return ret;
2215e2a18d9SJens Axboe }
2225e2a18d9SJens Axboe 
2235e2a18d9SJens Axboe int io_fsetxattr(struct io_kiocb *req, unsigned int issue_flags)
2245e2a18d9SJens Axboe {
2255e2a18d9SJens Axboe 	int ret;
2265e2a18d9SJens Axboe 
2275e2a18d9SJens Axboe 	if (issue_flags & IO_URING_F_NONBLOCK)
2285e2a18d9SJens Axboe 		return -EAGAIN;
2295e2a18d9SJens Axboe 
2305e2a18d9SJens Axboe 	ret = __io_setxattr(req, issue_flags, &req->file->f_path);
2315e2a18d9SJens Axboe 	io_xattr_finish(req, ret);
2325e2a18d9SJens Axboe 	return IOU_OK;
2335e2a18d9SJens Axboe }
2345e2a18d9SJens Axboe 
2355e2a18d9SJens Axboe int io_setxattr(struct io_kiocb *req, unsigned int issue_flags)
2365e2a18d9SJens Axboe {
237f2ccb5aeSStefan Metzmacher 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
2385e2a18d9SJens Axboe 	unsigned int lookup_flags = LOOKUP_FOLLOW;
2395e2a18d9SJens Axboe 	struct path path;
2405e2a18d9SJens Axboe 	int ret;
2415e2a18d9SJens Axboe 
2425e2a18d9SJens Axboe 	if (issue_flags & IO_URING_F_NONBLOCK)
2435e2a18d9SJens Axboe 		return -EAGAIN;
2445e2a18d9SJens Axboe 
2455e2a18d9SJens Axboe retry:
2465e2a18d9SJens Axboe 	ret = filename_lookup(AT_FDCWD, ix->filename, lookup_flags, &path, NULL);
2475e2a18d9SJens Axboe 	if (!ret) {
2485e2a18d9SJens Axboe 		ret = __io_setxattr(req, issue_flags, &path);
2495e2a18d9SJens Axboe 		path_put(&path);
2505e2a18d9SJens Axboe 		if (retry_estale(ret, lookup_flags)) {
2515e2a18d9SJens Axboe 			lookup_flags |= LOOKUP_REVAL;
2525e2a18d9SJens Axboe 			goto retry;
2535e2a18d9SJens Axboe 		}
2545e2a18d9SJens Axboe 	}
2555e2a18d9SJens Axboe 
2565e2a18d9SJens Axboe 	io_xattr_finish(req, ret);
2575e2a18d9SJens Axboe 	return IOU_OK;
2585e2a18d9SJens Axboe }
259