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