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 <assert.h>
13
14 #include "helpers.h"
15 #include "liburing.h"
16
submit_wait(struct io_uring * ring)17 static int submit_wait(struct io_uring *ring)
18 {
19 struct io_uring_cqe *cqe;
20 int ret;
21
22 ret = io_uring_submit(ring);
23 if (ret <= 0) {
24 fprintf(stderr, "sqe submit failed: %d\n", ret);
25 return 1;
26 }
27 ret = io_uring_wait_cqe(ring, &cqe);
28 if (ret < 0) {
29 fprintf(stderr, "wait completion %d\n", ret);
30 return 1;
31 }
32
33 ret = cqe->res;
34 io_uring_cqe_seen(ring, cqe);
35 return ret;
36 }
37
try_close(struct io_uring * ring,int fd,int slot)38 static inline int try_close(struct io_uring *ring, int fd, int slot)
39 {
40 struct io_uring_sqe *sqe;
41
42 sqe = io_uring_get_sqe(ring);
43 io_uring_prep_close(sqe, fd);
44 __io_uring_set_target_fixed_file(sqe, slot);
45 return submit_wait(ring);
46 }
47
test_close_fixed(void)48 static int test_close_fixed(void)
49 {
50 struct io_uring ring;
51 struct io_uring_sqe *sqe;
52 int ret, fds[2];
53 char buf[1];
54
55 ret = io_uring_queue_init(8, &ring, 0);
56 if (ret) {
57 fprintf(stderr, "ring setup failed\n");
58 return -1;
59 }
60 if (pipe(fds)) {
61 perror("pipe");
62 return -1;
63 }
64
65 ret = try_close(&ring, 0, 0);
66 if (ret == -EINVAL) {
67 fprintf(stderr, "close for fixed files is not supported\n");
68 return 0;
69 } else if (ret != -ENXIO) {
70 fprintf(stderr, "no table failed %i\n", ret);
71 return -1;
72 }
73
74 ret = try_close(&ring, 1, 0);
75 if (ret != -EINVAL) {
76 fprintf(stderr, "set fd failed %i\n", ret);
77 return -1;
78 }
79
80 ret = io_uring_register_files(&ring, fds, 2);
81 if (ret) {
82 fprintf(stderr, "file_register: %d\n", ret);
83 return ret;
84 }
85
86 ret = try_close(&ring, 0, 2);
87 if (ret != -EINVAL) {
88 fprintf(stderr, "out of table failed %i\n", ret);
89 return -1;
90 }
91
92 ret = try_close(&ring, 0, 0);
93 if (ret != 0) {
94 fprintf(stderr, "close failed %i\n", ret);
95 return -1;
96 }
97
98 sqe = io_uring_get_sqe(&ring);
99 io_uring_prep_read(sqe, 0, buf, sizeof(buf), 0);
100 sqe->flags |= IOSQE_FIXED_FILE;
101 ret = submit_wait(&ring);
102 if (ret != -EBADF) {
103 fprintf(stderr, "read failed %i\n", ret);
104 return -1;
105 }
106
107 ret = try_close(&ring, 0, 1);
108 if (ret != 0) {
109 fprintf(stderr, "close 2 failed %i\n", ret);
110 return -1;
111 }
112
113 ret = try_close(&ring, 0, 0);
114 if (ret != -EBADF) {
115 fprintf(stderr, "empty slot failed %i\n", ret);
116 return -1;
117 }
118
119 close(fds[0]);
120 close(fds[1]);
121 io_uring_queue_exit(&ring);
122 return 0;
123 }
124
test_close(struct io_uring * ring,int fd,int is_ring_fd)125 static int test_close(struct io_uring *ring, int fd, int is_ring_fd)
126 {
127 struct io_uring_cqe *cqe;
128 struct io_uring_sqe *sqe;
129 int ret;
130
131 sqe = io_uring_get_sqe(ring);
132 if (!sqe) {
133 fprintf(stderr, "get sqe failed\n");
134 goto err;
135 }
136 io_uring_prep_close(sqe, fd);
137
138 ret = io_uring_submit(ring);
139 if (ret <= 0) {
140 fprintf(stderr, "sqe submit failed: %d\n", ret);
141 goto err;
142 }
143
144 ret = io_uring_wait_cqe(ring, &cqe);
145 if (ret < 0) {
146 if (!(is_ring_fd && ret == -EBADF)) {
147 fprintf(stderr, "wait completion %d\n", ret);
148 goto err;
149 }
150 return ret;
151 }
152 ret = cqe->res;
153 io_uring_cqe_seen(ring, cqe);
154 return ret;
155 err:
156 return -1;
157 }
158
test_openat(struct io_uring * ring,const char * path,int dfd)159 static int test_openat(struct io_uring *ring, const char *path, int dfd)
160 {
161 struct io_uring_cqe *cqe;
162 struct io_uring_sqe *sqe;
163 int ret;
164
165 sqe = io_uring_get_sqe(ring);
166 if (!sqe) {
167 fprintf(stderr, "get sqe failed\n");
168 goto err;
169 }
170 io_uring_prep_openat(sqe, dfd, path, O_RDONLY, 0);
171
172 ret = io_uring_submit(ring);
173 if (ret <= 0) {
174 fprintf(stderr, "sqe submit failed: %d\n", ret);
175 goto err;
176 }
177
178 ret = io_uring_wait_cqe(ring, &cqe);
179 if (ret < 0) {
180 fprintf(stderr, "wait completion %d\n", ret);
181 goto err;
182 }
183 ret = cqe->res;
184 io_uring_cqe_seen(ring, cqe);
185 return ret;
186 err:
187 return -1;
188 }
189
main(int argc,char * argv[])190 int main(int argc, char *argv[])
191 {
192 struct io_uring ring;
193 const char *path, *path_rel;
194 int ret, do_unlink;
195
196 ret = io_uring_queue_init(8, &ring, 0);
197 if (ret) {
198 fprintf(stderr, "ring setup failed\n");
199 return 1;
200 }
201
202 if (argc > 1) {
203 path = "/tmp/.open.close";
204 path_rel = argv[1];
205 do_unlink = 0;
206 } else {
207 path = "/tmp/.open.close";
208 path_rel = ".open.close";
209 do_unlink = 1;
210 }
211
212 t_create_file(path, 4096);
213
214 if (do_unlink)
215 t_create_file(path_rel, 4096);
216
217 ret = test_openat(&ring, path, -1);
218 if (ret < 0) {
219 if (ret == -EINVAL) {
220 fprintf(stdout, "Open not supported, skipping\n");
221 goto done;
222 }
223 fprintf(stderr, "test_openat absolute failed: %d\n", ret);
224 goto err;
225 }
226
227 ret = test_openat(&ring, path_rel, AT_FDCWD);
228 if (ret < 0) {
229 fprintf(stderr, "test_openat relative failed: %d\n", ret);
230 goto err;
231 }
232
233 ret = test_close(&ring, ret, 0);
234 if (ret) {
235 fprintf(stderr, "test_close normal failed\n");
236 goto err;
237 }
238
239 ret = test_close(&ring, ring.ring_fd, 1);
240 if (ret != -EBADF) {
241 fprintf(stderr, "test_close ring_fd failed\n");
242 goto err;
243 }
244
245 ret = test_close_fixed();
246 if (ret) {
247 fprintf(stderr, "test_close_fixed failed\n");
248 goto err;
249 }
250
251 done:
252 unlink(path);
253 if (do_unlink)
254 unlink(path_rel);
255 return 0;
256 err:
257 unlink(path);
258 if (do_unlink)
259 unlink(path_rel);
260 return 1;
261 }
262