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 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 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 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 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 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 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 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 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 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 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 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 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 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 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