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]
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 char path
[PATH_MAX
] = "/tmp/xattrtest";
95 static char script
[PATH_MAX
] = "/bin/true";
96 static char xattrbytes
[XATTR_SIZE_MAX
];
99 usage(int argc
, char **argv
)
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(argc
, argv
));
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
);
164 strncpy(path
, optarg
, PATH_MAX
);
165 path
[PATH_MAX
- 1] = '\0';
174 strncpy(script
, optarg
, PATH_MAX
);
175 script
[PATH_MAX
- 1] = '\0';
178 seed
= strtol(optarg
, NULL
, 0);
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
);
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");
234 char file
[] = "/proc/sys/vm/drop_caches";
237 fd
= open(file
, O_WRONLY
);
239 ERROR("Error %d: open(\"%s\", O_WRONLY)\n", errno
, file
);
243 rc
= write(fd
, "3", 1);
244 if ((rc
== -1) || (rc
!= 1)) {
245 ERROR("Error %d: write(%d, \"3\", 1)\n", errno
, fd
);
252 ERROR("Error %d: close(%d)\n", errno
, fd
);
260 run_process(const char *path
, char *argv
[])
267 devnull_fd
= open("/dev/null", O_WRONLY
);
272 (void) dup2(devnull_fd
, STDOUT_FILENO
);
273 (void) dup2(devnull_fd
, STDERR_FILENO
);
276 (void) execvp(path
, argv
);
278 } else if (pid
> 0) {
281 while ((rc
= waitpid(pid
, &status
, 0)) == -1 &&
284 if (rc
< 0 || !WIFEXITED(status
))
287 return (WEXITSTATUS(status
));
294 post_hook(char *phase
)
296 char *argv
[3] = { script
, phase
, (char *)0 };
308 rc
= run_process(script
, argv
);
315 #define USEC_PER_SEC 1000000
318 timeval_normalize(struct timeval
*tv
, time_t sec
, suseconds_t usec
)
320 while (usec
>= USEC_PER_SEC
) {
321 usec
-= USEC_PER_SEC
;
326 usec
+= USEC_PER_SEC
;
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
);
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
);
356 struct timeval start
, stop
;
361 file
= malloc(fsize
);
364 ERROR("Error %d: malloc(%d) bytes for file name\n", rc
,
369 (void) gettimeofday(&start
, NULL
);
371 for (i
= 1; i
<= files
; i
++) {
372 if (snprintf(file
, fsize
, "%s/file-%d", path
, i
) >= fsize
) {
374 ERROR("Error %d: path too long\n", rc
);
378 if (nth
&& ((i
% nth
) == 0))
379 fprintf(stdout
, "create: %s\n", file
);
382 if ((rc
== -1) && (errno
!= ENOENT
)) {
383 ERROR("Error %d: unlink(%s)\n", errno
, file
);
388 rc
= open(file
, O_CREAT
, 0644);
390 ERROR("Error %d: open(%s, O_CREATE, 0644)\n",
398 ERROR("Error %d: close(%d)\n", errno
, rc
);
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");
418 get_random_bytes(char *buf
, size_t bytes
)
421 ssize_t bytes_read
= 0;
423 rand
= open("/dev/urandom", O_RDONLY
);
428 while (bytes_read
< bytes
) {
429 ssize_t rc
= read(rand
, buf
+ bytes_read
, bytes
- bytes_read
);
443 int i
, j
, rnd_size
= size
, shift
, rc
= 0;
444 char name
[XATTR_NAME_MAX
];
447 struct timeval start
, stop
;
451 value
= malloc(XATTR_SIZE_MAX
);
454 ERROR("Error %d: malloc(%d) bytes for xattr value\n", rc
,
460 file
= malloc(fsize
);
463 ERROR("Error %d: malloc(%d) bytes for file name\n", rc
,
468 (void) gettimeofday(&start
, NULL
);
470 for (i
= 1; i
<= files
; i
++) {
471 if (snprintf(file
, fsize
, "%s/file-%d", path
, i
) >= fsize
) {
473 ERROR("Error %d: path too long\n", rc
);
477 if (nth
&& ((i
% nth
) == 0))
478 fprintf(stdout
, "setxattr: %s\n", file
);
480 for (j
= 1; j
<= xattrs
; j
++) {
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);
491 ERROR("Error %d: lsetxattr(%s, %s, ..., %d)\n",
492 errno
, file
, name
, rnd_size
);
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");
517 int i
, j
, rnd_size
, shift
, rc
= 0;
518 char name
[XATTR_NAME_MAX
];
519 char *verify_value
= NULL
;
524 struct timeval start
, stop
;
528 verify_value
= malloc(XATTR_SIZE_MAX
);
529 if (verify_value
== NULL
) {
531 ERROR("Error %d: malloc(%d) bytes for xattr verify\n", rc
,
536 value
= malloc(XATTR_SIZE_MAX
);
539 ERROR("Error %d: malloc(%d) bytes for xattr value\n", rc
,
544 verify_string
= value_is_random
? "<random>" : verify_value
;
545 value_string
= value_is_random
? "<random>" : value
;
548 file
= malloc(fsize
);
552 ERROR("Error %d: malloc(%d) bytes for file name\n", rc
,
557 (void) gettimeofday(&start
, NULL
);
559 for (i
= 1; i
<= files
; i
++) {
560 if (snprintf(file
, fsize
, "%s/file-%d", path
, i
) >= fsize
) {
562 ERROR("Error %d: path too long\n", rc
);
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
);
574 ERROR("Error %d: lgetxattr(%s, %s, ..., %d)\n",
575 errno
, file
, name
, XATTR_SIZE_MAX
);
582 sscanf(value
, "size=%d [a-z]", &rnd_size
);
583 shift
= sprintf(verify_value
, "size=%d ",
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
);
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");
623 struct timeval start
, stop
;
628 file
= malloc(fsize
);
631 ERROR("Error %d: malloc(%d) bytes for file name\n",
636 (void) gettimeofday(&start
, NULL
);
638 for (i
= 1; i
<= files
; i
++) {
639 if (snprintf(file
, fsize
, "%s/file-%d", path
, i
) >= fsize
) {
641 ERROR("Error %d: path too long\n", rc
);
645 if (nth
&& ((i
% nth
) == 0))
646 fprintf(stdout
, "unlink: %s\n", file
);
649 if ((rc
== -1) && (errno
!= ENOENT
)) {
650 ERROR("Error %d: unlink(%s)\n", errno
, file
);
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");
670 main(int argc
, char **argv
)
674 rc
= parse_args(argc
, argv
);
678 if (value_is_random
) {
679 size_t rndsz
= sizeof (xattrbytes
);
681 rc
= get_random_bytes(xattrbytes
, rndsz
);
683 ERROR("Error %d: get_random_bytes() wanted %zd "
684 "got %d\n", errno
, rndsz
, rc
);
688 memset(xattrbytes
, 'x', sizeof (xattrbytes
));
691 if (phase
== PHASE_ALL
|| phase
== PHASE_CREATE
) {
697 if (phase
== PHASE_ALL
|| phase
== PHASE_SETXATTR
) {
703 if (phase
== PHASE_ALL
|| phase
== PHASE_GETXATTR
) {
709 if (!keep_files
&& (phase
== PHASE_ALL
|| phase
== PHASE_UNLINK
)) {