Commit | Line | Data |
---|---|---|
11aeb714 JA |
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 | ||
11 | #include <uapi/linux/io_uring.h> | |
12 | ||
13 | #include "../fs/internal.h" | |
14 | ||
11aeb714 JA |
15 | #include "io_uring.h" |
16 | #include "fs.h" | |
17 | ||
18 | struct io_rename { | |
19 | struct file *file; | |
20 | int old_dfd; | |
21 | int new_dfd; | |
22 | struct filename *oldpath; | |
23 | struct filename *newpath; | |
24 | int flags; | |
25 | }; | |
26 | ||
27 | struct io_unlink { | |
28 | struct file *file; | |
29 | int dfd; | |
30 | int flags; | |
31 | struct filename *filename; | |
32 | }; | |
33 | ||
34 | struct io_mkdir { | |
35 | struct file *file; | |
36 | int dfd; | |
37 | umode_t mode; | |
38 | struct filename *filename; | |
39 | }; | |
40 | ||
41 | struct io_link { | |
42 | struct file *file; | |
43 | int old_dfd; | |
44 | int new_dfd; | |
45 | struct filename *oldpath; | |
46 | struct filename *newpath; | |
47 | int flags; | |
48 | }; | |
49 | ||
50 | int io_renameat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) | |
51 | { | |
f2ccb5ae | 52 | struct io_rename *ren = io_kiocb_to_cmd(req, struct io_rename); |
11aeb714 JA |
53 | const char __user *oldf, *newf; |
54 | ||
55 | if (sqe->buf_index || sqe->splice_fd_in) | |
56 | return -EINVAL; | |
57 | if (unlikely(req->flags & REQ_F_FIXED_FILE)) | |
58 | return -EBADF; | |
59 | ||
60 | ren->old_dfd = READ_ONCE(sqe->fd); | |
61 | oldf = u64_to_user_ptr(READ_ONCE(sqe->addr)); | |
62 | newf = u64_to_user_ptr(READ_ONCE(sqe->addr2)); | |
63 | ren->new_dfd = READ_ONCE(sqe->len); | |
64 | ren->flags = READ_ONCE(sqe->rename_flags); | |
65 | ||
66 | ren->oldpath = getname(oldf); | |
67 | if (IS_ERR(ren->oldpath)) | |
68 | return PTR_ERR(ren->oldpath); | |
69 | ||
70 | ren->newpath = getname(newf); | |
71 | if (IS_ERR(ren->newpath)) { | |
72 | putname(ren->oldpath); | |
73 | return PTR_ERR(ren->newpath); | |
74 | } | |
75 | ||
76 | req->flags |= REQ_F_NEED_CLEANUP; | |
aebb224f | 77 | req->flags |= REQ_F_FORCE_ASYNC; |
11aeb714 JA |
78 | return 0; |
79 | } | |
80 | ||
81 | int io_renameat(struct io_kiocb *req, unsigned int issue_flags) | |
82 | { | |
f2ccb5ae | 83 | struct io_rename *ren = io_kiocb_to_cmd(req, struct io_rename); |
11aeb714 JA |
84 | int ret; |
85 | ||
aebb224f | 86 | WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK); |
11aeb714 JA |
87 | |
88 | ret = do_renameat2(ren->old_dfd, ren->oldpath, ren->new_dfd, | |
89 | ren->newpath, ren->flags); | |
90 | ||
91 | req->flags &= ~REQ_F_NEED_CLEANUP; | |
92 | io_req_set_res(req, ret, 0); | |
93 | return IOU_OK; | |
94 | } | |
95 | ||
96 | void io_renameat_cleanup(struct io_kiocb *req) | |
97 | { | |
f2ccb5ae | 98 | struct io_rename *ren = io_kiocb_to_cmd(req, struct io_rename); |
11aeb714 JA |
99 | |
100 | putname(ren->oldpath); | |
101 | putname(ren->newpath); | |
102 | } | |
103 | ||
104 | int io_unlinkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) | |
105 | { | |
f2ccb5ae | 106 | struct io_unlink *un = io_kiocb_to_cmd(req, struct io_unlink); |
11aeb714 JA |
107 | const char __user *fname; |
108 | ||
109 | if (sqe->off || sqe->len || sqe->buf_index || sqe->splice_fd_in) | |
110 | return -EINVAL; | |
111 | if (unlikely(req->flags & REQ_F_FIXED_FILE)) | |
112 | return -EBADF; | |
113 | ||
114 | un->dfd = READ_ONCE(sqe->fd); | |
115 | ||
116 | un->flags = READ_ONCE(sqe->unlink_flags); | |
117 | if (un->flags & ~AT_REMOVEDIR) | |
118 | return -EINVAL; | |
119 | ||
120 | fname = u64_to_user_ptr(READ_ONCE(sqe->addr)); | |
121 | un->filename = getname(fname); | |
122 | if (IS_ERR(un->filename)) | |
123 | return PTR_ERR(un->filename); | |
124 | ||
125 | req->flags |= REQ_F_NEED_CLEANUP; | |
aebb224f | 126 | req->flags |= REQ_F_FORCE_ASYNC; |
11aeb714 JA |
127 | return 0; |
128 | } | |
129 | ||
130 | int io_unlinkat(struct io_kiocb *req, unsigned int issue_flags) | |
131 | { | |
f2ccb5ae | 132 | struct io_unlink *un = io_kiocb_to_cmd(req, struct io_unlink); |
11aeb714 JA |
133 | int ret; |
134 | ||
aebb224f | 135 | WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK); |
11aeb714 JA |
136 | |
137 | if (un->flags & AT_REMOVEDIR) | |
138 | ret = do_rmdir(un->dfd, un->filename); | |
139 | else | |
140 | ret = do_unlinkat(un->dfd, un->filename); | |
141 | ||
142 | req->flags &= ~REQ_F_NEED_CLEANUP; | |
143 | io_req_set_res(req, ret, 0); | |
144 | return IOU_OK; | |
145 | } | |
146 | ||
147 | void io_unlinkat_cleanup(struct io_kiocb *req) | |
148 | { | |
f2ccb5ae | 149 | struct io_unlink *ul = io_kiocb_to_cmd(req, struct io_unlink); |
11aeb714 JA |
150 | |
151 | putname(ul->filename); | |
152 | } | |
153 | ||
154 | int io_mkdirat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) | |
155 | { | |
f2ccb5ae | 156 | struct io_mkdir *mkd = io_kiocb_to_cmd(req, struct io_mkdir); |
11aeb714 JA |
157 | const char __user *fname; |
158 | ||
159 | if (sqe->off || sqe->rw_flags || sqe->buf_index || sqe->splice_fd_in) | |
160 | return -EINVAL; | |
161 | if (unlikely(req->flags & REQ_F_FIXED_FILE)) | |
162 | return -EBADF; | |
163 | ||
164 | mkd->dfd = READ_ONCE(sqe->fd); | |
165 | mkd->mode = READ_ONCE(sqe->len); | |
166 | ||
167 | fname = u64_to_user_ptr(READ_ONCE(sqe->addr)); | |
168 | mkd->filename = getname(fname); | |
169 | if (IS_ERR(mkd->filename)) | |
170 | return PTR_ERR(mkd->filename); | |
171 | ||
172 | req->flags |= REQ_F_NEED_CLEANUP; | |
aebb224f | 173 | req->flags |= REQ_F_FORCE_ASYNC; |
11aeb714 JA |
174 | return 0; |
175 | } | |
176 | ||
177 | int io_mkdirat(struct io_kiocb *req, unsigned int issue_flags) | |
178 | { | |
f2ccb5ae | 179 | struct io_mkdir *mkd = io_kiocb_to_cmd(req, struct io_mkdir); |
11aeb714 JA |
180 | int ret; |
181 | ||
aebb224f | 182 | WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK); |
11aeb714 JA |
183 | |
184 | ret = do_mkdirat(mkd->dfd, mkd->filename, mkd->mode); | |
185 | ||
186 | req->flags &= ~REQ_F_NEED_CLEANUP; | |
187 | io_req_set_res(req, ret, 0); | |
188 | return IOU_OK; | |
189 | } | |
190 | ||
191 | void io_mkdirat_cleanup(struct io_kiocb *req) | |
192 | { | |
f2ccb5ae | 193 | struct io_mkdir *md = io_kiocb_to_cmd(req, struct io_mkdir); |
11aeb714 JA |
194 | |
195 | putname(md->filename); | |
196 | } | |
197 | ||
198 | int io_symlinkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) | |
199 | { | |
f2ccb5ae | 200 | struct io_link *sl = io_kiocb_to_cmd(req, struct io_link); |
11aeb714 JA |
201 | const char __user *oldpath, *newpath; |
202 | ||
203 | if (sqe->len || sqe->rw_flags || sqe->buf_index || sqe->splice_fd_in) | |
204 | return -EINVAL; | |
205 | if (unlikely(req->flags & REQ_F_FIXED_FILE)) | |
206 | return -EBADF; | |
207 | ||
208 | sl->new_dfd = READ_ONCE(sqe->fd); | |
209 | oldpath = u64_to_user_ptr(READ_ONCE(sqe->addr)); | |
210 | newpath = u64_to_user_ptr(READ_ONCE(sqe->addr2)); | |
211 | ||
212 | sl->oldpath = getname(oldpath); | |
213 | if (IS_ERR(sl->oldpath)) | |
214 | return PTR_ERR(sl->oldpath); | |
215 | ||
216 | sl->newpath = getname(newpath); | |
217 | if (IS_ERR(sl->newpath)) { | |
218 | putname(sl->oldpath); | |
219 | return PTR_ERR(sl->newpath); | |
220 | } | |
221 | ||
222 | req->flags |= REQ_F_NEED_CLEANUP; | |
aebb224f | 223 | req->flags |= REQ_F_FORCE_ASYNC; |
11aeb714 JA |
224 | return 0; |
225 | } | |
226 | ||
227 | int io_symlinkat(struct io_kiocb *req, unsigned int issue_flags) | |
228 | { | |
f2ccb5ae | 229 | struct io_link *sl = io_kiocb_to_cmd(req, struct io_link); |
11aeb714 JA |
230 | int ret; |
231 | ||
aebb224f | 232 | WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK); |
11aeb714 JA |
233 | |
234 | ret = do_symlinkat(sl->oldpath, sl->new_dfd, sl->newpath); | |
235 | ||
236 | req->flags &= ~REQ_F_NEED_CLEANUP; | |
237 | io_req_set_res(req, ret, 0); | |
238 | return IOU_OK; | |
239 | } | |
240 | ||
241 | int io_linkat_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) | |
242 | { | |
f2ccb5ae | 243 | struct io_link *lnk = io_kiocb_to_cmd(req, struct io_link); |
11aeb714 JA |
244 | const char __user *oldf, *newf; |
245 | ||
a52d4f65 | 246 | if (sqe->buf_index || sqe->splice_fd_in) |
11aeb714 JA |
247 | return -EINVAL; |
248 | if (unlikely(req->flags & REQ_F_FIXED_FILE)) | |
249 | return -EBADF; | |
250 | ||
251 | lnk->old_dfd = READ_ONCE(sqe->fd); | |
252 | lnk->new_dfd = READ_ONCE(sqe->len); | |
253 | oldf = u64_to_user_ptr(READ_ONCE(sqe->addr)); | |
254 | newf = u64_to_user_ptr(READ_ONCE(sqe->addr2)); | |
255 | lnk->flags = READ_ONCE(sqe->hardlink_flags); | |
256 | ||
8479063f | 257 | lnk->oldpath = getname_uflags(oldf, lnk->flags); |
11aeb714 JA |
258 | if (IS_ERR(lnk->oldpath)) |
259 | return PTR_ERR(lnk->oldpath); | |
260 | ||
261 | lnk->newpath = getname(newf); | |
262 | if (IS_ERR(lnk->newpath)) { | |
263 | putname(lnk->oldpath); | |
264 | return PTR_ERR(lnk->newpath); | |
265 | } | |
266 | ||
267 | req->flags |= REQ_F_NEED_CLEANUP; | |
aebb224f | 268 | req->flags |= REQ_F_FORCE_ASYNC; |
11aeb714 JA |
269 | return 0; |
270 | } | |
271 | ||
272 | int io_linkat(struct io_kiocb *req, unsigned int issue_flags) | |
273 | { | |
f2ccb5ae | 274 | struct io_link *lnk = io_kiocb_to_cmd(req, struct io_link); |
11aeb714 JA |
275 | int ret; |
276 | ||
aebb224f | 277 | WARN_ON_ONCE(issue_flags & IO_URING_F_NONBLOCK); |
11aeb714 JA |
278 | |
279 | ret = do_linkat(lnk->old_dfd, lnk->oldpath, lnk->new_dfd, | |
280 | lnk->newpath, lnk->flags); | |
281 | ||
282 | req->flags &= ~REQ_F_NEED_CLEANUP; | |
283 | io_req_set_res(req, ret, 0); | |
284 | return IOU_OK; | |
285 | } | |
286 | ||
287 | void io_link_cleanup(struct io_kiocb *req) | |
288 | { | |
f2ccb5ae | 289 | struct io_link *sl = io_kiocb_to_cmd(req, struct io_link); |
11aeb714 JA |
290 | |
291 | putname(sl->oldpath); | |
292 | putname(sl->newpath); | |
293 | } |