xref: /openbmc/linux/io_uring/xattr.c (revision ca637c0e)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/kernel.h>
3 #include <linux/errno.h>
4 #include <linux/fs.h>
5 #include <linux/file.h>
6 #include <linux/mm.h>
7 #include <linux/slab.h>
8 #include <linux/namei.h>
9 #include <linux/io_uring.h>
10 #include <linux/xattr.h>
11 
12 #include <uapi/linux/io_uring.h>
13 
14 #include "../fs/internal.h"
15 
16 #include "io_uring.h"
17 #include "xattr.h"
18 
19 struct io_xattr {
20 	struct file			*file;
21 	struct xattr_ctx		ctx;
22 	struct filename			*filename;
23 };
24 
25 void io_xattr_cleanup(struct io_kiocb *req)
26 {
27 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
28 
29 	if (ix->filename)
30 		putname(ix->filename);
31 
32 	kfree(ix->ctx.kname);
33 	kvfree(ix->ctx.kvalue);
34 }
35 
36 static void io_xattr_finish(struct io_kiocb *req, int ret)
37 {
38 	req->flags &= ~REQ_F_NEED_CLEANUP;
39 
40 	io_xattr_cleanup(req);
41 	io_req_set_res(req, ret, 0);
42 }
43 
44 static int __io_getxattr_prep(struct io_kiocb *req,
45 			      const struct io_uring_sqe *sqe)
46 {
47 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
48 	const char __user *name;
49 	int ret;
50 
51 	if (unlikely(req->flags & REQ_F_FIXED_FILE))
52 		return -EBADF;
53 
54 	ix->filename = NULL;
55 	ix->ctx.kvalue = NULL;
56 	name = u64_to_user_ptr(READ_ONCE(sqe->addr));
57 	ix->ctx.cvalue = u64_to_user_ptr(READ_ONCE(sqe->addr2));
58 	ix->ctx.size = READ_ONCE(sqe->len);
59 	ix->ctx.flags = READ_ONCE(sqe->xattr_flags);
60 
61 	if (ix->ctx.flags)
62 		return -EINVAL;
63 
64 	ix->ctx.kname = kmalloc(sizeof(*ix->ctx.kname), GFP_KERNEL);
65 	if (!ix->ctx.kname)
66 		return -ENOMEM;
67 
68 	ret = strncpy_from_user(ix->ctx.kname->name, name,
69 				sizeof(ix->ctx.kname->name));
70 	if (!ret || ret == sizeof(ix->ctx.kname->name))
71 		ret = -ERANGE;
72 	if (ret < 0) {
73 		kfree(ix->ctx.kname);
74 		return ret;
75 	}
76 
77 	req->flags |= REQ_F_NEED_CLEANUP;
78 	return 0;
79 }
80 
81 int io_fgetxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
82 {
83 	return __io_getxattr_prep(req, sqe);
84 }
85 
86 int io_getxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
87 {
88 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
89 	const char __user *path;
90 	int ret;
91 
92 	ret = __io_getxattr_prep(req, sqe);
93 	if (ret)
94 		return ret;
95 
96 	path = u64_to_user_ptr(READ_ONCE(sqe->addr3));
97 
98 	ix->filename = getname_flags(path, LOOKUP_FOLLOW, NULL);
99 	if (IS_ERR(ix->filename)) {
100 		ret = PTR_ERR(ix->filename);
101 		ix->filename = NULL;
102 	}
103 
104 	return ret;
105 }
106 
107 int io_fgetxattr(struct io_kiocb *req, unsigned int issue_flags)
108 {
109 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
110 	int ret;
111 
112 	if (issue_flags & IO_URING_F_NONBLOCK)
113 		return -EAGAIN;
114 
115 	ret = do_getxattr(mnt_user_ns(req->file->f_path.mnt),
116 			req->file->f_path.dentry,
117 			&ix->ctx);
118 
119 	io_xattr_finish(req, ret);
120 	return IOU_OK;
121 }
122 
123 int io_getxattr(struct io_kiocb *req, unsigned int issue_flags)
124 {
125 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
126 	unsigned int lookup_flags = LOOKUP_FOLLOW;
127 	struct path path;
128 	int ret;
129 
130 	if (issue_flags & IO_URING_F_NONBLOCK)
131 		return -EAGAIN;
132 
133 retry:
134 	ret = filename_lookup(AT_FDCWD, ix->filename, lookup_flags, &path, NULL);
135 	if (!ret) {
136 		ret = do_getxattr(mnt_user_ns(path.mnt),
137 				path.dentry,
138 				&ix->ctx);
139 
140 		path_put(&path);
141 		if (retry_estale(ret, lookup_flags)) {
142 			lookup_flags |= LOOKUP_REVAL;
143 			goto retry;
144 		}
145 	}
146 
147 	io_xattr_finish(req, ret);
148 	return IOU_OK;
149 }
150 
151 static int __io_setxattr_prep(struct io_kiocb *req,
152 			const struct io_uring_sqe *sqe)
153 {
154 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
155 	const char __user *name;
156 	int ret;
157 
158 	if (unlikely(req->flags & REQ_F_FIXED_FILE))
159 		return -EBADF;
160 
161 	ix->filename = NULL;
162 	name = u64_to_user_ptr(READ_ONCE(sqe->addr));
163 	ix->ctx.cvalue = u64_to_user_ptr(READ_ONCE(sqe->addr2));
164 	ix->ctx.kvalue = NULL;
165 	ix->ctx.size = READ_ONCE(sqe->len);
166 	ix->ctx.flags = READ_ONCE(sqe->xattr_flags);
167 
168 	ix->ctx.kname = kmalloc(sizeof(*ix->ctx.kname), GFP_KERNEL);
169 	if (!ix->ctx.kname)
170 		return -ENOMEM;
171 
172 	ret = setxattr_copy(name, &ix->ctx);
173 	if (ret) {
174 		kfree(ix->ctx.kname);
175 		return ret;
176 	}
177 
178 	req->flags |= REQ_F_NEED_CLEANUP;
179 	return 0;
180 }
181 
182 int io_setxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
183 {
184 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
185 	const char __user *path;
186 	int ret;
187 
188 	ret = __io_setxattr_prep(req, sqe);
189 	if (ret)
190 		return ret;
191 
192 	path = u64_to_user_ptr(READ_ONCE(sqe->addr3));
193 
194 	ix->filename = getname_flags(path, LOOKUP_FOLLOW, NULL);
195 	if (IS_ERR(ix->filename)) {
196 		ret = PTR_ERR(ix->filename);
197 		ix->filename = NULL;
198 	}
199 
200 	return ret;
201 }
202 
203 int io_fsetxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
204 {
205 	return __io_setxattr_prep(req, sqe);
206 }
207 
208 static int __io_setxattr(struct io_kiocb *req, unsigned int issue_flags,
209 			const struct path *path)
210 {
211 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
212 	int ret;
213 
214 	ret = mnt_want_write(path->mnt);
215 	if (!ret) {
216 		ret = do_setxattr(mnt_user_ns(path->mnt), path->dentry, &ix->ctx);
217 		mnt_drop_write(path->mnt);
218 	}
219 
220 	return ret;
221 }
222 
223 int io_fsetxattr(struct io_kiocb *req, unsigned int issue_flags)
224 {
225 	int ret;
226 
227 	if (issue_flags & IO_URING_F_NONBLOCK)
228 		return -EAGAIN;
229 
230 	ret = __io_setxattr(req, issue_flags, &req->file->f_path);
231 	io_xattr_finish(req, ret);
232 	return IOU_OK;
233 }
234 
235 int io_setxattr(struct io_kiocb *req, unsigned int issue_flags)
236 {
237 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
238 	unsigned int lookup_flags = LOOKUP_FOLLOW;
239 	struct path path;
240 	int ret;
241 
242 	if (issue_flags & IO_URING_F_NONBLOCK)
243 		return -EAGAIN;
244 
245 retry:
246 	ret = filename_lookup(AT_FDCWD, ix->filename, lookup_flags, &path, NULL);
247 	if (!ret) {
248 		ret = __io_setxattr(req, issue_flags, &path);
249 		path_put(&path);
250 		if (retry_estale(ret, lookup_flags)) {
251 			lookup_flags |= LOOKUP_REVAL;
252 			goto retry;
253 		}
254 	}
255 
256 	io_xattr_finish(req, ret);
257 	return IOU_OK;
258 }
259