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