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