1 /* SPDX-License-Identifier: MIT */
2
3 #include <string.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <fcntl.h>
9 #include <errno.h>
10 #include <sys/resource.h>
11 #include <unistd.h>
12
13 #include "liburing.h"
14
15 #define DIE(...) \
16 do { \
17 fprintf(stderr, __VA_ARGS__); \
18 abort(); \
19 } while(0)
20
21 static const int RSIZE = 2;
22 static const int OPEN_FLAGS = O_RDWR | O_CREAT | O_LARGEFILE;
23 static const mode_t OPEN_MODE = S_IRUSR | S_IWUSR;
24
open_io_uring(struct io_uring * ring,int dfd,const char * fn)25 static int open_io_uring(struct io_uring *ring, int dfd, const char *fn)
26 {
27 struct io_uring_sqe *sqe;
28 struct io_uring_cqe *cqe;
29 int ret, fd;
30
31 sqe = io_uring_get_sqe(ring);
32 if (!sqe) {
33 fprintf(stderr, "failed to get sqe\n");
34 return 1;
35 }
36 io_uring_prep_openat(sqe, dfd, fn, OPEN_FLAGS, OPEN_MODE);
37
38 ret = io_uring_submit(ring);
39 if (ret < 0) {
40 fprintf(stderr, "failed to submit openat: %s\n", strerror(-ret));
41 return 1;
42 }
43
44 ret = io_uring_wait_cqe(ring, &cqe);
45 fd = cqe->res;
46 io_uring_cqe_seen(ring, cqe);
47 if (ret < 0) {
48 fprintf(stderr, "wait_cqe failed: %s\n", strerror(-ret));
49 return 1;
50 } else if (fd < 0) {
51 fprintf(stderr, "io_uring openat failed: %s\n", strerror(-fd));
52 return 1;
53 }
54
55 close(fd);
56 return 0;
57 }
58
prepare_file(int dfd,const char * fn)59 static int prepare_file(int dfd, const char* fn)
60 {
61 const char buf[] = "foo";
62 int fd, res;
63
64 fd = openat(dfd, fn, OPEN_FLAGS, OPEN_MODE);
65 if (fd < 0) {
66 fprintf(stderr, "prepare/open: %s\n", strerror(errno));
67 return -1;
68 }
69
70 res = pwrite(fd, buf, sizeof(buf), 1ull << 32);
71 if (res < 0)
72 fprintf(stderr, "prepare/pwrite: %s\n", strerror(errno));
73
74 close(fd);
75 return res < 0 ? res : 0;
76 }
77
test_linked_files(int dfd,const char * fn,bool async)78 static int test_linked_files(int dfd, const char *fn, bool async)
79 {
80 struct io_uring ring;
81 struct io_uring_sqe *sqe;
82 char buffer[128];
83 struct iovec iov = {.iov_base = buffer, .iov_len = sizeof(buffer), };
84 int ret, fd;
85 int fds[2];
86
87 ret = io_uring_queue_init(10, &ring, 0);
88 if (ret < 0)
89 DIE("failed to init io_uring: %s\n", strerror(-ret));
90
91 if (pipe(fds)) {
92 perror("pipe");
93 return 1;
94 }
95
96 sqe = io_uring_get_sqe(&ring);
97 if (!sqe) {
98 printf("get sqe failed\n");
99 return -1;
100 }
101 io_uring_prep_readv(sqe, fds[0], &iov, 1, 0);
102 sqe->flags |= IOSQE_IO_LINK;
103 if (async)
104 sqe->flags |= IOSQE_ASYNC;
105
106 sqe = io_uring_get_sqe(&ring);
107 if (!sqe) {
108 fprintf(stderr, "failed to get sqe\n");
109 return 1;
110 }
111 io_uring_prep_openat(sqe, dfd, fn, OPEN_FLAGS, OPEN_MODE);
112
113 ret = io_uring_submit(&ring);
114 if (ret != 2) {
115 fprintf(stderr, "failed to submit openat: %s\n", strerror(-ret));
116 return 1;
117 }
118
119 fd = dup(ring.ring_fd);
120 if (fd < 0) {
121 fprintf(stderr, "dup() failed: %s\n", strerror(-fd));
122 return 1;
123 }
124
125 /* io_uring->flush() */
126 close(fd);
127
128 io_uring_queue_exit(&ring);
129 return 0;
130 }
131
test_drained_files(int dfd,const char * fn,bool linked,bool prepend)132 static int test_drained_files(int dfd, const char *fn, bool linked, bool prepend)
133 {
134 struct io_uring ring;
135 struct io_uring_sqe *sqe;
136 char buffer[128];
137 struct iovec iov = {.iov_base = buffer, .iov_len = sizeof(buffer), };
138 int ret, fd, fds[2], to_cancel = 0;
139
140 ret = io_uring_queue_init(10, &ring, 0);
141 if (ret < 0)
142 DIE("failed to init io_uring: %s\n", strerror(-ret));
143
144 if (pipe(fds)) {
145 perror("pipe");
146 return 1;
147 }
148
149 sqe = io_uring_get_sqe(&ring);
150 if (!sqe) {
151 printf("get sqe failed\n");
152 return -1;
153 }
154 io_uring_prep_readv(sqe, fds[0], &iov, 1, 0);
155 sqe->user_data = 0;
156
157 if (prepend) {
158 sqe = io_uring_get_sqe(&ring);
159 if (!sqe) {
160 fprintf(stderr, "failed to get sqe\n");
161 return 1;
162 }
163 io_uring_prep_nop(sqe);
164 sqe->flags |= IOSQE_IO_DRAIN;
165 to_cancel++;
166 sqe->user_data = to_cancel;
167 }
168
169 if (linked) {
170 sqe = io_uring_get_sqe(&ring);
171 if (!sqe) {
172 fprintf(stderr, "failed to get sqe\n");
173 return 1;
174 }
175 io_uring_prep_nop(sqe);
176 sqe->flags |= IOSQE_IO_DRAIN | IOSQE_IO_LINK;
177 to_cancel++;
178 sqe->user_data = to_cancel;
179 }
180
181 sqe = io_uring_get_sqe(&ring);
182 if (!sqe) {
183 fprintf(stderr, "failed to get sqe\n");
184 return 1;
185 }
186 io_uring_prep_openat(sqe, dfd, fn, OPEN_FLAGS, OPEN_MODE);
187 sqe->flags |= IOSQE_IO_DRAIN;
188 to_cancel++;
189 sqe->user_data = to_cancel;
190
191
192 ret = io_uring_submit(&ring);
193 if (ret != 1 + to_cancel) {
194 fprintf(stderr, "failed to submit openat: %s\n", strerror(-ret));
195 return 1;
196 }
197
198 fd = dup(ring.ring_fd);
199 if (fd < 0) {
200 fprintf(stderr, "dup() failed: %s\n", strerror(-fd));
201 return 1;
202 }
203
204 /*
205 * close(), which triggers ->flush(), and io_uring_queue_exit()
206 * should successfully return and not hang.
207 */
208 close(fd);
209 io_uring_queue_exit(&ring);
210 return 0;
211 }
212
main(int argc,char * argv[])213 int main(int argc, char *argv[])
214 {
215 const char *fn = "io_uring_openat_test";
216 struct io_uring ring;
217 int ret, dfd;
218
219 if (argc > 1)
220 return 0;
221
222 dfd = open("/tmp", O_PATH);
223 if (dfd < 0)
224 DIE("open /tmp: %s\n", strerror(errno));
225
226 ret = io_uring_queue_init(RSIZE, &ring, 0);
227 if (ret < 0)
228 DIE("failed to init io_uring: %s\n", strerror(-ret));
229
230 if (prepare_file(dfd, fn))
231 return 1;
232
233 ret = open_io_uring(&ring, dfd, fn);
234 if (ret) {
235 fprintf(stderr, "open_io_uring() failed\n");
236 goto out;
237 }
238
239 ret = test_linked_files(dfd, fn, false);
240 if (ret) {
241 fprintf(stderr, "test_linked_files() !async failed\n");
242 goto out;
243 }
244
245 ret = test_linked_files(dfd, fn, true);
246 if (ret) {
247 fprintf(stderr, "test_linked_files() async failed\n");
248 goto out;
249 }
250
251 ret = test_drained_files(dfd, fn, false, false);
252 if (ret) {
253 fprintf(stderr, "test_drained_files() failed\n");
254 goto out;
255 }
256
257 ret = test_drained_files(dfd, fn, false, true);
258 if (ret) {
259 fprintf(stderr, "test_drained_files() middle failed\n");
260 goto out;
261 }
262
263 ret = test_drained_files(dfd, fn, true, false);
264 if (ret) {
265 fprintf(stderr, "test_drained_files() linked failed\n");
266 goto out;
267 }
268 out:
269 io_uring_queue_exit(&ring);
270 close(dfd);
271 unlink("/tmp/io_uring_openat_test");
272 return ret;
273 }
274