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>
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>
46
47 extern 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
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' },
72 { 0, 0, 0, 0 }
73 };
74
75 enum phases {
76 PHASE_ALL = 0,
77 PHASE_CREATE,
78 PHASE_SETXATTR,
79 PHASE_GETXATTR,
80 PHASE_UNLINK,
81 PHASE_INVAL
82 };
83
84 static int verbose = 0;
85 static int verify = 0;
86 static int synccaches = 0;
87 static int dropcaches = 0;
88 static int nth = 0;
89 static int files = 1000;
90 static int xattrs = 1;
91 static int size = 6;
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];
99
100 static int
usage(int argc,char ** argv)101 usage(int argc, char **argv)
102 {
103 fprintf(stderr,
104 "usage: %s [-hvycdrRk] [-n <nth>] [-f <files>] [-x <xattrs>]\n"
105 " [-s <bytes>] [-p <path>] [-t <script> ] [-o <phase>]\n",
106 argv[0]);
107
108 fprintf(stderr,
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");
127
128 return (1);
129 }
130
131 static int
parse_args(int argc,char ** argv)132 parse_args(int argc, char **argv)
133 {
134 long seed = time(NULL);
135 int c;
136 int rc = 0;
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;
147 break;
148 case 'n':
149 nth = strtol(optarg, NULL, 0);
150 break;
151 case 'f':
152 files = strtol(optarg, NULL, 0);
153 break;
154 case 'x':
155 xattrs = strtol(optarg, NULL, 0);
156 break;
157 case 's':
158 size = strtol(optarg, NULL, 0);
159 if (size > XATTR_SIZE_MAX) {
160 fprintf(stderr, "Error: the -s value may not "
161 "be greater than %d\n", XATTR_SIZE_MAX);
162 rc = 1;
163 }
164 break;
165 case 'p':
166 strncpy(path, optarg, PATH_MAX);
167 path[PATH_MAX - 1] = '\0';
168 break;
169 case 'c':
170 synccaches = 1;
171 break;
172 case 'd':
173 dropcaches = 1;
174 break;
175 case 't':
176 strncpy(script, optarg, PATH_MAX);
177 script[PATH_MAX - 1] = '\0';
178 break;
179 case 'e':
180 seed = strtol(optarg, NULL, 0);
181 break;
182 case 'r':
183 size_is_random = 1;
184 break;
185 case 'R':
186 value_is_random = 1;
187 break;
188 case 'k':
189 keep_files = 1;
190 break;
191 case 'o':
192 phase = strtol(optarg, NULL, 0);
193 if (phase <= PHASE_ALL || phase >= PHASE_INVAL) {
194 fprintf(stderr, "Error: the -o value must be "
195 "greater than %d and less than %d\n",
196 PHASE_ALL, PHASE_INVAL);
197 rc = 1;
198 }
199 break;
200 default:
201 rc = 1;
202 break;
203 }
204 }
205
206 if (rc != 0)
207 return (rc);
208
209 srandom(seed);
210
211 if (verbose) {
212 fprintf(stdout, "verbose: %d\n", verbose);
213 fprintf(stdout, "verify: %d\n", verify);
214 fprintf(stdout, "nth: %d\n", nth);
215 fprintf(stdout, "files: %d\n", files);
216 fprintf(stdout, "xattrs: %d\n", xattrs);
217 fprintf(stdout, "size: %d\n", size);
218 fprintf(stdout, "path: %s\n", path);
219 fprintf(stdout, "synccaches: %d\n", synccaches);
220 fprintf(stdout, "dropcaches: %d\n", dropcaches);
221 fprintf(stdout, "script: %s\n", script);
222 fprintf(stdout, "seed: %ld\n", seed);
223 fprintf(stdout, "random size: %d\n", size_is_random);
224 fprintf(stdout, "random value: %d\n", value_is_random);
225 fprintf(stdout, "keep: %d\n", keep_files);
226 fprintf(stdout, "only: %d\n", phase);
227 fprintf(stdout, "%s", "\n");
228 }
229
230 return (rc);
231 }
232
233 static int
drop_caches(void)234 drop_caches(void)
235 {
236 char file[] = "/proc/sys/vm/drop_caches";
237 int fd, rc;
238
239 fd = open(file, O_WRONLY);
240 if (fd == -1) {
241 ERROR("Error %d: open(\"%s\", O_WRONLY)\n", errno, file);
242 return (errno);
243 }
244
245 rc = write(fd, "3", 1);
246 if ((rc == -1) || (rc != 1)) {
247 ERROR("Error %d: write(%d, \"3\", 1)\n", errno, fd);
248 (void) close(fd);
249 return (errno);
250 }
251
252 rc = close(fd);
253 if (rc == -1) {
254 ERROR("Error %d: close(%d)\n", errno, fd);
255 return (errno);
256 }
257
258 return (0);
259 }
260
261 static int
run_process(const char * path,char * argv[])262 run_process(const char *path, char *argv[])
263 {
264 pid_t pid;
265 int rc, devnull_fd;
266
267 pid = vfork();
268 if (pid == 0) {
269 devnull_fd = open("/dev/null", O_WRONLY);
270
271 if (devnull_fd < 0)
272 _exit(-1);
273
274 (void) dup2(devnull_fd, STDOUT_FILENO);
275 (void) dup2(devnull_fd, STDERR_FILENO);
276 close(devnull_fd);
277
278 (void) execvp(path, argv);
279 _exit(-1);
280 } else if (pid > 0) {
281 int status;
282
283 while ((rc = waitpid(pid, &status, 0)) == -1 &&
284 errno == EINTR) { }
285
286 if (rc < 0 || !WIFEXITED(status))
287 return (-1);
288
289 return (WEXITSTATUS(status));
290 }
291
292 return (-1);
293 }
294
295 static int
post_hook(char * phase)296 post_hook(char *phase)
297 {
298 char *argv[3] = { script, phase, (char *)0 };
299 int rc;
300
301 if (synccaches)
302 sync();
303
304 if (dropcaches) {
305 rc = drop_caches();
306 if (rc)
307 return (rc);
308 }
309
310 rc = run_process(script, argv);
311 if (rc)
312 return (rc);
313
314 return (0);
315 }
316
317 #define USEC_PER_SEC 1000000
318
319 static void
timeval_normalize(struct timeval * tv,time_t sec,suseconds_t usec)320 timeval_normalize(struct timeval *tv, time_t sec, suseconds_t usec)
321 {
322 while (usec >= USEC_PER_SEC) {
323 usec -= USEC_PER_SEC;
324 sec++;
325 }
326
327 while (usec < 0) {
328 usec += USEC_PER_SEC;
329 sec--;
330 }
331
332 tv->tv_sec = sec;
333 tv->tv_usec = usec;
334 }
335
336 static void
timeval_sub(struct timeval * delta,struct timeval * tv1,struct timeval * tv2)337 timeval_sub(struct timeval *delta, struct timeval *tv1, struct timeval *tv2)
338 {
339 timeval_normalize(delta,
340 tv1->tv_sec - tv2->tv_sec,
341 tv1->tv_usec - tv2->tv_usec);
342 }
343
344 static double
timeval_sub_seconds(struct timeval * tv1,struct timeval * tv2)345 timeval_sub_seconds(struct timeval *tv1, struct timeval *tv2)
346 {
347 struct timeval delta;
348
349 timeval_sub(&delta, tv1, tv2);
350 return ((double)delta.tv_usec / USEC_PER_SEC + delta.tv_sec);
351 }
352
353 static int
create_files(void)354 create_files(void)
355 {
356 int i, rc;
357 char *file = NULL;
358 struct timeval start, stop;
359 double seconds;
360 size_t fsize;
361
362 fsize = PATH_MAX;
363 file = malloc(fsize);
364 if (file == NULL) {
365 rc = ENOMEM;
366 ERROR("Error %d: malloc(%d) bytes for file name\n", rc,
367 PATH_MAX);
368 goto out;
369 }
370
371 (void) gettimeofday(&start, NULL);
372
373 for (i = 1; i <= files; i++) {
374 if (snprintf(file, fsize, "%s/file-%d", path, i) >= fsize) {
375 rc = EINVAL;
376 ERROR("Error %d: path too long\n", rc);
377 goto out;
378 }
379
380 if (nth && ((i % nth) == 0))
381 fprintf(stdout, "create: %s\n", file);
382
383 rc = unlink(file);
384 if ((rc == -1) && (errno != ENOENT)) {
385 ERROR("Error %d: unlink(%s)\n", errno, file);
386 rc = errno;
387 goto out;
388 }
389
390 rc = open(file, O_CREAT, 0644);
391 if (rc == -1) {
392 ERROR("Error %d: open(%s, O_CREATE, 0644)\n",
393 errno, file);
394 rc = errno;
395 goto out;
396 }
397
398 rc = close(rc);
399 if (rc == -1) {
400 ERROR("Error %d: close(%d)\n", errno, rc);
401 rc = errno;
402 goto out;
403 }
404 }
405
406 (void) gettimeofday(&stop, NULL);
407 seconds = timeval_sub_seconds(&stop, &start);
408 fprintf(stdout, "create: %f seconds %f creates/second\n",
409 seconds, files / seconds);
410
411 rc = post_hook("post");
412 out:
413 if (file)
414 free(file);
415
416 return (rc);
417 }
418
419 static int
get_random_bytes(char * buf,size_t bytes)420 get_random_bytes(char *buf, size_t bytes)
421 {
422 int rand;
423 ssize_t bytes_read = 0;
424
425 rand = open("/dev/urandom", O_RDONLY);
426
427 if (rand < 0)
428 return (rand);
429
430 while (bytes_read < bytes) {
431 ssize_t rc = read(rand, buf + bytes_read, bytes - bytes_read);
432 if (rc < 0)
433 break;
434 bytes_read += rc;
435 }
436
437 (void) close(rand);
438
439 return (bytes_read);
440 }
441
442 static int
setxattrs(void)443 setxattrs(void)
444 {
445 int i, j, rnd_size = size, shift, rc = 0;
446 char name[XATTR_NAME_MAX];
447 char *value = NULL;
448 char *file = NULL;
449 struct timeval start, stop;
450 double seconds;
451 size_t fsize;
452
453 value = malloc(XATTR_SIZE_MAX);
454 if (value == NULL) {
455 rc = ENOMEM;
456 ERROR("Error %d: malloc(%d) bytes for xattr value\n", rc,
457 XATTR_SIZE_MAX);
458 goto out;
459 }
460
461 fsize = PATH_MAX;
462 file = malloc(fsize);
463 if (file == NULL) {
464 rc = ENOMEM;
465 ERROR("Error %d: malloc(%d) bytes for file name\n", rc,
466 PATH_MAX);
467 goto out;
468 }
469
470 (void) gettimeofday(&start, NULL);
471
472 for (i = 1; i <= files; i++) {
473 if (snprintf(file, fsize, "%s/file-%d", path, i) >= fsize) {
474 rc = EINVAL;
475 ERROR("Error %d: path too long\n", rc);
476 goto out;
477 }
478
479 if (nth && ((i % nth) == 0))
480 fprintf(stdout, "setxattr: %s\n", file);
481
482 for (j = 1; j <= xattrs; j++) {
483 if (size_is_random)
484 rnd_size = (random() % (size - 16)) + 16;
485
486 (void) sprintf(name, "user.%d", j);
487 shift = sprintf(value, "size=%d ", rnd_size);
488 memcpy(value + shift, xattrbytes,
489 sizeof (xattrbytes) - shift);
490
491 rc = lsetxattr(file, name, value, rnd_size, 0);
492 if (rc == -1) {
493 ERROR("Error %d: lsetxattr(%s, %s, ..., %d)\n",
494 errno, file, name, rnd_size);
495 goto out;
496 }
497 }
498 }
499
500 (void) gettimeofday(&stop, NULL);
501 seconds = timeval_sub_seconds(&stop, &start);
502 fprintf(stdout, "setxattr: %f seconds %f setxattrs/second\n",
503 seconds, (files * xattrs) / seconds);
504
505 rc = post_hook("post");
506 out:
507 if (file)
508 free(file);
509
510 if (value)
511 free(value);
512
513 return (rc);
514 }
515
516 static int
getxattrs(void)517 getxattrs(void)
518 {
519 int i, j, rnd_size, shift, rc = 0;
520 char name[XATTR_NAME_MAX];
521 char *verify_value = NULL;
522 char *verify_string;
523 char *value = NULL;
524 char *value_string;
525 char *file = NULL;
526 struct timeval start, stop;
527 double seconds;
528 size_t fsize;
529
530 verify_value = malloc(XATTR_SIZE_MAX);
531 if (verify_value == NULL) {
532 rc = ENOMEM;
533 ERROR("Error %d: malloc(%d) bytes for xattr verify\n", rc,
534 XATTR_SIZE_MAX);
535 goto out;
536 }
537
538 value = malloc(XATTR_SIZE_MAX);
539 if (value == NULL) {
540 rc = ENOMEM;
541 ERROR("Error %d: malloc(%d) bytes for xattr value\n", rc,
542 XATTR_SIZE_MAX);
543 goto out;
544 }
545
546 verify_string = value_is_random ? "<random>" : verify_value;
547 value_string = value_is_random ? "<random>" : value;
548
549 fsize = PATH_MAX;
550 file = malloc(fsize);
551
552 if (file == NULL) {
553 rc = ENOMEM;
554 ERROR("Error %d: malloc(%d) bytes for file name\n", rc,
555 PATH_MAX);
556 goto out;
557 }
558
559 (void) gettimeofday(&start, NULL);
560
561 for (i = 1; i <= files; i++) {
562 if (snprintf(file, fsize, "%s/file-%d", path, i) >= fsize) {
563 rc = EINVAL;
564 ERROR("Error %d: path too long\n", rc);
565 goto out;
566 }
567
568 if (nth && ((i % nth) == 0))
569 fprintf(stdout, "getxattr: %s\n", file);
570
571 for (j = 1; j <= xattrs; j++) {
572 (void) sprintf(name, "user.%d", j);
573
574 rc = lgetxattr(file, name, value, XATTR_SIZE_MAX);
575 if (rc == -1) {
576 ERROR("Error %d: lgetxattr(%s, %s, ..., %d)\n",
577 errno, file, name, XATTR_SIZE_MAX);
578 goto out;
579 }
580
581 if (!verify)
582 continue;
583
584 sscanf(value, "size=%d [a-z]", &rnd_size);
585 shift = sprintf(verify_value, "size=%d ",
586 rnd_size);
587 memcpy(verify_value + shift, xattrbytes,
588 sizeof (xattrbytes) - shift);
589
590 if (rnd_size != rc ||
591 memcmp(verify_value, value, rnd_size)) {
592 ERROR("Error %d: verify failed\n "
593 "verify: %s\n value: %s\n", EINVAL,
594 verify_string, value_string);
595 rc = 1;
596 goto out;
597 }
598 }
599 }
600
601 (void) gettimeofday(&stop, NULL);
602 seconds = timeval_sub_seconds(&stop, &start);
603 fprintf(stdout, "getxattr: %f seconds %f getxattrs/second\n",
604 seconds, (files * xattrs) / seconds);
605
606 rc = post_hook("post");
607 out:
608 if (file)
609 free(file);
610
611 if (value)
612 free(value);
613
614 if (verify_value)
615 free(verify_value);
616
617 return (rc);
618 }
619
620 static int
unlink_files(void)621 unlink_files(void)
622 {
623 int i, rc;
624 char *file = NULL;
625 struct timeval start, stop;
626 double seconds;
627 size_t fsize;
628
629 fsize = PATH_MAX;
630 file = malloc(fsize);
631 if (file == NULL) {
632 rc = ENOMEM;
633 ERROR("Error %d: malloc(%d) bytes for file name\n",
634 rc, PATH_MAX);
635 goto out;
636 }
637
638 (void) gettimeofday(&start, NULL);
639
640 for (i = 1; i <= files; i++) {
641 if (snprintf(file, fsize, "%s/file-%d", path, i) >= fsize) {
642 rc = EINVAL;
643 ERROR("Error %d: path too long\n", rc);
644 goto out;
645 }
646
647 if (nth && ((i % nth) == 0))
648 fprintf(stdout, "unlink: %s\n", file);
649
650 rc = unlink(file);
651 if ((rc == -1) && (errno != ENOENT)) {
652 ERROR("Error %d: unlink(%s)\n", errno, file);
653 free(file);
654 return (errno);
655 }
656 }
657
658 (void) gettimeofday(&stop, NULL);
659 seconds = timeval_sub_seconds(&stop, &start);
660 fprintf(stdout, "unlink: %f seconds %f unlinks/second\n",
661 seconds, files / seconds);
662
663 rc = post_hook("post");
664 out:
665 if (file)
666 free(file);
667
668 return (rc);
669 }
670
671 int
main(int argc,char ** argv)672 main(int argc, char **argv)
673 {
674 int rc;
675
676 rc = parse_args(argc, argv);
677 if (rc)
678 return (rc);
679
680 if (value_is_random) {
681 size_t rndsz = sizeof (xattrbytes);
682
683 rc = get_random_bytes(xattrbytes, rndsz);
684 if (rc < rndsz) {
685 ERROR("Error %d: get_random_bytes() wanted %zd "
686 "got %d\n", errno, rndsz, rc);
687 return (rc);
688 }
689 } else {
690 memset(xattrbytes, 'x', sizeof (xattrbytes));
691 }
692
693 if (phase == PHASE_ALL || phase == PHASE_CREATE) {
694 rc = create_files();
695 if (rc)
696 return (rc);
697 }
698
699 if (phase == PHASE_ALL || phase == PHASE_SETXATTR) {
700 rc = setxattrs();
701 if (rc)
702 return (rc);
703 }
704
705 if (phase == PHASE_ALL || phase == PHASE_GETXATTR) {
706 rc = getxattrs();
707 if (rc)
708 return (rc);
709 }
710
711 if (!keep_files && (phase == PHASE_ALL || phase == PHASE_UNLINK)) {
712 rc = unlink_files();
713 if (rc)
714 return (rc);
715 }
716
717 return (0);
718 }
719