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