xref: /linux/io_uring/xattr.c (revision 09e61efd884ca68a768717d60858f138685b161b)
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_idmap(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_idmap(path.mnt), path.dentry, &ix->ctx);
137 
138 		path_put(&path);
139 		if (retry_estale(ret, lookup_flags)) {
140 			lookup_flags |= LOOKUP_REVAL;
141 			goto retry;
142 		}
143 	}
144 
145 	io_xattr_finish(req, ret);
146 	return IOU_OK;
147 }
148 
149 static int __io_setxattr_prep(struct io_kiocb *req,
150 			const struct io_uring_sqe *sqe)
151 {
152 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
153 	const char __user *name;
154 	int ret;
155 
156 	if (unlikely(req->flags & REQ_F_FIXED_FILE))
157 		return -EBADF;
158 
159 	ix->filename = NULL;
160 	name = u64_to_user_ptr(READ_ONCE(sqe->addr));
161 	ix->ctx.cvalue = u64_to_user_ptr(READ_ONCE(sqe->addr2));
162 	ix->ctx.kvalue = NULL;
163 	ix->ctx.size = READ_ONCE(sqe->len);
164 	ix->ctx.flags = READ_ONCE(sqe->xattr_flags);
165 
166 	ix->ctx.kname = kmalloc(sizeof(*ix->ctx.kname), GFP_KERNEL);
167 	if (!ix->ctx.kname)
168 		return -ENOMEM;
169 
170 	ret = setxattr_copy(name, &ix->ctx);
171 	if (ret) {
172 		kfree(ix->ctx.kname);
173 		return ret;
174 	}
175 
176 	req->flags |= REQ_F_NEED_CLEANUP;
177 	return 0;
178 }
179 
180 int io_setxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
181 {
182 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
183 	const char __user *path;
184 	int ret;
185 
186 	ret = __io_setxattr_prep(req, sqe);
187 	if (ret)
188 		return ret;
189 
190 	path = u64_to_user_ptr(READ_ONCE(sqe->addr3));
191 
192 	ix->filename = getname_flags(path, LOOKUP_FOLLOW, NULL);
193 	if (IS_ERR(ix->filename)) {
194 		ret = PTR_ERR(ix->filename);
195 		ix->filename = NULL;
196 	}
197 
198 	return ret;
199 }
200 
201 int io_fsetxattr_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
202 {
203 	return __io_setxattr_prep(req, sqe);
204 }
205 
206 static int __io_setxattr(struct io_kiocb *req, unsigned int issue_flags,
207 			const struct path *path)
208 {
209 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
210 	int ret;
211 
212 	ret = mnt_want_write(path->mnt);
213 	if (!ret) {
214 		ret = do_setxattr(mnt_idmap(path->mnt), path->dentry, &ix->ctx);
215 		mnt_drop_write(path->mnt);
216 	}
217 
218 	return ret;
219 }
220 
221 int io_fsetxattr(struct io_kiocb *req, unsigned int issue_flags)
222 {
223 	int ret;
224 
225 	if (issue_flags & IO_URING_F_NONBLOCK)
226 		return -EAGAIN;
227 
228 	ret = __io_setxattr(req, issue_flags, &req->file->f_path);
229 	io_xattr_finish(req, ret);
230 	return IOU_OK;
231 }
232 
233 int io_setxattr(struct io_kiocb *req, unsigned int issue_flags)
234 {
235 	struct io_xattr *ix = io_kiocb_to_cmd(req, struct io_xattr);
236 	unsigned int lookup_flags = LOOKUP_FOLLOW;
237 	struct path path;
238 	int ret;
239 
240 	if (issue_flags & IO_URING_F_NONBLOCK)
241 		return -EAGAIN;
242 
243 retry:
244 	ret = filename_lookup(AT_FDCWD, ix->filename, lookup_flags, &path, NULL);
245 	if (!ret) {
246 		ret = __io_setxattr(req, issue_flags, &path);
247 		path_put(&path);
248 		if (retry_estale(ret, lookup_flags)) {
249 			lookup_flags |= LOOKUP_REVAL;
250 			goto retry;
251 		}
252 	}
253 
254 	io_xattr_finish(req, ret);
255 	return IOU_OK;
256 }
257