Fix report_mount_progress never calling set_progress_header
[zfs.git] / tests / zfs-tests / cmd / xattrtest / xattrtest.c
blob8c4cb88955eac3f1c70e4114aadedc37d2d6ee44
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2016 Lawrence Livermore National Security, LLC.
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.
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>
40 #include <sys/xattr.h>
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>
47 #define ERROR(fmt, ...) \
48 fprintf(stderr, "xattrtest: %s:%d: %s: " fmt "\n", \
49 __FILE__, __LINE__, \
50 __func__, ## __VA_ARGS__);
52 static const char shortopts[] = "hvycdn:f:x:s:p:t:e:rRko:";
53 static const struct option longopts[] = {
54 { "help", no_argument, 0, 'h' },
55 { "verbose", no_argument, 0, 'v' },
56 { "verify", no_argument, 0, 'y' },
57 { "nth", required_argument, 0, 'n' },
58 { "files", required_argument, 0, 'f' },
59 { "xattrs", required_argument, 0, 'x' },
60 { "size", required_argument, 0, 's' },
61 { "path", required_argument, 0, 'p' },
62 { "synccaches", no_argument, 0, 'c' },
63 { "dropcaches", no_argument, 0, 'd' },
64 { "script", required_argument, 0, 't' },
65 { "seed", required_argument, 0, 'e' },
66 { "random", no_argument, 0, 'r' },
67 { "randomvalue", no_argument, 0, 'R' },
68 { "keep", no_argument, 0, 'k' },
69 { "only", required_argument, 0, 'o' },
70 { 0, 0, 0, 0 }
73 enum phases {
74 PHASE_ALL = 0,
75 PHASE_CREATE,
76 PHASE_SETXATTR,
77 PHASE_GETXATTR,
78 PHASE_UNLINK,
79 PHASE_INVAL
82 static int verbose = 0;
83 static int verify = 0;
84 static int synccaches = 0;
85 static int dropcaches = 0;
86 static int nth = 0;
87 static int files = 1000;
88 static int xattrs = 1;
89 static int size = 6;
90 static int size_is_random = 0;
91 static int value_is_random = 0;
92 static int keep_files = 0;
93 static int phase = PHASE_ALL;
94 static char path[PATH_MAX] = "/tmp/xattrtest";
95 static char script[PATH_MAX] = "/bin/true";
96 static char xattrbytes[XATTR_SIZE_MAX];
98 static int
99 usage(int argc, char **argv)
101 fprintf(stderr,
102 "usage: %s [-hvycdrRk] [-n <nth>] [-f <files>] [-x <xattrs>]\n"
103 " [-s <bytes>] [-p <path>] [-t <script> ] [-o <phase>]\n",
104 argv[0]);
106 fprintf(stderr,
107 " --help -h This help\n"
108 " --verbose -v Increase verbosity\n"
109 " --verify -y Verify xattr contents\n"
110 " --nth -n <nth> Print every nth file\n"
111 " --files -f <files> Set xattrs on N files\n"
112 " --xattrs -x <xattrs> Set N xattrs on each file\n"
113 " --size -s <bytes> Set N bytes per xattr\n"
114 " --path -p <path> Path to files\n"
115 " --synccaches -c Sync caches between phases\n"
116 " --dropcaches -d Drop caches between phases\n"
117 " --script -t <script> Exec script between phases\n"
118 " --seed -e <seed> Random seed value\n"
119 " --random -r Randomly sized xattrs [16-size]\n"
120 " --randomvalue -R Random xattr values\n"
121 " --keep -k Don't unlink files\n"
122 " --only -o <num> Only run phase N\n"
123 " 0=all, 1=create, 2=setxattr,\n"
124 " 3=getxattr, 4=unlink\n\n");
126 return (1);
129 static int
130 parse_args(int argc, char **argv)
132 long seed = time(NULL);
133 int c;
134 int rc = 0;
136 while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
137 switch (c) {
138 case 'h':
139 return (usage(argc, argv));
140 case 'v':
141 verbose++;
142 break;
143 case 'y':
144 verify = 1;
145 break;
146 case 'n':
147 nth = strtol(optarg, NULL, 0);
148 break;
149 case 'f':
150 files = strtol(optarg, NULL, 0);
151 break;
152 case 'x':
153 xattrs = strtol(optarg, NULL, 0);
154 break;
155 case 's':
156 size = strtol(optarg, NULL, 0);
157 if (size > XATTR_SIZE_MAX) {
158 fprintf(stderr, "Error: the -s value may not "
159 "be greater than %d\n", XATTR_SIZE_MAX);
160 rc = 1;
162 break;
163 case 'p':
164 strncpy(path, optarg, PATH_MAX);
165 path[PATH_MAX - 1] = '\0';
166 break;
167 case 'c':
168 synccaches = 1;
169 break;
170 case 'd':
171 dropcaches = 1;
172 break;
173 case 't':
174 strncpy(script, optarg, PATH_MAX);
175 script[PATH_MAX - 1] = '\0';
176 break;
177 case 'e':
178 seed = strtol(optarg, NULL, 0);
179 break;
180 case 'r':
181 size_is_random = 1;
182 break;
183 case 'R':
184 value_is_random = 1;
185 break;
186 case 'k':
187 keep_files = 1;
188 break;
189 case 'o':
190 phase = strtol(optarg, NULL, 0);
191 if (phase <= PHASE_ALL || phase >= PHASE_INVAL) {
192 fprintf(stderr, "Error: the -o value must be "
193 "greater than %d and less than %d\n",
194 PHASE_ALL, PHASE_INVAL);
195 rc = 1;
197 break;
198 default:
199 rc = 1;
200 break;
204 if (rc != 0)
205 return (rc);
207 srandom(seed);
209 if (verbose) {
210 fprintf(stdout, "verbose: %d\n", verbose);
211 fprintf(stdout, "verify: %d\n", verify);
212 fprintf(stdout, "nth: %d\n", nth);
213 fprintf(stdout, "files: %d\n", files);
214 fprintf(stdout, "xattrs: %d\n", xattrs);
215 fprintf(stdout, "size: %d\n", size);
216 fprintf(stdout, "path: %s\n", path);
217 fprintf(stdout, "synccaches: %d\n", synccaches);
218 fprintf(stdout, "dropcaches: %d\n", dropcaches);
219 fprintf(stdout, "script: %s\n", script);
220 fprintf(stdout, "seed: %ld\n", seed);
221 fprintf(stdout, "random size: %d\n", size_is_random);
222 fprintf(stdout, "random value: %d\n", value_is_random);
223 fprintf(stdout, "keep: %d\n", keep_files);
224 fprintf(stdout, "only: %d\n", phase);
225 fprintf(stdout, "%s", "\n");
228 return (rc);
231 static int
232 drop_caches(void)
234 char file[] = "/proc/sys/vm/drop_caches";
235 int fd, rc;
237 fd = open(file, O_WRONLY);
238 if (fd == -1) {
239 ERROR("Error %d: open(\"%s\", O_WRONLY)\n", errno, file);
240 return (errno);
243 rc = write(fd, "3", 1);
244 if ((rc == -1) || (rc != 1)) {
245 ERROR("Error %d: write(%d, \"3\", 1)\n", errno, fd);
246 (void) close(fd);
247 return (errno);
250 rc = close(fd);
251 if (rc == -1) {
252 ERROR("Error %d: close(%d)\n", errno, fd);
253 return (errno);
256 return (0);
259 static int
260 run_process(const char *path, char *argv[])
262 pid_t pid;
263 int rc, devnull_fd;
265 pid = vfork();
266 if (pid == 0) {
267 devnull_fd = open("/dev/null", O_WRONLY);
269 if (devnull_fd < 0)
270 _exit(-1);
272 (void) dup2(devnull_fd, STDOUT_FILENO);
273 (void) dup2(devnull_fd, STDERR_FILENO);
274 close(devnull_fd);
276 (void) execvp(path, argv);
277 _exit(-1);
278 } else if (pid > 0) {
279 int status;
281 while ((rc = waitpid(pid, &status, 0)) == -1 &&
282 errno == EINTR) { }
284 if (rc < 0 || !WIFEXITED(status))
285 return (-1);
287 return (WEXITSTATUS(status));
290 return (-1);
293 static int
294 post_hook(char *phase)
296 char *argv[3] = { script, phase, (char *)0 };
297 int rc;
299 if (synccaches)
300 sync();
302 if (dropcaches) {
303 rc = drop_caches();
304 if (rc)
305 return (rc);
308 rc = run_process(script, argv);
309 if (rc)
310 return (rc);
312 return (0);
315 #define USEC_PER_SEC 1000000
317 static void
318 timeval_normalize(struct timeval *tv, time_t sec, suseconds_t usec)
320 while (usec >= USEC_PER_SEC) {
321 usec -= USEC_PER_SEC;
322 sec++;
325 while (usec < 0) {
326 usec += USEC_PER_SEC;
327 sec--;
330 tv->tv_sec = sec;
331 tv->tv_usec = usec;
334 static void
335 timeval_sub(struct timeval *delta, struct timeval *tv1, struct timeval *tv2)
337 timeval_normalize(delta,
338 tv1->tv_sec - tv2->tv_sec,
339 tv1->tv_usec - tv2->tv_usec);
342 static double
343 timeval_sub_seconds(struct timeval *tv1, struct timeval *tv2)
345 struct timeval delta;
347 timeval_sub(&delta, tv1, tv2);
348 return ((double)delta.tv_usec / USEC_PER_SEC + delta.tv_sec);
351 static int
352 create_files(void)
354 int i, rc;
355 char *file = NULL;
356 struct timeval start, stop;
357 double seconds;
358 size_t fsize;
360 fsize = PATH_MAX;
361 file = malloc(fsize);
362 if (file == NULL) {
363 rc = ENOMEM;
364 ERROR("Error %d: malloc(%d) bytes for file name\n", rc,
365 PATH_MAX);
366 goto out;
369 (void) gettimeofday(&start, NULL);
371 for (i = 1; i <= files; i++) {
372 if (snprintf(file, fsize, "%s/file-%d", path, i) >= fsize) {
373 rc = EINVAL;
374 ERROR("Error %d: path too long\n", rc);
375 goto out;
378 if (nth && ((i % nth) == 0))
379 fprintf(stdout, "create: %s\n", file);
381 rc = unlink(file);
382 if ((rc == -1) && (errno != ENOENT)) {
383 ERROR("Error %d: unlink(%s)\n", errno, file);
384 rc = errno;
385 goto out;
388 rc = open(file, O_CREAT, 0644);
389 if (rc == -1) {
390 ERROR("Error %d: open(%s, O_CREATE, 0644)\n",
391 errno, file);
392 rc = errno;
393 goto out;
396 rc = close(rc);
397 if (rc == -1) {
398 ERROR("Error %d: close(%d)\n", errno, rc);
399 rc = errno;
400 goto out;
404 (void) gettimeofday(&stop, NULL);
405 seconds = timeval_sub_seconds(&stop, &start);
406 fprintf(stdout, "create: %f seconds %f creates/second\n",
407 seconds, files / seconds);
409 rc = post_hook("post");
410 out:
411 if (file)
412 free(file);
414 return (rc);
417 static int
418 get_random_bytes(char *buf, size_t bytes)
420 int rand;
421 ssize_t bytes_read = 0;
423 rand = open("/dev/urandom", O_RDONLY);
425 if (rand < 0)
426 return (rand);
428 while (bytes_read < bytes) {
429 ssize_t rc = read(rand, buf + bytes_read, bytes - bytes_read);
430 if (rc < 0)
431 break;
432 bytes_read += rc;
435 (void) close(rand);
437 return (bytes_read);
440 static int
441 setxattrs(void)
443 int i, j, rnd_size = size, shift, rc = 0;
444 char name[XATTR_NAME_MAX];
445 char *value = NULL;
446 char *file = NULL;
447 struct timeval start, stop;
448 double seconds;
449 size_t fsize;
451 value = malloc(XATTR_SIZE_MAX);
452 if (value == NULL) {
453 rc = ENOMEM;
454 ERROR("Error %d: malloc(%d) bytes for xattr value\n", rc,
455 XATTR_SIZE_MAX);
456 goto out;
459 fsize = PATH_MAX;
460 file = malloc(fsize);
461 if (file == NULL) {
462 rc = ENOMEM;
463 ERROR("Error %d: malloc(%d) bytes for file name\n", rc,
464 PATH_MAX);
465 goto out;
468 (void) gettimeofday(&start, NULL);
470 for (i = 1; i <= files; i++) {
471 if (snprintf(file, fsize, "%s/file-%d", path, i) >= fsize) {
472 rc = EINVAL;
473 ERROR("Error %d: path too long\n", rc);
474 goto out;
477 if (nth && ((i % nth) == 0))
478 fprintf(stdout, "setxattr: %s\n", file);
480 for (j = 1; j <= xattrs; j++) {
481 if (size_is_random)
482 rnd_size = (random() % (size - 16)) + 16;
484 (void) sprintf(name, "user.%d", j);
485 shift = sprintf(value, "size=%d ", rnd_size);
486 memcpy(value + shift, xattrbytes,
487 sizeof (xattrbytes) - shift);
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;
498 (void) gettimeofday(&stop, NULL);
499 seconds = timeval_sub_seconds(&stop, &start);
500 fprintf(stdout, "setxattr: %f seconds %f setxattrs/second\n",
501 seconds, (files * xattrs) / seconds);
503 rc = post_hook("post");
504 out:
505 if (file)
506 free(file);
508 if (value)
509 free(value);
511 return (rc);
514 static int
515 getxattrs(void)
517 int i, j, rnd_size, shift, rc = 0;
518 char name[XATTR_NAME_MAX];
519 char *verify_value = NULL;
520 char *verify_string;
521 char *value = NULL;
522 char *value_string;
523 char *file = NULL;
524 struct timeval start, stop;
525 double seconds;
526 size_t fsize;
528 verify_value = malloc(XATTR_SIZE_MAX);
529 if (verify_value == NULL) {
530 rc = ENOMEM;
531 ERROR("Error %d: malloc(%d) bytes for xattr verify\n", rc,
532 XATTR_SIZE_MAX);
533 goto out;
536 value = malloc(XATTR_SIZE_MAX);
537 if (value == NULL) {
538 rc = ENOMEM;
539 ERROR("Error %d: malloc(%d) bytes for xattr value\n", rc,
540 XATTR_SIZE_MAX);
541 goto out;
544 verify_string = value_is_random ? "<random>" : verify_value;
545 value_string = value_is_random ? "<random>" : value;
547 fsize = PATH_MAX;
548 file = malloc(fsize);
550 if (file == NULL) {
551 rc = ENOMEM;
552 ERROR("Error %d: malloc(%d) bytes for file name\n", rc,
553 PATH_MAX);
554 goto out;
557 (void) gettimeofday(&start, NULL);
559 for (i = 1; i <= files; i++) {
560 if (snprintf(file, fsize, "%s/file-%d", path, i) >= fsize) {
561 rc = EINVAL;
562 ERROR("Error %d: path too long\n", rc);
563 goto out;
566 if (nth && ((i % nth) == 0))
567 fprintf(stdout, "getxattr: %s\n", file);
569 for (j = 1; j <= xattrs; j++) {
570 (void) sprintf(name, "user.%d", j);
572 rc = lgetxattr(file, name, value, XATTR_SIZE_MAX);
573 if (rc == -1) {
574 ERROR("Error %d: lgetxattr(%s, %s, ..., %d)\n",
575 errno, file, name, XATTR_SIZE_MAX);
576 goto out;
579 if (!verify)
580 continue;
582 sscanf(value, "size=%d [a-z]", &rnd_size);
583 shift = sprintf(verify_value, "size=%d ",
584 rnd_size);
585 memcpy(verify_value + shift, xattrbytes,
586 sizeof (xattrbytes) - shift);
588 if (rnd_size != rc ||
589 memcmp(verify_value, value, rnd_size)) {
590 ERROR("Error %d: verify failed\n "
591 "verify: %s\n value: %s\n", EINVAL,
592 verify_string, value_string);
593 rc = 1;
594 goto out;
599 (void) gettimeofday(&stop, NULL);
600 seconds = timeval_sub_seconds(&stop, &start);
601 fprintf(stdout, "getxattr: %f seconds %f getxattrs/second\n",
602 seconds, (files * xattrs) / seconds);
604 rc = post_hook("post");
605 out:
606 if (file)
607 free(file);
609 if (value)
610 free(value);
612 if (verify_value)
613 free(verify_value);
615 return (rc);
618 static int
619 unlink_files(void)
621 int i, rc;
622 char *file = NULL;
623 struct timeval start, stop;
624 double seconds;
625 size_t fsize;
627 fsize = PATH_MAX;
628 file = malloc(fsize);
629 if (file == NULL) {
630 rc = ENOMEM;
631 ERROR("Error %d: malloc(%d) bytes for file name\n",
632 rc, PATH_MAX);
633 goto out;
636 (void) gettimeofday(&start, NULL);
638 for (i = 1; i <= files; i++) {
639 if (snprintf(file, fsize, "%s/file-%d", path, i) >= fsize) {
640 rc = EINVAL;
641 ERROR("Error %d: path too long\n", rc);
642 goto out;
645 if (nth && ((i % nth) == 0))
646 fprintf(stdout, "unlink: %s\n", file);
648 rc = unlink(file);
649 if ((rc == -1) && (errno != ENOENT)) {
650 ERROR("Error %d: unlink(%s)\n", errno, file);
651 free(file);
652 return (errno);
656 (void) gettimeofday(&stop, NULL);
657 seconds = timeval_sub_seconds(&stop, &start);
658 fprintf(stdout, "unlink: %f seconds %f unlinks/second\n",
659 seconds, files / seconds);
661 rc = post_hook("post");
662 out:
663 if (file)
664 free(file);
666 return (rc);
670 main(int argc, char **argv)
672 int rc;
674 rc = parse_args(argc, argv);
675 if (rc)
676 return (rc);
678 if (value_is_random) {
679 size_t rndsz = sizeof (xattrbytes);
681 rc = get_random_bytes(xattrbytes, rndsz);
682 if (rc < rndsz) {
683 ERROR("Error %d: get_random_bytes() wanted %zd "
684 "got %d\n", errno, rndsz, rc);
685 return (rc);
687 } else {
688 memset(xattrbytes, 'x', sizeof (xattrbytes));
691 if (phase == PHASE_ALL || phase == PHASE_CREATE) {
692 rc = create_files();
693 if (rc)
694 return (rc);
697 if (phase == PHASE_ALL || phase == PHASE_SETXATTR) {
698 rc = setxattrs();
699 if (rc)
700 return (rc);
703 if (phase == PHASE_ALL || phase == PHASE_GETXATTR) {
704 rc = getxattrs();
705 if (rc)
706 return (rc);
709 if (!keep_files && (phase == PHASE_ALL || phase == PHASE_UNLINK)) {
710 rc = unlink_files();
711 if (rc)
712 return (rc);
715 return (0);