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