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