1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Description: run various openat2(2) tests
4 *
5 */
6 #include <errno.h>
7 #include <stdio.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <fcntl.h>
12 #include <sys/uio.h>
13
14 #include "helpers.h"
15 #include "liburing.h"
16
test_openat2(struct io_uring * ring,const char * path,int dfd,bool direct,int fixed_index,int bad_how)17 static int test_openat2(struct io_uring *ring, const char *path, int dfd,
18 bool direct, int fixed_index, int bad_how)
19 {
20 struct io_uring_cqe *cqe;
21 struct io_uring_sqe *sqe;
22 struct open_how __how, *how;
23 int ret;
24
25 if (bad_how)
26 how = (struct open_how *) (uintptr_t) 0x1234;
27 else
28 how = &__how;
29
30 sqe = io_uring_get_sqe(ring);
31 if (!sqe) {
32 fprintf(stderr, "get sqe failed\n");
33 return -1;
34 }
35 if (!bad_how) {
36 memset(how, 0, sizeof(*how));
37 how->flags = O_RDWR;
38 }
39
40 if (!direct)
41 io_uring_prep_openat2(sqe, dfd, path, how);
42 else
43 io_uring_prep_openat2_direct(sqe, dfd, path, how, fixed_index);
44
45 ret = io_uring_submit(ring);
46 if (ret <= 0) {
47 fprintf(stderr, "sqe submit failed: %d\n", ret);
48 return -1;
49 }
50
51 ret = io_uring_wait_cqe(ring, &cqe);
52 if (ret < 0) {
53 fprintf(stderr, "wait completion %d\n", ret);
54 return -1;
55 }
56 ret = cqe->res;
57 io_uring_cqe_seen(ring, cqe);
58
59 if (direct && ret > 0) {
60 close(ret);
61 return -EINVAL;
62 }
63 return ret;
64 }
65
test_open_fixed(const char * path,int dfd)66 static int test_open_fixed(const char *path, int dfd)
67 {
68 struct io_uring_cqe *cqe;
69 struct io_uring_sqe *sqe;
70 struct io_uring ring;
71 const char pattern = 0xac;
72 char buffer[] = { 0, 0 };
73 int i, ret, fd = -1;
74
75 ret = io_uring_queue_init(8, &ring, 0);
76 if (ret) {
77 fprintf(stderr, "ring setup failed\n");
78 return -1;
79 }
80 ret = io_uring_register_files(&ring, &fd, 1);
81 if (ret) {
82 if (ret == -EINVAL || ret == -EBADF)
83 return 0;
84 fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
85 return -1;
86 }
87
88 ret = test_openat2(&ring, path, dfd, true, 0, 0);
89 if (ret == -EINVAL) {
90 printf("fixed open isn't supported\n");
91 return 1;
92 } else if (ret) {
93 fprintf(stderr, "direct open failed %d\n", ret);
94 return -1;
95 }
96
97 sqe = io_uring_get_sqe(&ring);
98 io_uring_prep_write(sqe, 0, &pattern, 1, 0);
99 sqe->user_data = 1;
100 sqe->flags |= IOSQE_FIXED_FILE | IOSQE_IO_LINK;
101
102 sqe = io_uring_get_sqe(&ring);
103 io_uring_prep_read(sqe, 0, buffer, 1, 0);
104 sqe->user_data = 2;
105 sqe->flags |= IOSQE_FIXED_FILE;
106
107 ret = io_uring_submit(&ring);
108 if (ret != 2) {
109 fprintf(stderr, "%s: got %d, wanted 2\n", __FUNCTION__, ret);
110 return -1;
111 }
112
113 for (i = 0; i < 2; i++) {
114 ret = io_uring_wait_cqe(&ring, &cqe);
115 if (ret < 0) {
116 fprintf(stderr, "wait completion %d\n", ret);
117 return -1;
118 }
119 if (cqe->res != 1) {
120 fprintf(stderr, "unexpectetd ret %d\n", cqe->res);
121 return -1;
122 }
123 io_uring_cqe_seen(&ring, cqe);
124 }
125 if (memcmp(&pattern, buffer, 1) != 0) {
126 fprintf(stderr, "buf validation failed\n");
127 return -1;
128 }
129
130 io_uring_queue_exit(&ring);
131 return 0;
132 }
133
test_open_fixed_fail(const char * path,int dfd)134 static int test_open_fixed_fail(const char *path, int dfd)
135 {
136 struct io_uring ring;
137 int ret, fd = -1;
138
139 ret = io_uring_queue_init(8, &ring, 0);
140 if (ret) {
141 fprintf(stderr, "ring setup failed\n");
142 return -1;
143 }
144
145 ret = test_openat2(&ring, path, dfd, true, 0, 0);
146 if (ret != -ENXIO) {
147 fprintf(stderr, "install into not existing table, %i\n", ret);
148 return 1;
149 }
150
151 ret = io_uring_register_files(&ring, &fd, 1);
152 if (ret) {
153 if (ret == -EINVAL || ret == -EBADF)
154 return 0;
155 fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
156 return -1;
157 }
158
159 ret = test_openat2(&ring, path, dfd, true, 1, 0);
160 if (ret != -EINVAL) {
161 fprintf(stderr, "install out of bounds, %i\n", ret);
162 return -1;
163 }
164
165 ret = test_openat2(&ring, path, dfd, true, (1u << 16), 0);
166 if (ret != -EINVAL) {
167 fprintf(stderr, "install out of bounds or u16 overflow, %i\n", ret);
168 return -1;
169 }
170
171 ret = test_openat2(&ring, path, dfd, true, (1u << 16) + 1, 0);
172 if (ret != -EINVAL) {
173 fprintf(stderr, "install out of bounds or u16 overflow, %i\n", ret);
174 return -1;
175 }
176
177 io_uring_queue_exit(&ring);
178 return 0;
179 }
180
test_direct_reinstall(const char * path,int dfd)181 static int test_direct_reinstall(const char *path, int dfd)
182 {
183 struct io_uring_cqe *cqe;
184 struct io_uring_sqe *sqe;
185 char buf[1] = { 0xfa };
186 struct io_uring ring;
187 int ret, pipe_fds[2];
188 ssize_t ret2;
189
190 if (pipe2(pipe_fds, O_NONBLOCK)) {
191 fprintf(stderr, "pipe() failed\n");
192 return -1;
193 }
194 ret = io_uring_queue_init(8, &ring, 0);
195 if (ret) {
196 fprintf(stderr, "ring setup failed\n");
197 return -1;
198 }
199 ret = io_uring_register_files(&ring, pipe_fds, 2);
200 if (ret) {
201 fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
202 return -1;
203 }
204
205 /* reinstall into the second slot */
206 ret = test_openat2(&ring, path, dfd, true, 1, 0);
207 if (ret != 0) {
208 fprintf(stderr, "reinstall failed, %i\n", ret);
209 return -1;
210 }
211
212 /* verify it's reinstalled, first write into the slot... */
213 sqe = io_uring_get_sqe(&ring);
214 io_uring_prep_write(sqe, 1, buf, sizeof(buf), 0);
215 sqe->flags |= IOSQE_FIXED_FILE;
216
217 ret = io_uring_submit(&ring);
218 if (ret != 1) {
219 fprintf(stderr, "sqe submit failed: %d\n", ret);
220 return -1;
221 }
222 ret = io_uring_wait_cqe(&ring, &cqe);
223 if (ret < 0) {
224 fprintf(stderr, "wait completion %d\n", ret);
225 return ret;
226 }
227 ret = cqe->res;
228 io_uring_cqe_seen(&ring, cqe);
229 if (ret != 1) {
230 fprintf(stderr, "invalid write %i\n", ret);
231 return -1;
232 }
233
234 /* ... and make sure nothing has been written to the pipe */
235 ret2 = read(pipe_fds[0], buf, 1);
236 if (ret2 != 0 && !(ret2 < 0 && errno == EAGAIN)) {
237 fprintf(stderr, "invalid pipe read, %d %d\n", errno, (int)ret2);
238 return -1;
239 }
240
241 close(pipe_fds[0]);
242 close(pipe_fds[1]);
243 io_uring_queue_exit(&ring);
244 return 0;
245 }
246
main(int argc,char * argv[])247 int main(int argc, char *argv[])
248 {
249 struct io_uring ring;
250 const char *path, *path_rel;
251 int ret, do_unlink;
252
253 ret = io_uring_queue_init(8, &ring, 0);
254 if (ret) {
255 fprintf(stderr, "ring setup failed\n");
256 return 1;
257 }
258
259 if (argc > 1) {
260 path = "/tmp/.open.at2";
261 path_rel = argv[1];
262 do_unlink = 0;
263 } else {
264 path = "/tmp/.open.at2";
265 path_rel = ".open.at2";
266 do_unlink = 1;
267 }
268
269 t_create_file(path, 4096);
270
271 if (do_unlink)
272 t_create_file(path_rel, 4096);
273
274 ret = test_openat2(&ring, path, -1, false, 0, 0);
275 if (ret < 0) {
276 if (ret == -EINVAL) {
277 fprintf(stdout, "openat2 not supported, skipping\n");
278 goto done;
279 }
280 if (ret == -EPERM || ret == -EACCES)
281 return T_EXIT_SKIP;
282 fprintf(stderr, "test_openat2 absolute failed: %d\n", ret);
283 goto err;
284 }
285
286 ret = test_openat2(&ring, path_rel, AT_FDCWD, false, 0, 0);
287 if (ret < 0) {
288 if (ret == -EPERM || ret == -EACCES)
289 return T_EXIT_SKIP;
290 fprintf(stderr, "test_openat2 relative failed: %d\n", ret);
291 goto err;
292 }
293
294 ret = test_open_fixed(path, -1);
295 if (ret > 0)
296 goto done;
297 if (ret) {
298 fprintf(stderr, "test_open_fixed failed\n");
299 goto err;
300 }
301 ret = test_open_fixed_fail(path, -1);
302 if (ret) {
303 fprintf(stderr, "test_open_fixed_fail failed\n");
304 goto err;
305 }
306
307 ret = test_direct_reinstall(path, -1);
308 if (ret) {
309 fprintf(stderr, "test_direct_reinstall failed\n");
310 goto err;
311 }
312
313 ret = test_openat2(&ring, (const char *) (uintptr_t) 0x1234, AT_FDCWD, false, 0, 0);
314 if (ret != -EFAULT) {
315 fprintf(stderr, "test_openat2 bad address failed: %d\n", ret);
316 goto err;
317 }
318
319 ret = test_openat2(&ring, path_rel, AT_FDCWD, false, 0, 1);
320 if (ret != -EFAULT) {
321 fprintf(stderr, "test_openat2 bad how failed: %d\n", ret);
322 goto err;
323 }
324
325 done:
326 unlink(path);
327 if (do_unlink)
328 unlink(path_rel);
329 return 0;
330 err:
331 unlink(path);
332 if (do_unlink)
333 unlink(path_rel);
334 return 1;
335 }
336