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