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 extern char *program_invocation_short_name
;
49 #define ERROR(fmt, ...) \
50 fprintf(stderr, "%s: %s:%d: %s: " fmt "\n", \
51 program_invocation_short_name, __FILE__, __LINE__, \
52 __func__, ## __VA_ARGS__);
54 static const char shortopts
[] = "hvycdn:f:x:s:p:t:e:rRko:";
55 static 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' },
71 { "only", required_argument
, 0, 'o' },
84 static int verbose
= 0;
85 static int verify
= 0;
86 static int synccaches
= 0;
87 static int dropcaches
= 0;
89 static int files
= 1000;
90 static int xattrs
= 1;
92 static int size_is_random
= 0;
93 static int value_is_random
= 0;
94 static int keep_files
= 0;
95 static int phase
= PHASE_ALL
;
96 static char path
[PATH_MAX
] = "/tmp/xattrtest";
97 static char script
[PATH_MAX
] = "/bin/true";
98 static char xattrbytes
[XATTR_SIZE_MAX
];
101 usage(int argc
, char **argv
)
104 "usage: %s [-hvycdrRk] [-n <nth>] [-f <files>] [-x <xattrs>]\n"
105 " [-s <bytes>] [-p <path>] [-t <script> ] [-o <phase>]\n",
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");
132 parse_args(int argc
, char **argv
)
134 long seed
= time(NULL
);
138 while ((c
= getopt_long(argc
, argv
, shortopts
, longopts
, NULL
)) != -1) {
141 return (usage(argc
, argv
));
147 if (phase
!= PHASE_ALL
) {
149 "Error: -y and -o are incompatible.\n");
154 nth
= strtol(optarg
, NULL
, 0);
157 files
= strtol(optarg
, NULL
, 0);
160 xattrs
= strtol(optarg
, NULL
, 0);
163 size
= strtol(optarg
, NULL
, 0);
164 if (size
> XATTR_SIZE_MAX
) {
165 fprintf(stderr
, "Error: the -s value may not "
166 "be greater than %d\n", XATTR_SIZE_MAX
);
171 strncpy(path
, optarg
, PATH_MAX
);
172 path
[PATH_MAX
- 1] = '\0';
181 strncpy(script
, optarg
, PATH_MAX
);
182 script
[PATH_MAX
- 1] = '\0';
185 seed
= strtol(optarg
, NULL
, 0);
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
);
206 "Error: -y and -o are incompatible.\n");
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
);
236 fprintf(stdout
, "only: %d\n", phase
);
237 fprintf(stdout
, "%s", "\n");
246 char file
[] = "/proc/sys/vm/drop_caches";
249 fd
= open(file
, O_WRONLY
);
251 ERROR("Error %d: open(\"%s\", O_WRONLY)\n", errno
, file
);
255 rc
= write(fd
, "3", 1);
256 if ((rc
== -1) || (rc
!= 1)) {
257 ERROR("Error %d: write(%d, \"3\", 1)\n", errno
, fd
);
264 ERROR("Error %d: close(%d)\n", errno
, fd
);
272 run_process(const char *path
, char *argv
[])
279 devnull_fd
= open("/dev/null", O_WRONLY
);
284 (void) dup2(devnull_fd
, STDOUT_FILENO
);
285 (void) dup2(devnull_fd
, STDERR_FILENO
);
288 (void) execvp(path
, argv
);
290 } else if (pid
> 0) {
293 while ((rc
= waitpid(pid
, &status
, 0)) == -1 &&
296 if (rc
< 0 || !WIFEXITED(status
))
299 return (WEXITSTATUS(status
));
306 post_hook(char *phase
)
308 char *argv
[3] = { script
, phase
, (char *)0 };
320 rc
= run_process(script
, argv
);
327 #define USEC_PER_SEC 1000000
330 timeval_normalize(struct timeval
*tv
, time_t sec
, suseconds_t usec
)
332 while (usec
>= USEC_PER_SEC
) {
333 usec
-= USEC_PER_SEC
;
338 usec
+= USEC_PER_SEC
;
347 timeval_sub(struct timeval
*delta
, struct timeval
*tv1
, struct timeval
*tv2
)
349 timeval_normalize(delta
,
350 tv1
->tv_sec
- tv2
->tv_sec
,
351 tv1
->tv_usec
- tv2
->tv_usec
);
355 timeval_sub_seconds(struct timeval
*tv1
, struct timeval
*tv2
)
357 struct timeval delta
;
359 timeval_sub(&delta
, tv1
, tv2
);
360 return ((double)delta
.tv_usec
/ USEC_PER_SEC
+ delta
.tv_sec
);
368 struct timeval start
, stop
;
371 file
= malloc(PATH_MAX
);
374 ERROR("Error %d: malloc(%d) bytes for file name\n", rc
,
379 (void) gettimeofday(&start
, NULL
);
381 for (i
= 1; i
<= files
; i
++) {
382 (void) sprintf(file
, "%s/file-%d", path
, i
);
384 if (nth
&& ((i
% nth
) == 0))
385 fprintf(stdout
, "create: %s\n", file
);
388 if ((rc
== -1) && (errno
!= ENOENT
)) {
389 ERROR("Error %d: unlink(%s)\n", errno
, file
);
394 rc
= open(file
, O_CREAT
, 0644);
396 ERROR("Error %d: open(%s, O_CREATE, 0644)\n",
404 ERROR("Error %d: close(%d)\n", errno
, rc
);
410 (void) gettimeofday(&stop
, NULL
);
411 seconds
= timeval_sub_seconds(&stop
, &start
);
412 fprintf(stdout
, "create: %f seconds %f creates/second\n",
413 seconds
, files
/ seconds
);
415 rc
= post_hook("post");
424 get_random_bytes(char *buf
, size_t bytes
)
427 ssize_t bytes_read
= 0;
429 rand
= open("/dev/urandom", O_RDONLY
);
434 while (bytes_read
< bytes
) {
435 ssize_t rc
= read(rand
, buf
+ bytes_read
, bytes
- bytes_read
);
449 int i
, j
, rnd_size
= size
, shift
, rc
= 0;
450 char name
[XATTR_NAME_MAX
];
453 struct timeval start
, stop
;
456 value
= malloc(XATTR_SIZE_MAX
);
459 ERROR("Error %d: malloc(%d) bytes for xattr value\n", rc
,
464 file
= malloc(PATH_MAX
);
467 ERROR("Error %d: malloc(%d) bytes for file name\n", rc
,
472 (void) gettimeofday(&start
, NULL
);
474 for (i
= 1; i
<= files
; i
++) {
475 (void) sprintf(file
, "%s/file-%d", path
, i
);
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
;
527 verify_value
= malloc(XATTR_SIZE_MAX
);
528 if (verify_value
== NULL
) {
530 ERROR("Error %d: malloc(%d) bytes for xattr verify\n", rc
,
535 value
= malloc(XATTR_SIZE_MAX
);
538 ERROR("Error %d: malloc(%d) bytes for xattr value\n", rc
,
543 verify_string
= value_is_random
? "<random>" : verify_value
;
544 value_string
= value_is_random
? "<random>" : value
;
546 file
= malloc(PATH_MAX
);
549 ERROR("Error %d: malloc(%d) bytes for file name\n", rc
,
554 (void) gettimeofday(&start
, NULL
);
556 for (i
= 1; i
<= files
; i
++) {
557 (void) sprintf(file
, "%s/file-%d", path
, i
);
559 if (nth
&& ((i
% nth
) == 0))
560 fprintf(stdout
, "getxattr: %s\n", file
);
562 for (j
= 1; j
<= xattrs
; j
++) {
563 (void) sprintf(name
, "user.%d", j
);
565 rc
= lgetxattr(file
, name
, value
, XATTR_SIZE_MAX
);
567 ERROR("Error %d: lgetxattr(%s, %s, ..., %d)\n",
568 errno
, file
, name
, XATTR_SIZE_MAX
);
575 sscanf(value
, "size=%d [a-z]", &rnd_size
);
576 shift
= sprintf(verify_value
, "size=%d ",
578 memcpy(verify_value
+ shift
, xattrbytes
,
579 sizeof (xattrbytes
) - shift
);
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
);
592 (void) gettimeofday(&stop
, NULL
);
593 seconds
= timeval_sub_seconds(&stop
, &start
);
594 fprintf(stdout
, "getxattr: %f seconds %f getxattrs/second\n",
595 seconds
, (files
* xattrs
) / seconds
);
597 rc
= post_hook("post");
616 struct timeval start
, stop
;
619 file
= malloc(PATH_MAX
);
622 ERROR("Error %d: malloc(%d) bytes for file name\n",
627 (void) gettimeofday(&start
, NULL
);
629 for (i
= 1; i
<= files
; i
++) {
630 (void) sprintf(file
, "%s/file-%d", path
, i
);
632 if (nth
&& ((i
% nth
) == 0))
633 fprintf(stdout
, "unlink: %s\n", file
);
636 if ((rc
== -1) && (errno
!= ENOENT
)) {
637 ERROR("Error %d: unlink(%s)\n", errno
, file
);
643 (void) gettimeofday(&stop
, NULL
);
644 seconds
= timeval_sub_seconds(&stop
, &start
);
645 fprintf(stdout
, "unlink: %f seconds %f unlinks/second\n",
646 seconds
, files
/ seconds
);
648 rc
= post_hook("post");
657 main(int argc
, char **argv
)
661 rc
= parse_args(argc
, argv
);
665 if (value_is_random
) {
666 size_t rndsz
= sizeof (xattrbytes
);
668 rc
= get_random_bytes(xattrbytes
, rndsz
);
670 ERROR("Error %d: get_random_bytes() wanted %zd "
671 "got %d\n", errno
, rndsz
, rc
);
675 memset(xattrbytes
, 'x', sizeof (xattrbytes
));
678 if (phase
== PHASE_ALL
|| phase
== PHASE_CREATE
) {
684 if (phase
== PHASE_ALL
|| phase
== PHASE_SETXATTR
) {
690 if (phase
== PHASE_ALL
|| phase
== PHASE_GETXATTR
) {
696 if (!keep_files
&& (phase
== PHASE_ALL
|| phase
== PHASE_UNLINK
)) {