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 https://opensource.org/licenses/CDDL-1.0.
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]
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.
40 #include <sys/xattr.h>
41 #include <sys/types.h>
45 #include <linux/limits.h>
47 #define ERROR(fmt, ...) \
48 fprintf(stderr, "xattrtest: %s:%d: %s: " fmt "\n", \
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' },
82 static int verbose
= 0;
83 static int verify
= 0;
84 static int synccaches
= 0;
85 static int dropcaches
= 0;
87 static int files
= 1000;
88 static int xattrs
= 1;
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 const char *path
= "/tmp/xattrtest";
95 static const char *script
= "/bin/true";
96 static char xattrbytes
[XATTR_SIZE_MAX
];
102 "usage: %s [-hvycdrRk] [-n <nth>] [-f <files>] [-x <xattrs>]\n"
103 " [-s <bytes>] [-p <path>] [-t <script> ] [-o <phase>]\n",
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");
130 parse_args(int argc
, char **argv
)
132 long seed
= time(NULL
);
136 while ((c
= getopt_long(argc
, argv
, shortopts
, longopts
, NULL
)) != -1) {
139 return (usage(argv
[0]));
147 nth
= strtol(optarg
, NULL
, 0);
150 files
= strtol(optarg
, NULL
, 0);
153 xattrs
= strtol(optarg
, NULL
, 0);
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
);
176 seed
= strtol(optarg
, NULL
, 0);
188 phase
= strtol(optarg
, NULL
, 0);
189 if (phase
<= PHASE_ALL
|| phase
>= PHASE_INVAL
) {
190 fprintf(stderr
, "Error: the -o value must be "
191 "greater than %d and less than %d\n",
192 PHASE_ALL
, PHASE_INVAL
);
208 fprintf(stdout
, "verbose: %d\n", verbose
);
209 fprintf(stdout
, "verify: %d\n", verify
);
210 fprintf(stdout
, "nth: %d\n", nth
);
211 fprintf(stdout
, "files: %d\n", files
);
212 fprintf(stdout
, "xattrs: %d\n", xattrs
);
213 fprintf(stdout
, "size: %d\n", size
);
214 fprintf(stdout
, "path: %s\n", path
);
215 fprintf(stdout
, "synccaches: %d\n", synccaches
);
216 fprintf(stdout
, "dropcaches: %d\n", dropcaches
);
217 fprintf(stdout
, "script: %s\n", script
);
218 fprintf(stdout
, "seed: %ld\n", seed
);
219 fprintf(stdout
, "random size: %d\n", size_is_random
);
220 fprintf(stdout
, "random value: %d\n", value_is_random
);
221 fprintf(stdout
, "keep: %d\n", keep_files
);
222 fprintf(stdout
, "only: %d\n", phase
);
223 fprintf(stdout
, "%s", "\n");
232 char file
[] = "/proc/sys/vm/drop_caches";
235 fd
= open(file
, O_WRONLY
);
237 ERROR("Error %d: open(\"%s\", O_WRONLY)\n", errno
, file
);
241 rc
= write(fd
, "3", 1);
242 if ((rc
== -1) || (rc
!= 1)) {
243 ERROR("Error %d: write(%d, \"3\", 1)\n", errno
, fd
);
250 ERROR("Error %d: close(%d)\n", errno
, fd
);
258 run_process(const char *path
, char *argv
[])
265 devnull_fd
= open("/dev/null", O_WRONLY
);
270 (void) dup2(devnull_fd
, STDOUT_FILENO
);
271 (void) dup2(devnull_fd
, STDERR_FILENO
);
274 (void) execvp(path
, argv
);
276 } else if (pid
> 0) {
279 while ((rc
= waitpid(pid
, &status
, 0)) == -1 &&
282 if (rc
< 0 || !WIFEXITED(status
))
285 return (WEXITSTATUS(status
));
292 post_hook(const char *phase
)
294 char *argv
[3] = { (char *)script
, (char *)phase
, NULL
};
306 rc
= run_process(script
, argv
);
313 #define USEC_PER_SEC 1000000
316 timeval_normalize(struct timeval
*tv
, time_t sec
, suseconds_t usec
)
318 while (usec
>= USEC_PER_SEC
) {
319 usec
-= USEC_PER_SEC
;
324 usec
+= USEC_PER_SEC
;
333 timeval_sub(struct timeval
*delta
, struct timeval
*tv1
, struct timeval
*tv2
)
335 timeval_normalize(delta
,
336 tv1
->tv_sec
- tv2
->tv_sec
,
337 tv1
->tv_usec
- tv2
->tv_usec
);
341 timeval_sub_seconds(struct timeval
*tv1
, struct timeval
*tv2
)
343 struct timeval delta
;
345 timeval_sub(&delta
, tv1
, tv2
);
346 return ((double)delta
.tv_usec
/ USEC_PER_SEC
+ delta
.tv_sec
);
354 struct timeval start
, stop
;
359 file
= malloc(fsize
);
362 ERROR("Error %d: malloc(%d) bytes for file name\n", rc
,
367 (void) gettimeofday(&start
, NULL
);
369 for (i
= 1; i
<= files
; i
++) {
370 if (snprintf(file
, fsize
, "%s/file-%d", path
, i
) >= fsize
) {
372 ERROR("Error %d: path too long\n", rc
);
376 if (nth
&& ((i
% nth
) == 0))
377 fprintf(stdout
, "create: %s\n", file
);
380 if ((rc
== -1) && (errno
!= ENOENT
)) {
381 ERROR("Error %d: unlink(%s)\n", errno
, file
);
386 rc
= open(file
, O_CREAT
, 0644);
388 ERROR("Error %d: open(%s, O_CREATE, 0644)\n",
396 ERROR("Error %d: close(%d)\n", errno
, rc
);
402 (void) gettimeofday(&stop
, NULL
);
403 seconds
= timeval_sub_seconds(&stop
, &start
);
404 fprintf(stdout
, "create: %f seconds %f creates/second\n",
405 seconds
, files
/ seconds
);
407 rc
= post_hook("post");
416 get_random_bytes(char *buf
, size_t bytes
)
419 ssize_t bytes_read
= 0;
421 rand
= open("/dev/urandom", O_RDONLY
);
426 while (bytes_read
< bytes
) {
427 ssize_t rc
= read(rand
, buf
+ bytes_read
, bytes
- bytes_read
);
441 int i
, j
, rnd_size
= size
, shift
, rc
= 0;
442 char name
[XATTR_NAME_MAX
];
445 struct timeval start
, stop
;
449 value
= malloc(XATTR_SIZE_MAX
);
452 ERROR("Error %d: malloc(%d) bytes for xattr value\n", rc
,
458 file
= malloc(fsize
);
461 ERROR("Error %d: malloc(%d) bytes for file name\n", rc
,
466 (void) gettimeofday(&start
, NULL
);
468 for (i
= 1; i
<= files
; i
++) {
469 if (snprintf(file
, fsize
, "%s/file-%d", path
, i
) >= fsize
) {
471 ERROR("Error %d: path too long\n", rc
);
475 if (nth
&& ((i
% nth
) == 0))
476 fprintf(stdout
, "setxattr: %s\n", file
);
478 for (j
= 1; j
<= xattrs
; j
++) {
480 rnd_size
= (random() % (size
- 16)) + 16;
482 (void) sprintf(name
, "user.%d", j
);
483 shift
= sprintf(value
, "size=%d ", rnd_size
);
484 memcpy(value
+ shift
, xattrbytes
,
485 sizeof (xattrbytes
) - shift
);
487 rc
= lsetxattr(file
, name
, value
, rnd_size
, 0);
489 ERROR("Error %d: lsetxattr(%s, %s, ..., %d)\n",
490 errno
, file
, name
, rnd_size
);
496 (void) gettimeofday(&stop
, NULL
);
497 seconds
= timeval_sub_seconds(&stop
, &start
);
498 fprintf(stdout
, "setxattr: %f seconds %f setxattrs/second\n",
499 seconds
, (files
* xattrs
) / seconds
);
501 rc
= post_hook("post");
515 int i
, j
, rnd_size
, shift
, rc
= 0;
516 char name
[XATTR_NAME_MAX
];
517 char *verify_value
= NULL
;
518 const char *verify_string
;
520 const char *value_string
;
522 struct timeval start
, stop
;
526 verify_value
= malloc(XATTR_SIZE_MAX
);
527 if (verify_value
== NULL
) {
529 ERROR("Error %d: malloc(%d) bytes for xattr verify\n", rc
,
534 value
= malloc(XATTR_SIZE_MAX
);
537 ERROR("Error %d: malloc(%d) bytes for xattr value\n", rc
,
542 verify_string
= value_is_random
? "<random>" : verify_value
;
543 value_string
= value_is_random
? "<random>" : value
;
546 file
= malloc(fsize
);
550 ERROR("Error %d: malloc(%d) bytes for file name\n", rc
,
555 (void) gettimeofday(&start
, NULL
);
557 for (i
= 1; i
<= files
; i
++) {
558 if (snprintf(file
, fsize
, "%s/file-%d", path
, i
) >= fsize
) {
560 ERROR("Error %d: path too long\n", rc
);
564 if (nth
&& ((i
% nth
) == 0))
565 fprintf(stdout
, "getxattr: %s\n", file
);
567 for (j
= 1; j
<= xattrs
; j
++) {
568 (void) sprintf(name
, "user.%d", j
);
570 rc
= lgetxattr(file
, name
, value
, XATTR_SIZE_MAX
);
572 ERROR("Error %d: lgetxattr(%s, %s, ..., %d)\n",
573 errno
, file
, name
, XATTR_SIZE_MAX
);
580 sscanf(value
, "size=%d [a-z]", &rnd_size
);
581 shift
= sprintf(verify_value
, "size=%d ",
583 memcpy(verify_value
+ shift
, xattrbytes
,
584 sizeof (xattrbytes
) - shift
);
586 if (rnd_size
!= rc
||
587 memcmp(verify_value
, value
, rnd_size
)) {
588 ERROR("Error %d: verify failed\n "
589 "verify: %s\n value: %s\n", EINVAL
,
590 verify_string
, value_string
);
597 (void) gettimeofday(&stop
, NULL
);
598 seconds
= timeval_sub_seconds(&stop
, &start
);
599 fprintf(stdout
, "getxattr: %f seconds %f getxattrs/second\n",
600 seconds
, (files
* xattrs
) / seconds
);
602 rc
= post_hook("post");
621 struct timeval start
, stop
;
626 file
= malloc(fsize
);
629 ERROR("Error %d: malloc(%d) bytes for file name\n",
634 (void) gettimeofday(&start
, NULL
);
636 for (i
= 1; i
<= files
; i
++) {
637 if (snprintf(file
, fsize
, "%s/file-%d", path
, i
) >= fsize
) {
639 ERROR("Error %d: path too long\n", rc
);
643 if (nth
&& ((i
% nth
) == 0))
644 fprintf(stdout
, "unlink: %s\n", file
);
647 if ((rc
== -1) && (errno
!= ENOENT
)) {
648 ERROR("Error %d: unlink(%s)\n", errno
, file
);
654 (void) gettimeofday(&stop
, NULL
);
655 seconds
= timeval_sub_seconds(&stop
, &start
);
656 fprintf(stdout
, "unlink: %f seconds %f unlinks/second\n",
657 seconds
, files
/ seconds
);
659 rc
= post_hook("post");
668 main(int argc
, char **argv
)
672 rc
= parse_args(argc
, argv
);
676 if (value_is_random
) {
677 size_t rndsz
= sizeof (xattrbytes
);
679 rc
= get_random_bytes(xattrbytes
, rndsz
);
681 ERROR("Error %d: get_random_bytes() wanted %zd "
682 "got %d\n", errno
, rndsz
, rc
);
686 memset(xattrbytes
, 'x', sizeof (xattrbytes
));
689 if (phase
== PHASE_ALL
|| phase
== PHASE_CREATE
) {
695 if (phase
== PHASE_ALL
|| phase
== PHASE_SETXATTR
) {
701 if (phase
== PHASE_ALL
|| phase
== PHASE_GETXATTR
) {
707 if (!keep_files
&& (phase
== PHASE_ALL
|| phase
== PHASE_UNLINK
)) {