xref: /openbmc/linux/io_uring/xattr.c (revision aebb224f)
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 
io_xattr_cleanup(struct io_kiocb * req)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 
io_xattr_finish(struct io_kiocb * req,int ret)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 
__io_getxattr_prep(struct io_kiocb * req,const struct io_uring_sqe * sqe)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;
78*aebb224fSDylan Yudaken 	req->flags |= REQ_F_FORCE_ASYNC;
795e2a18d9SJens Axboe 	return 0;
805e2a18d9SJens Axboe }
815e2a18d9SJens Axboe 
io_fgetxattr_prep(struct io_kiocb * req,const struct io_uring_sqe * sqe)825e2a18d9SJens Axboe int io_fgetxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
835e2a18d9SJens Axboe {
845e2a18d9SJens Axboe 	return __io_getxattr_prep(req, sqe);
855e2a18d9SJens Axboe }
865e2a18d9SJens Axboe 
io_getxattr_prep(struct io_kiocb * req,const struct io_uring_sqe * sqe)875e2a18d9SJens Axboe int io_getxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
885e2a18d9SJens Axboe {
89f2ccb5aeSStefan Metzmacher 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
905e2a18d9SJens Axboe 	const char __user *path;
915e2a18d9SJens Axboe 	int ret;
925e2a18d9SJens Axboe 
935e2a18d9SJens Axboe 	ret = __io_getxattr_prep(req, sqe);
945e2a18d9SJens Axboe 	if (ret)
955e2a18d9SJens Axboe 		return ret;
965e2a18d9SJens Axboe 
975e2a18d9SJens Axboe 	path = u64_to_user_ptr(READ_ONCE(sqe->addr3));
985e2a18d9SJens Axboe 
995e2a18d9SJens Axboe 	ix->filename = getname_flags(path, LOOKUP_FOLLOW, NULL);
1005e2a18d9SJens Axboe 	if (IS_ERR(ix->filename)) {
1015e2a18d9SJens Axboe 		ret = PTR_ERR(ix->filename);
1025e2a18d9SJens Axboe 		ix->filename = NULL;
1035e2a18d9SJens Axboe 	}
1045e2a18d9SJens Axboe 
1055e2a18d9SJens Axboe 	return ret;
1065e2a18d9SJens Axboe }
1075e2a18d9SJens Axboe 
io_fgetxattr(struct io_kiocb * req,unsigned int issue_flags)1085e2a18d9SJens Axboe int io_fgetxattr(struct io_kiocb *req, unsigned int issue_flags)
1095e2a18d9SJens Axboe {
110f2ccb5aeSStefan Metzmacher 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
1115e2a18d9SJens Axboe 	int ret;
1125e2a18d9SJens Axboe 
113*aebb224fSDylan Yudaken 	WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
1145e2a18d9SJens Axboe 
1155a6f52d2SChristian Brauner 	ret = do_getxattr(mnt_idmap(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 
io_getxattr(struct io_kiocb * req,unsigned int issue_flags)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 
130*aebb224fSDylan Yudaken 	WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
1315e2a18d9SJens Axboe 
1325e2a18d9SJens Axboe retry:
1335e2a18d9SJens Axboe 	ret = filename_lookup(AT_FDCWD, ix->filename, lookup_flags, &path, NULL);
1345e2a18d9SJens Axboe 	if (!ret) {
1355a6f52d2SChristian Brauner 		ret = do_getxattr(mnt_idmap(path.mnt), path.dentry, &ix->ctx);
1365e2a18d9SJens Axboe 
1375e2a18d9SJens Axboe 		path_put(&path);
1385e2a18d9SJens Axboe 		if (retry_estale(ret, lookup_flags)) {
1395e2a18d9SJens Axboe 			lookup_flags |= LOOKUP_REVAL;
1405e2a18d9SJens Axboe 			goto retry;
1415e2a18d9SJens Axboe 		}
1425e2a18d9SJens Axboe 	}
1435e2a18d9SJens Axboe 
1445e2a18d9SJens Axboe 	io_xattr_finish(req, ret);
1455e2a18d9SJens Axboe 	return IOU_OK;
1465e2a18d9SJens Axboe }
1475e2a18d9SJens Axboe 
__io_setxattr_prep(struct io_kiocb * req,const struct io_uring_sqe * sqe)1485e2a18d9SJens Axboe static int __io_setxattr_prep(struct io_kiocb *req,
1495e2a18d9SJens Axboe 			const struct io_uring_sqe *sqe)
1505e2a18d9SJens Axboe {
151f2ccb5aeSStefan Metzmacher 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
1525e2a18d9SJens Axboe 	const char __user *name;
1535e2a18d9SJens Axboe 	int ret;
1545e2a18d9SJens Axboe 
1555e2a18d9SJens Axboe 	if (unlikely(req->flags & REQ_F_FIXED_FILE))
1565e2a18d9SJens Axboe 		return -EBADF;
1575e2a18d9SJens Axboe 
1585e2a18d9SJens Axboe 	ix->filename = NULL;
1595e2a18d9SJens Axboe 	name = u64_to_user_ptr(READ_ONCE(sqe->addr));
1605e2a18d9SJens Axboe 	ix->ctx.cvalue = u64_to_user_ptr(READ_ONCE(sqe->addr2));
1615e2a18d9SJens Axboe 	ix->ctx.kvalue = NULL;
1625e2a18d9SJens Axboe 	ix->ctx.size = READ_ONCE(sqe->len);
1635e2a18d9SJens Axboe 	ix->ctx.flags = READ_ONCE(sqe->xattr_flags);
1645e2a18d9SJens Axboe 
1655e2a18d9SJens Axboe 	ix->ctx.kname = kmalloc(sizeof(*ix->ctx.kname), GFP_KERNEL);
1665e2a18d9SJens Axboe 	if (!ix->ctx.kname)
1675e2a18d9SJens Axboe 		return -ENOMEM;
1685e2a18d9SJens Axboe 
1695e2a18d9SJens Axboe 	ret = setxattr_copy(name, &ix->ctx);
1705e2a18d9SJens Axboe 	if (ret) {
1715e2a18d9SJens Axboe 		kfree(ix->ctx.kname);
1725e2a18d9SJens Axboe 		return ret;
1735e2a18d9SJens Axboe 	}
1745e2a18d9SJens Axboe 
1755e2a18d9SJens Axboe 	req->flags |= REQ_F_NEED_CLEANUP;
176*aebb224fSDylan Yudaken 	req->flags |= REQ_F_FORCE_ASYNC;
1775e2a18d9SJens Axboe 	return 0;
1785e2a18d9SJens Axboe }
1795e2a18d9SJens Axboe 
io_setxattr_prep(struct io_kiocb * req,const struct io_uring_sqe * sqe)1805e2a18d9SJens Axboe int io_setxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
1815e2a18d9SJens Axboe {
182f2ccb5aeSStefan Metzmacher 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
1835e2a18d9SJens Axboe 	const char __user *path;
1845e2a18d9SJens Axboe 	int ret;
1855e2a18d9SJens Axboe 
1865e2a18d9SJens Axboe 	ret = __io_setxattr_prep(req, sqe);
1875e2a18d9SJens Axboe 	if (ret)
1885e2a18d9SJens Axboe 		return ret;
1895e2a18d9SJens Axboe 
1905e2a18d9SJens Axboe 	path = u64_to_user_ptr(READ_ONCE(sqe->addr3));
1915e2a18d9SJens Axboe 
1925e2a18d9SJens Axboe 	ix->filename = getname_flags(path, LOOKUP_FOLLOW, NULL);
1935e2a18d9SJens Axboe 	if (IS_ERR(ix->filename)) {
1945e2a18d9SJens Axboe 		ret = PTR_ERR(ix->filename);
1955e2a18d9SJens Axboe 		ix->filename = NULL;
1965e2a18d9SJens Axboe 	}
1975e2a18d9SJens Axboe 
1985e2a18d9SJens Axboe 	return ret;
1995e2a18d9SJens Axboe }
2005e2a18d9SJens Axboe 
io_fsetxattr_prep(struct io_kiocb * req,const struct io_uring_sqe * sqe)2015e2a18d9SJens Axboe int io_fsetxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
2025e2a18d9SJens Axboe {
2035e2a18d9SJens Axboe 	return __io_setxattr_prep(req, sqe);
2045e2a18d9SJens Axboe }
2055e2a18d9SJens Axboe 
__io_setxattr(struct io_kiocb * req,unsigned int issue_flags,const struct path * path)2065e2a18d9SJens Axboe static int __io_setxattr(struct io_kiocb *req, unsigned int issue_flags,
207e81f574dSAl Viro 			const struct path *path)
2085e2a18d9SJens Axboe {
209f2ccb5aeSStefan Metzmacher 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
2105e2a18d9SJens Axboe 	int ret;
2115e2a18d9SJens Axboe 
2125e2a18d9SJens Axboe 	ret = mnt_want_write(path->mnt);
2135e2a18d9SJens Axboe 	if (!ret) {
2145a6f52d2SChristian Brauner 		ret = do_setxattr(mnt_idmap(path->mnt), path->dentry, &ix->ctx);
2155e2a18d9SJens Axboe 		mnt_drop_write(path->mnt);
2165e2a18d9SJens Axboe 	}
2175e2a18d9SJens Axboe 
2185e2a18d9SJens Axboe 	return ret;
2195e2a18d9SJens Axboe }
2205e2a18d9SJens Axboe 
io_fsetxattr(struct io_kiocb * req,unsigned int issue_flags)2215e2a18d9SJens Axboe int io_fsetxattr(struct io_kiocb *req, unsigned int issue_flags)
2225e2a18d9SJens Axboe {
2235e2a18d9SJens Axboe 	int ret;
2245e2a18d9SJens Axboe 
225*aebb224fSDylan Yudaken 	WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
2265e2a18d9SJens Axboe 
2275e2a18d9SJens Axboe 	ret = __io_setxattr(req, issue_flags, &req->file->f_path);
2285e2a18d9SJens Axboe 	io_xattr_finish(req, ret);
2295e2a18d9SJens Axboe 	return IOU_OK;
2305e2a18d9SJens Axboe }
2315e2a18d9SJens Axboe 
io_setxattr(struct io_kiocb * req,unsigned int issue_flags)2325e2a18d9SJens Axboe int io_setxattr(struct io_kiocb *req, unsigned int issue_flags)
2335e2a18d9SJens Axboe {
234f2ccb5aeSStefan Metzmacher 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
2355e2a18d9SJens Axboe 	unsigned int lookup_flags = LOOKUP_FOLLOW;
2365e2a18d9SJens Axboe 	struct path path;
2375e2a18d9SJens Axboe 	int ret;
2385e2a18d9SJens Axboe 
239*aebb224fSDylan Yudaken 	WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK);
2405e2a18d9SJens Axboe 
2415e2a18d9SJens Axboe retry:
2425e2a18d9SJens Axboe 	ret = filename_lookup(AT_FDCWD, ix->filename, lookup_flags, &path, NULL);
2435e2a18d9SJens Axboe 	if (!ret) {
2445e2a18d9SJens Axboe 		ret = __io_setxattr(req, issue_flags, &path);
2455e2a18d9SJens Axboe 		path_put(&path);
2465e2a18d9SJens Axboe 		if (retry_estale(ret, lookup_flags)) {
2475e2a18d9SJens Axboe 			lookup_flags |= LOOKUP_REVAL;
2485e2a18d9SJens Axboe 			goto retry;
2495e2a18d9SJens Axboe 		}
2505e2a18d9SJens Axboe 	}
2515e2a18d9SJens Axboe 
2525e2a18d9SJens Axboe 	io_xattr_finish(req, ret);
2535e2a18d9SJens Axboe 	return IOU_OK;
2545e2a18d9SJens Axboe }
255