1 #include <assert.h>
2 #include <fcntl.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <sys/xattr.h>
7 #include <unistd.h>
8
9 #include "helpers.h"
10 #include "liburing.h"
11
12 static int no_xattr;
13
14 /* Define constants. */
15 #define XATTR_SIZE 255
16 #define QUEUE_DEPTH 32
17
18 #define FILENAME "xattr.test"
19 #define KEY1 "user.val1"
20 #define KEY2 "user.val2"
21 #define VALUE1 "value1"
22 #define VALUE2 "value2-a-lot-longer"
23
24
25 /* Call fsetxattr. */
io_uring_fsetxattr(struct io_uring * ring,int fd,const char * name,const void * value,size_t size,int flags)26 static int io_uring_fsetxattr(struct io_uring *ring, int fd, const char *name,
27 const void *value, size_t size, int flags)
28 {
29 struct io_uring_sqe *sqe;
30 struct io_uring_cqe *cqe;
31 int ret;
32
33 sqe = io_uring_get_sqe(ring);
34 if (!sqe) {
35 fprintf(stderr, "Error cannot get sqe\n");
36 return -1;
37 }
38
39 io_uring_prep_fsetxattr(sqe, fd, name, value, flags, size);
40
41 ret = io_uring_submit(ring);
42 if (ret != 1) {
43 fprintf(stderr, "Error io_uring_submit_and_wait: ret=%d\n", ret);
44 return -1;
45 }
46
47 ret = io_uring_wait_cqe(ring, &cqe);
48 if (ret) {
49 fprintf(stderr, "Error io_uring_wait_cqe: ret=%d\n", ret);
50 return -1;
51 }
52
53 ret = cqe->res;
54 if (ret < 0) {
55 if (cqe->res == -EINVAL || cqe->res == -EOPNOTSUPP)
56 no_xattr = 1;
57 }
58 io_uring_cqe_seen(ring, cqe);
59 return ret;
60 }
61
62 /* Submit fgetxattr request. */
io_uring_fgetxattr(struct io_uring * ring,int fd,const char * name,void * value,size_t size)63 static int io_uring_fgetxattr(struct io_uring *ring, int fd, const char *name,
64 void *value, size_t size)
65 {
66 struct io_uring_sqe *sqe;
67 struct io_uring_cqe *cqe;
68 int ret;
69
70 sqe = io_uring_get_sqe(ring);
71 if (!sqe) {
72 fprintf(stderr, "Error cannot get sqe\n");
73 return -1;
74 }
75
76 io_uring_prep_fgetxattr(sqe, fd, name, value, size);
77
78 ret = io_uring_submit(ring);
79 if (ret != 1) {
80 fprintf(stderr, "Error io_uring_submit_and_wait: ret=%d\n", ret);
81 return -1;
82 }
83
84 ret = io_uring_wait_cqe(ring, &cqe);
85 if (ret) {
86 fprintf(stderr, "Error io_uring_wait_cqe: ret=%d\n", ret);
87 return -1;
88 }
89
90 ret = cqe->res;
91 if (ret == -1) {
92 fprintf(stderr, "Error couldn'tget value\n");
93 return -1;
94 }
95
96 io_uring_cqe_seen(ring, cqe);
97 return ret;
98 }
99
100 /* Call setxattr. */
io_uring_setxattr(struct io_uring * ring,const char * path,const char * name,const void * value,size_t size,int flags)101 static int io_uring_setxattr(struct io_uring *ring, const char *path,
102 const char *name, const void *value, size_t size,
103 int flags)
104 {
105 struct io_uring_sqe *sqe;
106 struct io_uring_cqe *cqe;
107 int ret;
108
109 sqe = io_uring_get_sqe(ring);
110 if (!sqe) {
111 fprintf(stderr, "Error cannot get sqe\n");
112 return -1;
113 }
114
115 io_uring_prep_setxattr(sqe, name, value, path, flags, size);
116
117 ret = io_uring_submit_and_wait(ring, 1);
118 if (ret != 1) {
119 fprintf(stderr, "Error io_uring_submit_and_wait: ret=%d\n", ret);
120 return -1;
121 }
122
123 ret = io_uring_wait_cqe(ring, &cqe);
124 if (ret) {
125 fprintf(stderr, "Error io_uring_wait_cqe: ret=%d\n", ret);
126 return -1;
127 }
128
129 ret = cqe->res;
130 if (ret < 0) {
131 if (ret == -EINVAL || ret == -EOPNOTSUPP)
132 no_xattr = 1;
133 }
134 io_uring_cqe_seen(ring, cqe);
135 return ret;
136 }
137
138 /* Submit getxattr request. */
io_uring_getxattr(struct io_uring * ring,const char * path,const char * name,void * value,size_t size)139 static int io_uring_getxattr(struct io_uring *ring, const char *path,
140 const char *name, void *value, size_t size)
141 {
142 struct io_uring_sqe *sqe;
143 struct io_uring_cqe *cqe;
144 int ret;
145
146 sqe = io_uring_get_sqe(ring);
147 if (!sqe) {
148 fprintf(stderr, "Error cannot get sqe\n");
149 return -1;
150 }
151
152 io_uring_prep_getxattr(sqe, name, value, path, size);
153
154 ret = io_uring_submit(ring);
155 if (ret != 1) {
156 fprintf(stderr, "Error io_uring_submit_and_wait: ret=%d\n", ret);
157 return -1;
158 }
159
160 ret = io_uring_wait_cqe(ring, &cqe);
161 if (ret) {
162 fprintf(stderr, "Error io_uring_wait_cqe: ret=%d\n", ret);
163 return -1;
164 }
165
166 ret = cqe->res;
167 if (ret == -1) {
168 fprintf(stderr, "Error couldn'tget value\n");
169 return -1;
170 }
171
172 io_uring_cqe_seen(ring, cqe);
173 return ret;
174 }
175
176 /* Test driver for fsetxattr and fgetxattr. */
test_fxattr(void)177 static int test_fxattr(void)
178 {
179 int rc = 0;
180 size_t value_len;
181 struct io_uring ring;
182 char value[XATTR_SIZE];
183
184 /* Init io-uring queue. */
185 int ret = io_uring_queue_init(QUEUE_DEPTH, &ring, 0);
186 if (ret) {
187 fprintf(stderr, "child: ring setup failed: %d\n", ret);
188 return -1;
189 }
190
191 /* Create the test file. */
192 int fd = open(FILENAME, O_CREAT | O_RDWR, 0644);
193 if (fd < 0) {
194 fprintf(stderr, "Error: cannot open file: ret=%d\n", fd);
195 return -1;
196 }
197
198 /* Test writing attributes. */
199 if (io_uring_fsetxattr(&ring, fd, KEY1, VALUE1, strlen(VALUE1), 0) < 0) {
200 if (no_xattr) {
201 fprintf(stdout, "No xattr support, skipping\n");
202 goto Exit;
203 }
204 fprintf(stderr, "Error fsetxattr cannot write key1\n");
205 rc = -1;
206 goto Exit;
207 }
208
209 if (io_uring_fsetxattr(&ring, fd, KEY2, VALUE2, strlen(VALUE2), 0) < 0) {
210 fprintf(stderr, "Error fsetxattr cannot write key1\n");
211 rc = -1;
212 goto Exit;
213 }
214
215 /* Test reading attributes. */
216 value_len = io_uring_fgetxattr(&ring, fd, KEY1, value, XATTR_SIZE);
217 if (value_len != strlen(VALUE1) || strncmp(value, VALUE1, value_len)) {
218 fprintf(stderr, "Error: fgetxattr expected value: %s, returned value: %s\n", VALUE1, value);
219 rc = -1;
220 goto Exit;
221 }
222
223 value_len = io_uring_fgetxattr(&ring, fd, KEY2, value, XATTR_SIZE);
224 if (value_len != strlen(VALUE2) || strncmp(value, VALUE2, value_len)) {
225 fprintf(stderr, "Error: fgetxattr expected value: %s, returned value: %s\n", VALUE2, value);
226 rc = -1;
227 goto Exit;
228 }
229
230 /* Cleanup. */
231 Exit:
232 close(fd);
233 unlink(FILENAME);
234
235 io_uring_queue_exit(&ring);
236
237 return rc;
238 }
239
240 /* Test driver for setxattr and getxattr. */
test_xattr(void)241 static int test_xattr(void)
242 {
243 int rc = 0;
244 int value_len;
245 struct io_uring ring;
246 char value[XATTR_SIZE];
247
248 /* Init io-uring queue. */
249 int ret = io_uring_queue_init(QUEUE_DEPTH, &ring, 0);
250 if (ret) {
251 fprintf(stderr, "child: ring setup failed: %d\n", ret);
252 return -1;
253 }
254
255 /* Create the test file. */
256 t_create_file(FILENAME, 0);
257
258 /* Test writing attributes. */
259 if (io_uring_setxattr(&ring, FILENAME, KEY1, VALUE1, strlen(VALUE1), 0) < 0) {
260 fprintf(stderr, "Error setxattr cannot write key1\n");
261 rc = -1;
262 goto Exit;
263 }
264
265 if (io_uring_setxattr(&ring, FILENAME, KEY2, VALUE2, strlen(VALUE2), 0) < 0) {
266 fprintf(stderr, "Error setxattr cannot write key1\n");
267 rc = -1;
268 goto Exit;
269 }
270
271 /* Test reading attributes. */
272 value_len = io_uring_getxattr(&ring, FILENAME, KEY1, value, XATTR_SIZE);
273 if (value_len != strlen(VALUE1) || strncmp(value, VALUE1, value_len)) {
274 fprintf(stderr, "Error: getxattr expected value: %s, returned value: %s\n", VALUE1, value);
275 rc = -1;
276 goto Exit;
277 }
278
279 value_len = io_uring_getxattr(&ring, FILENAME, KEY2, value, XATTR_SIZE);
280 if (value_len != strlen(VALUE2) || strncmp(value, VALUE2, value_len)) {
281 fprintf(stderr, "Error: getxattr expected value: %s, returned value: %s\n", VALUE2, value);
282 rc = -1;
283 goto Exit;
284 }
285
286 /* Cleanup. */
287 Exit:
288 io_uring_queue_exit(&ring);
289 unlink(FILENAME);
290
291 return rc;
292 }
293
294 /* Test driver for failure cases of fsetxattr and fgetxattr. */
test_failure_fxattr(void)295 static int test_failure_fxattr(void)
296 {
297 struct io_uring ring;
298 char value[XATTR_SIZE];
299
300 /* Init io-uring queue. */
301 int ret = io_uring_queue_init(QUEUE_DEPTH, &ring, 0);
302 if (ret) {
303 fprintf(stderr, "child: ring setup failed: %d\n", ret);
304 return -1;
305 }
306
307 /* Create the test file. */
308 int fd = open(FILENAME, O_CREAT | O_RDWR, 0644);
309 if (fd < 0) {
310 fprintf(stderr, "Error: cannot open file: ret=%d\n", fd);
311 return -1;
312 }
313
314 /* Test writing attributes. */
315 if (io_uring_fsetxattr(&ring, -1, KEY1, VALUE1, strlen(VALUE1), 0) >= 0)
316 return 1;
317 if (io_uring_fsetxattr(&ring, fd, NULL, VALUE1, strlen(VALUE1), 0) >= 0)
318 return 1;
319 if (io_uring_fsetxattr(&ring, fd, KEY1, NULL, strlen(VALUE1), 0) >= 0)
320 return 1;
321 if (io_uring_fsetxattr(&ring, fd, KEY1, VALUE1, 0, 0) != 0)
322 return 1;
323 if (io_uring_fsetxattr(&ring, fd, KEY1, VALUE1, -1, 0) >= 0)
324 return 1;
325
326 /* Test reading attributes. */
327 if (io_uring_fgetxattr(&ring, -1, KEY1, value, XATTR_SIZE) >= 0)
328 return 1;
329 if (io_uring_fgetxattr(&ring, fd, NULL, value, XATTR_SIZE) >= 0)
330 return 1;
331 if (io_uring_fgetxattr(&ring, fd, KEY1, value, 0) != 0)
332 return 1;
333
334 /* Cleanup. */
335 close(fd);
336 unlink(FILENAME);
337 io_uring_queue_exit(&ring);
338 return 0;
339 }
340
341
342 /* Test driver for failure cases for setxattr and getxattr. */
test_failure_xattr(void)343 static int test_failure_xattr(void)
344 {
345 struct io_uring ring;
346 char value[XATTR_SIZE];
347
348 /* Init io-uring queue. */
349 int ret = io_uring_queue_init(QUEUE_DEPTH, &ring, 0);
350 if (ret) {
351 fprintf(stderr, "child: ring setup failed: %d\n", ret);
352 return -1;
353 }
354
355 /* Create the test file. */
356 t_create_file(FILENAME, 0);
357
358 /* Test writing attributes. */
359 if (io_uring_setxattr(&ring, "complete garbage", KEY1, VALUE1, strlen(VALUE1), 0) >= 0)
360 return 1;
361 if (io_uring_setxattr(&ring, NULL, KEY1, VALUE1, strlen(VALUE1), 0) >= 0)
362 return 1;
363 if (io_uring_setxattr(&ring, FILENAME, NULL, VALUE1, strlen(VALUE1), 0) >= 0)
364 return 1;
365 if (io_uring_setxattr(&ring, FILENAME, KEY1, NULL, strlen(VALUE1), 0) >= 0)
366 return 1;
367 if (io_uring_setxattr(&ring, FILENAME, KEY1, VALUE1, 0, 0) != 0)
368 return 1;
369
370 /* Test reading attributes. */
371 if (io_uring_getxattr(&ring, "complete garbage", KEY1, value, XATTR_SIZE) >= 0)
372 return 1;
373 if (io_uring_getxattr(&ring, NULL, KEY1, value, XATTR_SIZE) >= 0)
374 return 1;
375 if (io_uring_getxattr(&ring, FILENAME, NULL, value, XATTR_SIZE) >= 0)
376 return 1;
377 if (io_uring_getxattr(&ring, FILENAME, KEY1, NULL, XATTR_SIZE) != 0)
378 return 1;
379 if (io_uring_getxattr(&ring, FILENAME, KEY1, value, 0) != 0)
380 return 1;
381
382 /* Cleanup. */
383 io_uring_queue_exit(&ring);
384 unlink(FILENAME);
385 return 0;
386 }
387
388 /* Test for invalid SQE, this will cause a segmentation fault if enabled. */
test_invalid_sqe(void)389 static int test_invalid_sqe(void)
390 {
391 #ifdef DESTRUCTIVE_TEST
392 struct io_uring_sqe *sqe = NULL;
393 struct io_uring_cqe *cqe = NULL;
394 struct io_uring ring;
395
396 /* Init io-uring queue. */
397 int ret = io_uring_queue_init(QUEUE_DEPTH, &ring, 0);
398 if (ret) {
399 fprintf(stderr, "child: ring setup failed: %d\n", ret);
400 return -1;
401 }
402
403 /* Pass invalid SQE. */
404 io_uring_prep_setxattr(sqe, FILENAME, KEY1, VALUE1, strlen(VALUE1), 0);
405
406 ret = io_uring_submit(&ring);
407 if (ret != 1) {
408 fprintf(stderr, "Error io_uring_submit_and_wait: ret=%d\n", ret);
409 return -1;
410 }
411
412 ret = io_uring_wait_cqe(&ring, &cqe);
413 if (ret) {
414 fprintf(stderr, "Error io_uring_wait_cqe: ret=%d\n", ret);
415 return -1;
416 }
417
418 ret = cqe->res;
419 io_uring_cqe_seen(&ring, cqe);
420
421 return ret;
422 #else
423 return 0;
424 #endif
425 }
426
427 /* Test driver. */
main(int argc,char * argv[])428 int main(int argc, char *argv[])
429 {
430 if (argc > 1)
431 return 0;
432
433 if (test_fxattr())
434 return EXIT_FAILURE;
435 if (no_xattr)
436 return EXIT_SUCCESS;
437 if (test_xattr() || test_failure_fxattr() || test_failure_xattr() ||
438 test_invalid_sqe())
439 return EXIT_FAILURE;
440
441 return EXIT_SUCCESS;
442 }
443