1 // SPDX-License-Identifier: CDDL-1.0 2 /* 3 * CDDL HEADER START 4 * 5 * The contents of this file are subject to the terms of the 6 * Common Development and Distribution License (the "License"). 7 * You may not use this file except in compliance with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or https://opensource.org/licenses/CDDL-1.0. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 23 /* 24 * Copyright 2016 Lawrence Livermore National Security, LLC. 25 */ 26 27 /* 28 * An extended attribute (xattr) correctness test. This program creates 29 * N files and sets M attrs on them of size S. Optionally is will verify 30 * a pattern stored in the xattr. 31 */ 32 #include <stdlib.h> 33 #include <stddef.h> 34 #include <stdio.h> 35 #include <string.h> 36 #include <errno.h> 37 #include <getopt.h> 38 #include <fcntl.h> 39 #include <time.h> 40 #include <unistd.h> 41 #include <sys/xattr.h> 42 #include <sys/types.h> 43 #include <sys/wait.h> 44 #include <sys/stat.h> 45 #include <sys/time.h> 46 #include <linux/limits.h> 47 48 #define ERROR(fmt, ...) \ 49 fprintf(stderr, "xattrtest: %s:%d: %s: " fmt "\n", \ 50 __FILE__, __LINE__, \ 51 __func__, ## __VA_ARGS__); 52 53 static const char shortopts[] = "hvycdn:f:x:s:p:t:e:rRko:"; 54 static const struct option longopts[] = { 55 { "help", no_argument, 0, 'h' }, 56 { "verbose", no_argument, 0, 'v' }, 57 { "verify", no_argument, 0, 'y' }, 58 { "nth", required_argument, 0, 'n' }, 59 { "files", required_argument, 0, 'f' }, 60 { "xattrs", required_argument, 0, 'x' }, 61 { "size", required_argument, 0, 's' }, 62 { "path", required_argument, 0, 'p' }, 63 { "synccaches", no_argument, 0, 'c' }, 64 { "dropcaches", no_argument, 0, 'd' }, 65 { "script", required_argument, 0, 't' }, 66 { "seed", required_argument, 0, 'e' }, 67 { "random", no_argument, 0, 'r' }, 68 { "randomvalue", no_argument, 0, 'R' }, 69 { "keep", no_argument, 0, 'k' }, 70 { "only", required_argument, 0, 'o' }, 71 { 0, 0, 0, 0 } 72 }; 73 74 enum phases { 75 PHASE_ALL = 0, 76 PHASE_CREATE, 77 PHASE_SETXATTR, 78 PHASE_GETXATTR, 79 PHASE_UNLINK, 80 PHASE_INVAL 81 }; 82 83 static int verbose = 0; 84 static int verify = 0; 85 static int synccaches = 0; 86 static int dropcaches = 0; 87 static int nth = 0; 88 static int files = 1000; 89 static int xattrs = 1; 90 static int size = 6; 91 static int size_is_random = 0; 92 static int value_is_random = 0; 93 static int keep_files = 0; 94 static int phase = PHASE_ALL; 95 static const char *path = "/tmp/xattrtest"; 96 static const char *script = "/bin/true"; 97 static char xattrbytes[XATTR_SIZE_MAX]; 98 99 static int 100 usage(char *argv0) 101 { 102 fprintf(stderr, 103 "usage: %s [-hvycdrRk] [-n <nth>] [-f <files>] [-x <xattrs>]\n" 104 " [-s <bytes>] [-p <path>] [-t <script> ] [-o <phase>]\n", 105 argv0); 106 107 fprintf(stderr, 108 " --help -h This help\n" 109 " --verbose -v Increase verbosity\n" 110 " --verify -y Verify xattr contents\n" 111 " --nth -n <nth> Print every nth file\n" 112 " --files -f <files> Set xattrs on N files\n" 113 " --xattrs -x <xattrs> Set N xattrs on each file\n" 114 " --size -s <bytes> Set N bytes per xattr\n" 115 " --path -p <path> Path to files\n" 116 " --synccaches -c Sync caches between phases\n" 117 " --dropcaches -d Drop caches between phases\n" 118 " --script -t <script> Exec script between phases\n" 119 " --seed -e <seed> Random seed value\n" 120 " --random -r Randomly sized xattrs [16-size]\n" 121 " --randomvalue -R Random xattr values\n" 122 " --keep -k Don't unlink files\n" 123 " --only -o <num> Only run phase N\n" 124 " 0=all, 1=create, 2=setxattr,\n" 125 " 3=getxattr, 4=unlink\n\n"); 126 127 return (1); 128 } 129 130 static int 131 parse_args(int argc, char **argv) 132 { 133 long seed = time(NULL); 134 int c; 135 int rc = 0; 136 137 while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { 138 switch (c) { 139 case 'h': 140 return (usage(argv[0])); 141 case 'v': 142 verbose++; 143 break; 144 case 'y': 145 verify = 1; 146 break; 147 case 'n': 148 nth = strtol(optarg, NULL, 0); 149 break; 150 case 'f': 151 files = strtol(optarg, NULL, 0); 152 break; 153 case 'x': 154 xattrs = strtol(optarg, NULL, 0); 155 break; 156 case 's': 157 size = strtol(optarg, NULL, 0); 158 if (size > XATTR_SIZE_MAX) { 159 fprintf(stderr, "Error: the -s value may not " 160 "be greater than %d\n", XATTR_SIZE_MAX); 161 rc = 1; 162 } 163 break; 164 case 'p': 165 path = optarg; 166 break; 167 case 'c': 168 synccaches = 1; 169 break; 170 case 'd': 171 dropcaches = 1; 172 break; 173 case 't': 174 script = optarg; 175 break; 176 case 'e': 177 seed = strtol(optarg, NULL, 0); 178 break; 179 case 'r': 180 size_is_random = 1; 181 break; 182 case 'R': 183 value_is_random = 1; 184 break; 185 case 'k': 186 keep_files = 1; 187 break; 188 case 'o': 189 phase = strtol(optarg, NULL, 0); 190 if (phase <= PHASE_ALL || phase >= PHASE_INVAL) { 191 fprintf(stderr, "Error: the -o value must be " 192 "greater than %d and less than %d\n", 193 PHASE_ALL, PHASE_INVAL); 194 rc = 1; 195 } 196 break; 197 default: 198 rc = 1; 199 break; 200 } 201 } 202 203 if (rc != 0) 204 return (rc); 205 206 srandom(seed); 207 208 if (verbose) { 209 fprintf(stdout, "verbose: %d\n", verbose); 210 fprintf(stdout, "verify: %d\n", verify); 211 fprintf(stdout, "nth: %d\n", nth); 212 fprintf(stdout, "files: %d\n", files); 213 fprintf(stdout, "xattrs: %d\n", xattrs); 214 fprintf(stdout, "size: %d\n", size); 215 fprintf(stdout, "path: %s\n", path); 216 fprintf(stdout, "synccaches: %d\n", synccaches); 217 fprintf(stdout, "dropcaches: %d\n", dropcaches); 218 fprintf(stdout, "script: %s\n", script); 219 fprintf(stdout, "seed: %ld\n", seed); 220 fprintf(stdout, "random size: %d\n", size_is_random); 221 fprintf(stdout, "random value: %d\n", value_is_random); 222 fprintf(stdout, "keep: %d\n", keep_files); 223 fprintf(stdout, "only: %d\n", phase); 224 fprintf(stdout, "%s", "\n"); 225 } 226 227 return (rc); 228 } 229 230 static int 231 drop_caches(void) 232 { 233 char file[] = "/proc/sys/vm/drop_caches"; 234 int fd, rc; 235 236 fd = open(file, O_WRONLY); 237 if (fd == -1) { 238 ERROR("Error %d: open(\"%s\", O_WRONLY)\n", errno, file); 239 return (errno); 240 } 241 242 rc = write(fd, "3", 1); 243 if ((rc == -1) || (rc != 1)) { 244 ERROR("Error %d: write(%d, \"3\", 1)\n", errno, fd); 245 (void) close(fd); 246 return (errno); 247 } 248 249 rc = close(fd); 250 if (rc == -1) { 251 ERROR("Error %d: close(%d)\n", errno, fd); 252 return (errno); 253 } 254 255 return (0); 256 } 257 258 static int 259 run_process(const char *path, char *argv[]) 260 { 261 pid_t pid; 262 int rc, devnull_fd; 263 264 pid = fork(); 265 if (pid == 0) { 266 devnull_fd = open("/dev/null", O_WRONLY); 267 268 if (devnull_fd < 0) 269 _exit(-1); 270 271 (void) dup2(devnull_fd, STDOUT_FILENO); 272 (void) dup2(devnull_fd, STDERR_FILENO); 273 close(devnull_fd); 274 275 (void) execvp(path, argv); 276 _exit(-1); 277 } else if (pid > 0) { 278 int status; 279 280 while ((rc = waitpid(pid, &status, 0)) == -1 && 281 errno == EINTR) { } 282 283 if (rc < 0 || !WIFEXITED(status)) 284 return (-1); 285 286 return (WEXITSTATUS(status)); 287 } 288 289 return (-1); 290 } 291 292 static int 293 post_hook(const char *phase) 294 { 295 char *argv[3] = { (char *)script, (char *)phase, NULL }; 296 int rc; 297 298 if (synccaches) 299 sync(); 300 301 if (dropcaches) { 302 rc = drop_caches(); 303 if (rc) 304 return (rc); 305 } 306 307 rc = run_process(script, argv); 308 if (rc) 309 return (rc); 310 311 return (0); 312 } 313 314 #define USEC_PER_SEC 1000000 315 316 static void 317 timeval_normalize(struct timeval *tv, time_t sec, suseconds_t usec) 318 { 319 while (usec >= USEC_PER_SEC) { 320 usec -= USEC_PER_SEC; 321 sec++; 322 } 323 324 while (usec < 0) { 325 usec += USEC_PER_SEC; 326 sec--; 327 } 328 329 tv->tv_sec = sec; 330 tv->tv_usec = usec; 331 } 332 333 static void 334 timeval_sub(struct timeval *delta, struct timeval *tv1, struct timeval *tv2) 335 { 336 timeval_normalize(delta, 337 tv1->tv_sec - tv2->tv_sec, 338 tv1->tv_usec - tv2->tv_usec); 339 } 340 341 static double 342 timeval_sub_seconds(struct timeval *tv1, struct timeval *tv2) 343 { 344 struct timeval delta; 345 346 timeval_sub(&delta, tv1, tv2); 347 return ((double)delta.tv_usec / USEC_PER_SEC + delta.tv_sec); 348 } 349 350 static int 351 create_files(void) 352 { 353 int i, rc; 354 char *file = NULL; 355 struct timeval start, stop; 356 double seconds; 357 size_t fsize; 358 359 fsize = PATH_MAX; 360 file = malloc(fsize); 361 if (file == NULL) { 362 rc = ENOMEM; 363 ERROR("Error %d: malloc(%d) bytes for file name\n", rc, 364 PATH_MAX); 365 goto out; 366 } 367 368 (void) gettimeofday(&start, NULL); 369 370 for (i = 1; i <= files; i++) { 371 if (snprintf(file, fsize, "%s/file-%d", path, i) >= fsize) { 372 rc = EINVAL; 373 ERROR("Error %d: path too long\n", rc); 374 goto out; 375 } 376 377 if (nth && ((i % nth) == 0)) 378 fprintf(stdout, "create: %s\n", file); 379 380 rc = unlink(file); 381 if ((rc == -1) && (errno != ENOENT)) { 382 ERROR("Error %d: unlink(%s)\n", errno, file); 383 rc = errno; 384 goto out; 385 } 386 387 rc = open(file, O_CREAT, 0644); 388 if (rc == -1) { 389 ERROR("Error %d: open(%s, O_CREATE, 0644)\n", 390 errno, file); 391 rc = errno; 392 goto out; 393 } 394 395 rc = close(rc); 396 if (rc == -1) { 397 ERROR("Error %d: close(%d)\n", errno, rc); 398 rc = errno; 399 goto out; 400 } 401 } 402 403 (void) gettimeofday(&stop, NULL); 404 seconds = timeval_sub_seconds(&stop, &start); 405 fprintf(stdout, "create: %f seconds %f creates/second\n", 406 seconds, files / seconds); 407 408 rc = post_hook("post"); 409 out: 410 if (file) 411 free(file); 412 413 return (rc); 414 } 415 416 static int 417 get_random_bytes(char *buf, size_t bytes) 418 { 419 int rand; 420 ssize_t bytes_read = 0; 421 422 rand = open("/dev/urandom", O_RDONLY); 423 424 if (rand < 0) 425 return (rand); 426 427 while (bytes_read < bytes) { 428 ssize_t rc = read(rand, buf + bytes_read, bytes - bytes_read); 429 if (rc < 0) 430 break; 431 bytes_read += rc; 432 } 433 434 (void) close(rand); 435 436 return (bytes_read); 437 } 438 439 static int 440 setxattrs(void) 441 { 442 int i, j, rnd_size = size, shift, rc = 0; 443 char name[XATTR_NAME_MAX]; 444 char *value = NULL; 445 char *file = NULL; 446 struct timeval start, stop; 447 double seconds; 448 size_t fsize; 449 450 value = malloc(XATTR_SIZE_MAX); 451 if (value == NULL) { 452 rc = ENOMEM; 453 ERROR("Error %d: malloc(%d) bytes for xattr value\n", rc, 454 XATTR_SIZE_MAX); 455 goto out; 456 } 457 458 fsize = PATH_MAX; 459 file = malloc(fsize); 460 if (file == NULL) { 461 rc = ENOMEM; 462 ERROR("Error %d: malloc(%d) bytes for file name\n", rc, 463 PATH_MAX); 464 goto out; 465 } 466 467 (void) gettimeofday(&start, NULL); 468 469 for (i = 1; i <= files; i++) { 470 if (snprintf(file, fsize, "%s/file-%d", path, i) >= fsize) { 471 rc = EINVAL; 472 ERROR("Error %d: path too long\n", rc); 473 goto out; 474 } 475 476 if (nth && ((i % nth) == 0)) 477 fprintf(stdout, "setxattr: %s\n", file); 478 479 for (j = 1; j <= xattrs; j++) { 480 if (size_is_random) 481 rnd_size = (random() % (size - 16)) + 16; 482 483 (void) sprintf(name, "user.%d", j); 484 shift = sprintf(value, "size=%d ", rnd_size); 485 memcpy(value + shift, xattrbytes, 486 sizeof (xattrbytes) - shift); 487 488 rc = lsetxattr(file, name, value, rnd_size, 0); 489 if (rc == -1) { 490 ERROR("Error %d: lsetxattr(%s, %s, ..., %d)\n", 491 errno, file, name, rnd_size); 492 goto out; 493 } 494 } 495 } 496 497 (void) gettimeofday(&stop, NULL); 498 seconds = timeval_sub_seconds(&stop, &start); 499 fprintf(stdout, "setxattr: %f seconds %f setxattrs/second\n", 500 seconds, (files * xattrs) / seconds); 501 502 rc = post_hook("post"); 503 out: 504 if (file) 505 free(file); 506 507 if (value) 508 free(value); 509 510 return (rc); 511 } 512 513 static int 514 getxattrs(void) 515 { 516 int i, j, rnd_size, shift, rc = 0; 517 char name[XATTR_NAME_MAX]; 518 char *verify_value = NULL; 519 const char *verify_string; 520 char *value = NULL; 521 const char *value_string; 522 char *file = NULL; 523 struct timeval start, stop; 524 double seconds; 525 size_t fsize; 526 527 verify_value = malloc(XATTR_SIZE_MAX); 528 if (verify_value == NULL) { 529 rc = ENOMEM; 530 ERROR("Error %d: malloc(%d) bytes for xattr verify\n", rc, 531 XATTR_SIZE_MAX); 532 goto out; 533 } 534 535 value = malloc(XATTR_SIZE_MAX); 536 if (value == NULL) { 537 rc = ENOMEM; 538 ERROR("Error %d: malloc(%d) bytes for xattr value\n", rc, 539 XATTR_SIZE_MAX); 540 goto out; 541 } 542 543 verify_string = value_is_random ? "<random>" : verify_value; 544 value_string = value_is_random ? "<random>" : value; 545 546 fsize = PATH_MAX; 547 file = malloc(fsize); 548 549 if (file == NULL) { 550 rc = ENOMEM; 551 ERROR("Error %d: malloc(%d) bytes for file name\n", rc, 552 PATH_MAX); 553 goto out; 554 } 555 556 (void) gettimeofday(&start, NULL); 557 558 for (i = 1; i <= files; i++) { 559 if (snprintf(file, fsize, "%s/file-%d", path, i) >= fsize) { 560 rc = EINVAL; 561 ERROR("Error %d: path too long\n", rc); 562 goto out; 563 } 564 565 if (nth && ((i % nth) == 0)) 566 fprintf(stdout, "getxattr: %s\n", file); 567 568 for (j = 1; j <= xattrs; j++) { 569 (void) sprintf(name, "user.%d", j); 570 571 rc = lgetxattr(file, name, value, XATTR_SIZE_MAX); 572 if (rc == -1) { 573 ERROR("Error %d: lgetxattr(%s, %s, ..., %d)\n", 574 errno, file, name, XATTR_SIZE_MAX); 575 goto out; 576 } 577 578 if (!verify) 579 continue; 580 581 sscanf(value, "size=%d [a-z]", &rnd_size); 582 shift = sprintf(verify_value, "size=%d ", 583 rnd_size); 584 memcpy(verify_value + shift, xattrbytes, 585 sizeof (xattrbytes) - shift); 586 587 if (rnd_size != rc || 588 memcmp(verify_value, value, rnd_size)) { 589 ERROR("Error %d: verify failed\n " 590 "verify: %s\n value: %s\n", EINVAL, 591 verify_string, value_string); 592 rc = 1; 593 goto out; 594 } 595 } 596 } 597 598 (void) gettimeofday(&stop, NULL); 599 seconds = timeval_sub_seconds(&stop, &start); 600 fprintf(stdout, "getxattr: %f seconds %f getxattrs/second\n", 601 seconds, (files * xattrs) / seconds); 602 603 rc = post_hook("post"); 604 out: 605 if (file) 606 free(file); 607 608 if (value) 609 free(value); 610 611 if (verify_value) 612 free(verify_value); 613 614 return (rc); 615 } 616 617 static int 618 unlink_files(void) 619 { 620 int i, rc; 621 char *file = NULL; 622 struct timeval start, stop; 623 double seconds; 624 size_t fsize; 625 626 fsize = PATH_MAX; 627 file = malloc(fsize); 628 if (file == NULL) { 629 rc = ENOMEM; 630 ERROR("Error %d: malloc(%d) bytes for file name\n", 631 rc, PATH_MAX); 632 goto out; 633 } 634 635 (void) gettimeofday(&start, NULL); 636 637 for (i = 1; i <= files; i++) { 638 if (snprintf(file, fsize, "%s/file-%d", path, i) >= fsize) { 639 rc = EINVAL; 640 ERROR("Error %d: path too long\n", rc); 641 goto out; 642 } 643 644 if (nth && ((i % nth) == 0)) 645 fprintf(stdout, "unlink: %s\n", file); 646 647 rc = unlink(file); 648 if ((rc == -1) && (errno != ENOENT)) { 649 ERROR("Error %d: unlink(%s)\n", errno, file); 650 free(file); 651 return (errno); 652 } 653 } 654 655 (void) gettimeofday(&stop, NULL); 656 seconds = timeval_sub_seconds(&stop, &start); 657 fprintf(stdout, "unlink: %f seconds %f unlinks/second\n", 658 seconds, files / seconds); 659 660 rc = post_hook("post"); 661 out: 662 if (file) 663 free(file); 664 665 return (rc); 666 } 667 668 int 669 main(int argc, char **argv) 670 { 671 int rc; 672 673 rc = parse_args(argc, argv); 674 if (rc) 675 return (rc); 676 677 if (value_is_random) { 678 size_t rndsz = sizeof (xattrbytes); 679 680 rc = get_random_bytes(xattrbytes, rndsz); 681 if (rc < rndsz) { 682 ERROR("Error %d: get_random_bytes() wanted %zd " 683 "got %d\n", errno, rndsz, rc); 684 return (rc); 685 } 686 } else { 687 memset(xattrbytes, 'x', sizeof (xattrbytes)); 688 } 689 690 if (phase == PHASE_ALL || phase == PHASE_CREATE) { 691 rc = create_files(); 692 if (rc) 693 return (rc); 694 } 695 696 if (phase == PHASE_ALL || phase == PHASE_SETXATTR) { 697 rc = setxattrs(); 698 if (rc) 699 return (rc); 700 } 701 702 if (phase == PHASE_ALL || phase == PHASE_GETXATTR) { 703 rc = getxattrs(); 704 if (rc) 705 return (rc); 706 } 707 708 if (!keep_files && (phase == PHASE_ALL || phase == PHASE_UNLINK)) { 709 rc = unlink_files(); 710 if (rc) 711 return (rc); 712 } 713 714 return (0); 715 } 716