• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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