]> git.proxmox.com Git - mirror_zfs.git/blame - tests/zfs-tests/cmd/xattrtest/xattrtest.c
Remove libattr requirement
[mirror_zfs.git] / tests / zfs-tests / cmd / xattrtest / xattrtest.c
CommitLineData
6bb24f4d
BB
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2016 Lawrence Livermore National Security, LLC.
24 */
25
26/*
27 * An extended attribute (xattr) correctness test. This program creates
28 * N files and sets M attrs on them of size S. Optionally is will verify
29 * a pattern stored in the xattr.
30 */
31#include <stdlib.h>
32#include <stddef.h>
33#include <stdio.h>
34#include <string.h>
35#include <errno.h>
36#include <getopt.h>
37#include <fcntl.h>
38#include <time.h>
39#include <unistd.h>
668173b5 40#include <sys/xattr.h>
6bb24f4d
BB
41#include <sys/types.h>
42#include <sys/wait.h>
43#include <sys/stat.h>
44#include <sys/time.h>
45#include <linux/limits.h>
46
47extern char *program_invocation_short_name;
48
49#define ERROR(fmt, ...) \
50 fprintf(stderr, "%s: %s:%d: %s: " fmt "\n", \
51 program_invocation_short_name, __FILE__, __LINE__, \
52 __func__, ## __VA_ARGS__);
53
81285582 54static const char shortopts[] = "hvycdn:f:x:s:p:t:e:rRko:";
6bb24f4d
BB
55static const struct option longopts[] = {
56 { "help", no_argument, 0, 'h' },
57 { "verbose", no_argument, 0, 'v' },
58 { "verify", no_argument, 0, 'y' },
59 { "nth", required_argument, 0, 'n' },
60 { "files", required_argument, 0, 'f' },
61 { "xattrs", required_argument, 0, 'x' },
62 { "size", required_argument, 0, 's' },
63 { "path", required_argument, 0, 'p' },
64 { "synccaches", no_argument, 0, 'c' },
65 { "dropcaches", no_argument, 0, 'd' },
66 { "script", required_argument, 0, 't' },
67 { "seed", required_argument, 0, 'e' },
68 { "random", no_argument, 0, 'r' },
69 { "randomvalue", no_argument, 0, 'R' },
70 { "keep", no_argument, 0, 'k' },
81285582 71 { "only", required_argument, 0, 'o' },
6bb24f4d
BB
72 { 0, 0, 0, 0 }
73};
74
81285582
NB
75enum phases {
76 PHASE_ALL = 0,
77 PHASE_CREATE,
78 PHASE_SETXATTR,
79 PHASE_GETXATTR,
80 PHASE_UNLINK,
81 PHASE_INVAL
82};
83
6bb24f4d
BB
84static int verbose = 0;
85static int verify = 0;
86static int synccaches = 0;
87static int dropcaches = 0;
88static int nth = 0;
89static int files = 1000;
90static int xattrs = 1;
81285582 91static int size = 6;
6bb24f4d
BB
92static int size_is_random = 0;
93static int value_is_random = 0;
94static int keep_files = 0;
81285582 95static int phase = PHASE_ALL;
6bb24f4d
BB
96static char path[PATH_MAX] = "/tmp/xattrtest";
97static char script[PATH_MAX] = "/bin/true";
81285582 98static char xattrbytes[XATTR_SIZE_MAX];
6bb24f4d
BB
99
100static int
4ea3f864
GM
101usage(int argc, char **argv)
102{
6bb24f4d 103 fprintf(stderr,
4ea3f864
GM
104 "usage: %s [-hvycdrRk] [-n <nth>] [-f <files>] [-x <xattrs>]\n"
105 " [-s <bytes>] [-p <path>] [-t <script> ] [-o <phase>]\n",
106 argv[0]);
81285582 107
6bb24f4d 108 fprintf(stderr,
4ea3f864
GM
109 " --help -h This help\n"
110 " --verbose -v Increase verbosity\n"
111 " --verify -y Verify xattr contents\n"
112 " --nth -n <nth> Print every nth file\n"
113 " --files -f <files> Set xattrs on N files\n"
114 " --xattrs -x <xattrs> Set N xattrs on each file\n"
115 " --size -s <bytes> Set N bytes per xattr\n"
116 " --path -p <path> Path to files\n"
117 " --synccaches -c Sync caches between phases\n"
118 " --dropcaches -d Drop caches between phases\n"
119 " --script -t <script> Exec script between phases\n"
120 " --seed -e <seed> Random seed value\n"
121 " --random -r Randomly sized xattrs [16-size]\n"
122 " --randomvalue -R Random xattr values\n"
123 " --keep -k Don't unlink files\n"
124 " --only -o <num> Only run phase N\n"
125 " 0=all, 1=create, 2=setxattr,\n"
126 " 3=getxattr, 4=unlink\n\n");
6bb24f4d 127
81285582 128 return (1);
6bb24f4d
BB
129}
130
131static int
132parse_args(int argc, char **argv)
133{
134 long seed = time(NULL);
135 int c;
81285582 136 int rc = 0;
6bb24f4d
BB
137
138 while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
139 switch (c) {
140 case 'h':
141 return (usage(argc, argv));
142 case 'v':
143 verbose++;
144 break;
145 case 'y':
146 verify = 1;
81285582 147 if (phase != PHASE_ALL) {
6bb24f4d 148 fprintf(stderr,
81285582 149 "Error: -y and -o are incompatible.\n");
6bb24f4d
BB
150 rc = 1;
151 }
152 break;
153 case 'n':
154 nth = strtol(optarg, NULL, 0);
155 break;
156 case 'f':
157 files = strtol(optarg, NULL, 0);
158 break;
159 case 'x':
160 xattrs = strtol(optarg, NULL, 0);
161 break;
162 case 's':
163 size = strtol(optarg, NULL, 0);
164 if (size > XATTR_SIZE_MAX) {
81285582
NB
165 fprintf(stderr, "Error: the -s value may not "
166 "be greater than %d\n", XATTR_SIZE_MAX);
6bb24f4d
BB
167 rc = 1;
168 }
169 break;
170 case 'p':
171 strncpy(path, optarg, PATH_MAX);
1b81ab46 172 path[PATH_MAX - 1] = '\0';
6bb24f4d
BB
173 break;
174 case 'c':
175 synccaches = 1;
176 break;
177 case 'd':
178 dropcaches = 1;
179 break;
180 case 't':
181 strncpy(script, optarg, PATH_MAX);
1b81ab46 182 script[PATH_MAX - 1] = '\0';
6bb24f4d
BB
183 break;
184 case 'e':
185 seed = strtol(optarg, NULL, 0);
186 break;
187 case 'r':
188 size_is_random = 1;
189 break;
190 case 'R':
191 value_is_random = 1;
6bb24f4d
BB
192 break;
193 case 'k':
194 keep_files = 1;
195 break;
81285582
NB
196 case 'o':
197 phase = strtol(optarg, NULL, 0);
198 if (phase <= PHASE_ALL || phase >= PHASE_INVAL) {
199 fprintf(stderr, "Error: the -o value must be "
200 "greater than %d and less than %d\n",
201 PHASE_ALL, PHASE_INVAL);
202 rc = 1;
203 }
204 if (verify == 1) {
205 fprintf(stderr,
206 "Error: -y and -o are incompatible.\n");
207 rc = 1;
208 }
209 break;
6bb24f4d
BB
210 default:
211 rc = 1;
212 break;
213 }
214 }
215
216 if (rc != 0)
217 return (rc);
218
219 srandom(seed);
220
221 if (verbose) {
222 fprintf(stdout, "verbose: %d\n", verbose);
223 fprintf(stdout, "verify: %d\n", verify);
224 fprintf(stdout, "nth: %d\n", nth);
225 fprintf(stdout, "files: %d\n", files);
226 fprintf(stdout, "xattrs: %d\n", xattrs);
227 fprintf(stdout, "size: %d\n", size);
228 fprintf(stdout, "path: %s\n", path);
229 fprintf(stdout, "synccaches: %d\n", synccaches);
230 fprintf(stdout, "dropcaches: %d\n", dropcaches);
231 fprintf(stdout, "script: %s\n", script);
232 fprintf(stdout, "seed: %ld\n", seed);
233 fprintf(stdout, "random size: %d\n", size_is_random);
234 fprintf(stdout, "random value: %d\n", value_is_random);
235 fprintf(stdout, "keep: %d\n", keep_files);
81285582 236 fprintf(stdout, "only: %d\n", phase);
6bb24f4d
BB
237 fprintf(stdout, "%s", "\n");
238 }
239
240 return (rc);
241}
242
243static int
244drop_caches(void)
245{
246 char file[] = "/proc/sys/vm/drop_caches";
247 int fd, rc;
248
249 fd = open(file, O_WRONLY);
250 if (fd == -1) {
251 ERROR("Error %d: open(\"%s\", O_WRONLY)\n", errno, file);
252 return (errno);
253 }
254
255 rc = write(fd, "3", 1);
256 if ((rc == -1) || (rc != 1)) {
257 ERROR("Error %d: write(%d, \"3\", 1)\n", errno, fd);
d0662a1b 258 (void) close(fd);
6bb24f4d
BB
259 return (errno);
260 }
261
262 rc = close(fd);
263 if (rc == -1) {
264 ERROR("Error %d: close(%d)\n", errno, fd);
265 return (errno);
266 }
267
268 return (0);
269}
270
271static int
272run_process(const char *path, char *argv[])
273{
274 pid_t pid;
275 int rc, devnull_fd;
276
277 pid = vfork();
278 if (pid == 0) {
279 devnull_fd = open("/dev/null", O_WRONLY);
280
281 if (devnull_fd < 0)
282 _exit(-1);
283
284 (void) dup2(devnull_fd, STDOUT_FILENO);
285 (void) dup2(devnull_fd, STDERR_FILENO);
286 close(devnull_fd);
287
288 (void) execvp(path, argv);
289 _exit(-1);
290 } else if (pid > 0) {
291 int status;
292
02730c33
BB
293 while ((rc = waitpid(pid, &status, 0)) == -1 &&
294 errno == EINTR) { }
6bb24f4d
BB
295
296 if (rc < 0 || !WIFEXITED(status))
297 return (-1);
298
299 return (WEXITSTATUS(status));
300 }
301
302 return (-1);
303}
304
305static int
306post_hook(char *phase)
307{
308 char *argv[3] = { script, phase, (char *)0 };
309 int rc;
310
311 if (synccaches)
312 sync();
313
314 if (dropcaches) {
315 rc = drop_caches();
316 if (rc)
317 return (rc);
318 }
319
320 rc = run_process(script, argv);
321 if (rc)
322 return (rc);
323
324 return (0);
325}
326
327#define USEC_PER_SEC 1000000
328
329static void
330timeval_normalize(struct timeval *tv, time_t sec, suseconds_t usec)
331{
332 while (usec >= USEC_PER_SEC) {
333 usec -= USEC_PER_SEC;
334 sec++;
335 }
336
337 while (usec < 0) {
338 usec += USEC_PER_SEC;
339 sec--;
340 }
341
342 tv->tv_sec = sec;
343 tv->tv_usec = usec;
344}
345
346static void
347timeval_sub(struct timeval *delta, struct timeval *tv1, struct timeval *tv2)
348{
349 timeval_normalize(delta,
350 tv1->tv_sec - tv2->tv_sec,
351 tv1->tv_usec - tv2->tv_usec);
352}
353
81285582
NB
354static double
355timeval_sub_seconds(struct timeval *tv1, struct timeval *tv2)
356{
357 struct timeval delta;
358
359 timeval_sub(&delta, tv1, tv2);
360 return ((double)delta.tv_usec / USEC_PER_SEC + delta.tv_sec);
361}
362
6bb24f4d
BB
363static int
364create_files(void)
365{
366 int i, rc;
367 char *file = NULL;
81285582
NB
368 struct timeval start, stop;
369 double seconds;
6bb24f4d
BB
370
371 file = malloc(PATH_MAX);
372 if (file == NULL) {
373 rc = ENOMEM;
02730c33
BB
374 ERROR("Error %d: malloc(%d) bytes for file name\n", rc,
375 PATH_MAX);
6bb24f4d
BB
376 goto out;
377 }
378
379 (void) gettimeofday(&start, NULL);
380
381 for (i = 1; i <= files; i++) {
382 (void) sprintf(file, "%s/file-%d", path, i);
383
384 if (nth && ((i % nth) == 0))
385 fprintf(stdout, "create: %s\n", file);
386
387 rc = unlink(file);
388 if ((rc == -1) && (errno != ENOENT)) {
389 ERROR("Error %d: unlink(%s)\n", errno, file);
390 rc = errno;
391 goto out;
392 }
393
394 rc = open(file, O_CREAT, 0644);
395 if (rc == -1) {
396 ERROR("Error %d: open(%s, O_CREATE, 0644)\n",
02730c33 397 errno, file);
6bb24f4d
BB
398 rc = errno;
399 goto out;
400 }
401
402 rc = close(rc);
403 if (rc == -1) {
404 ERROR("Error %d: close(%d)\n", errno, rc);
405 rc = errno;
406 goto out;
407 }
408 }
409
410 (void) gettimeofday(&stop, NULL);
81285582
NB
411 seconds = timeval_sub_seconds(&stop, &start);
412 fprintf(stdout, "create: %f seconds %f creates/second\n",
413 seconds, files / seconds);
6bb24f4d
BB
414
415 rc = post_hook("post");
416out:
417 if (file)
418 free(file);
419
420 return (rc);
421}
422
423static int
424get_random_bytes(char *buf, size_t bytes)
425{
426 int rand;
427 ssize_t bytes_read = 0;
428
429 rand = open("/dev/urandom", O_RDONLY);
430
431 if (rand < 0)
432 return (rand);
433
434 while (bytes_read < bytes) {
435 ssize_t rc = read(rand, buf + bytes_read, bytes - bytes_read);
436 if (rc < 0)
437 break;
438 bytes_read += rc;
439 }
440
441 (void) close(rand);
442
443 return (bytes_read);
444}
445
446static int
447setxattrs(void)
448{
449 int i, j, rnd_size = size, shift, rc = 0;
450 char name[XATTR_NAME_MAX];
451 char *value = NULL;
452 char *file = NULL;
81285582
NB
453 struct timeval start, stop;
454 double seconds;
6bb24f4d
BB
455
456 value = malloc(XATTR_SIZE_MAX);
457 if (value == NULL) {
458 rc = ENOMEM;
02730c33
BB
459 ERROR("Error %d: malloc(%d) bytes for xattr value\n", rc,
460 XATTR_SIZE_MAX);
6bb24f4d
BB
461 goto out;
462 }
463
464 file = malloc(PATH_MAX);
465 if (file == NULL) {
466 rc = ENOMEM;
02730c33
BB
467 ERROR("Error %d: malloc(%d) bytes for file name\n", rc,
468 PATH_MAX);
6bb24f4d
BB
469 goto out;
470 }
471
472 (void) gettimeofday(&start, NULL);
473
474 for (i = 1; i <= files; i++) {
475 (void) sprintf(file, "%s/file-%d", path, i);
476
477 if (nth && ((i % nth) == 0))
478 fprintf(stdout, "setxattr: %s\n", file);
479
480 for (j = 1; j <= xattrs; j++) {
481 if (size_is_random)
482 rnd_size = (random() % (size - 16)) + 16;
483
484 (void) sprintf(name, "user.%d", j);
81285582
NB
485 shift = sprintf(value, "size=%d ", rnd_size);
486 memcpy(value + shift, xattrbytes,
487 sizeof (xattrbytes) - shift);
6bb24f4d
BB
488
489 rc = lsetxattr(file, name, value, rnd_size, 0);
490 if (rc == -1) {
491 ERROR("Error %d: lsetxattr(%s, %s, ..., %d)\n",
492 errno, file, name, rnd_size);
493 goto out;
494 }
495 }
496 }
497
498 (void) gettimeofday(&stop, NULL);
81285582
NB
499 seconds = timeval_sub_seconds(&stop, &start);
500 fprintf(stdout, "setxattr: %f seconds %f setxattrs/second\n",
501 seconds, (files * xattrs) / seconds);
6bb24f4d
BB
502
503 rc = post_hook("post");
504out:
505 if (file)
506 free(file);
507
508 if (value)
509 free(value);
510
511 return (rc);
512}
513
514static int
515getxattrs(void)
516{
517 int i, j, rnd_size, shift, rc = 0;
518 char name[XATTR_NAME_MAX];
519 char *verify_value = NULL;
81285582 520 char *verify_string;
6bb24f4d 521 char *value = NULL;
81285582 522 char *value_string;
6bb24f4d 523 char *file = NULL;
81285582
NB
524 struct timeval start, stop;
525 double seconds;
6bb24f4d
BB
526
527 verify_value = malloc(XATTR_SIZE_MAX);
528 if (verify_value == NULL) {
529 rc = ENOMEM;
02730c33
BB
530 ERROR("Error %d: malloc(%d) bytes for xattr verify\n", rc,
531 XATTR_SIZE_MAX);
6bb24f4d
BB
532 goto out;
533 }
534
535 value = malloc(XATTR_SIZE_MAX);
536 if (value == NULL) {
537 rc = ENOMEM;
02730c33
BB
538 ERROR("Error %d: malloc(%d) bytes for xattr value\n", rc,
539 XATTR_SIZE_MAX);
6bb24f4d
BB
540 goto out;
541 }
542
81285582
NB
543 verify_string = value_is_random ? "<random>" : verify_value;
544 value_string = value_is_random ? "<random>" : value;
545
6bb24f4d
BB
546 file = malloc(PATH_MAX);
547 if (file == NULL) {
548 rc = ENOMEM;
02730c33
BB
549 ERROR("Error %d: malloc(%d) bytes for file name\n", rc,
550 PATH_MAX);
6bb24f4d
BB
551 goto out;
552 }
553
554 (void) gettimeofday(&start, NULL);
555
556 for (i = 1; i <= files; i++) {
557 (void) sprintf(file, "%s/file-%d", path, i);
558
559 if (nth && ((i % nth) == 0))
560 fprintf(stdout, "getxattr: %s\n", file);
561
562 for (j = 1; j <= xattrs; j++) {
563 (void) sprintf(name, "user.%d", j);
564
565 rc = lgetxattr(file, name, value, XATTR_SIZE_MAX);
566 if (rc == -1) {
567 ERROR("Error %d: lgetxattr(%s, %s, ..., %d)\n",
568 errno, file, name, XATTR_SIZE_MAX);
569 goto out;
570 }
571
81285582
NB
572 if (!verify)
573 continue;
574
575 sscanf(value, "size=%d [a-z]", &rnd_size);
576 shift = sprintf(verify_value, "size=%d ",
577 rnd_size);
578 memcpy(verify_value + shift, xattrbytes,
579 sizeof (xattrbytes) - shift);
580
581 if (rnd_size != rc ||
582 memcmp(verify_value, value, rnd_size)) {
583 ERROR("Error %d: verify failed\n "
584 "verify: %s\n value: %s\n", EINVAL,
585 verify_string, value_string);
586 rc = 1;
587 goto out;
6bb24f4d
BB
588 }
589 }
590 }
591
592 (void) gettimeofday(&stop, NULL);
81285582
NB
593 seconds = timeval_sub_seconds(&stop, &start);
594 fprintf(stdout, "getxattr: %f seconds %f getxattrs/second\n",
595 seconds, (files * xattrs) / seconds);
6bb24f4d
BB
596
597 rc = post_hook("post");
598out:
599 if (file)
600 free(file);
601
602 if (value)
603 free(value);
604
605 if (verify_value)
606 free(verify_value);
607
608 return (rc);
609}
610
611static int
612unlink_files(void)
613{
614 int i, rc;
615 char *file = NULL;
81285582
NB
616 struct timeval start, stop;
617 double seconds;
6bb24f4d
BB
618
619 file = malloc(PATH_MAX);
620 if (file == NULL) {
621 rc = ENOMEM;
622 ERROR("Error %d: malloc(%d) bytes for file name\n",
623 rc, PATH_MAX);
624 goto out;
625 }
626
627 (void) gettimeofday(&start, NULL);
628
629 for (i = 1; i <= files; i++) {
630 (void) sprintf(file, "%s/file-%d", path, i);
631
632 if (nth && ((i % nth) == 0))
633 fprintf(stdout, "unlink: %s\n", file);
634
635 rc = unlink(file);
636 if ((rc == -1) && (errno != ENOENT)) {
637 ERROR("Error %d: unlink(%s)\n", errno, file);
d0662a1b 638 free(file);
6bb24f4d
BB
639 return (errno);
640 }
641 }
642
643 (void) gettimeofday(&stop, NULL);
81285582
NB
644 seconds = timeval_sub_seconds(&stop, &start);
645 fprintf(stdout, "unlink: %f seconds %f unlinks/second\n",
646 seconds, files / seconds);
6bb24f4d
BB
647
648 rc = post_hook("post");
649out:
650 if (file)
651 free(file);
652
653 return (rc);
654}
655
656int
657main(int argc, char **argv)
658{
659 int rc;
660
661 rc = parse_args(argc, argv);
662 if (rc)
663 return (rc);
664
81285582
NB
665 if (value_is_random) {
666 size_t rndsz = sizeof (xattrbytes);
6bb24f4d 667
81285582
NB
668 rc = get_random_bytes(xattrbytes, rndsz);
669 if (rc < rndsz) {
670 ERROR("Error %d: get_random_bytes() wanted %zd "
671 "got %d\n", errno, rndsz, rc);
672 return (rc);
673 }
674 } else {
675 memset(xattrbytes, 'x', sizeof (xattrbytes));
676 }
6bb24f4d 677
81285582
NB
678 if (phase == PHASE_ALL || phase == PHASE_CREATE) {
679 rc = create_files();
680 if (rc)
681 return (rc);
682 }
683
684 if (phase == PHASE_ALL || phase == PHASE_SETXATTR) {
685 rc = setxattrs();
686 if (rc)
687 return (rc);
688 }
689
690 if (phase == PHASE_ALL || phase == PHASE_GETXATTR) {
691 rc = getxattrs();
692 if (rc)
693 return (rc);
694 }
6bb24f4d 695
81285582 696 if (!keep_files && (phase == PHASE_ALL || phase == PHASE_UNLINK)) {
6bb24f4d
BB
697 rc = unlink_files();
698 if (rc)
699 return (rc);
700 }
701
702 return (0);
703}