1 /* 2 * Copyright (c) 1998-2001 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * The contents of this file constitute Original Code as defined in and 7 * are subject to the Apple Public Source License Version 1.1 (the 8 * "License"). You may not use this file except in compliance with the 9 * License. Please obtain a copy of the License at 10 * http://www.apple.com/publicsource and read it before using this file. 11 * 12 * This Original Code and all software distributed under the License are 13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER 14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the 17 * License for the specific language governing rights and limitations 18 * under the License. 19 * 20 * @APPLE_LICENSE_HEADER_END@ 21 * 22 * File: fsx.c 23 * Author: Avadis Tevanian, Jr. 24 * 25 * File system exerciser. 26 * 27 * Rewrite and enhancements 1998-2001 Conrad Minshall -- conrad@mac.com 28 * 29 * Various features from Joe Sokol, Pat Dirks, and Clark Warner. 30 * 31 * Small changes to work under Linux -- davej@suse.de 32 * 33 * Sundry porting patches from Guy Harris 12/2001 34 * $FreeBSD$ 35 */ 36 37 #include <sys/types.h> 38 #include <sys/stat.h> 39 #ifdef _UWIN 40 # include <sys/param.h> 41 # include <limits.h> 42 # include <time.h> 43 # include <strings.h> 44 #endif 45 #include <fcntl.h> 46 #include <sys/mman.h> 47 #ifndef MAP_FILE 48 # define MAP_FILE 0 49 #endif 50 #include <limits.h> 51 #include <signal.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 #include <unistd.h> 56 #include <stdarg.h> 57 #include <errno.h> 58 59 #define NUMPRINTCOLUMNS 32 /* # columns of data to print on each line */ 60 61 /* 62 * A log entry is an operation and a bunch of arguments. 63 */ 64 65 struct log_entry { 66 int operation; 67 int args[3]; 68 }; 69 70 #define LOGSIZE 1000 71 72 struct log_entry oplog[LOGSIZE]; /* the log */ 73 int logptr = 0; /* current position in log */ 74 int logcount = 0; /* total ops */ 75 76 /* 77 * Define operations 78 */ 79 80 #define OP_READ 1 81 #define OP_WRITE 2 82 #define OP_TRUNCATE 3 83 #define OP_CLOSEOPEN 4 84 #define OP_MAPREAD 5 85 #define OP_MAPWRITE 6 86 #define OP_SKIPPED 7 87 88 int page_size; 89 int page_mask; 90 91 char *original_buf; /* a pointer to the original data */ 92 char *good_buf; /* a pointer to the correct data */ 93 char *temp_buf; /* a pointer to the current data */ 94 char *fname; /* name of our test file */ 95 int fd; /* fd for our test file */ 96 97 off_t file_size = 0; 98 off_t biggest = 0; 99 char state[256]; 100 unsigned long testcalls = 0; /* calls to function "test" */ 101 102 unsigned long simulatedopcount = 0; /* -b flag */ 103 int closeprob = 0; /* -c flag */ 104 int debug = 0; /* -d flag */ 105 unsigned long debugstart = 0; /* -D flag */ 106 unsigned long maxfilelen = 256 * 1024; /* -l flag */ 107 int sizechecks = 1; /* -n flag disables them */ 108 int maxoplen = 64 * 1024; /* -o flag */ 109 int quiet = 0; /* -q flag */ 110 unsigned long progressinterval = 0; /* -p flag */ 111 int readbdy = 1; /* -r flag */ 112 int style = 0; /* -s flag */ 113 int truncbdy = 1; /* -t flag */ 114 int writebdy = 1; /* -w flag */ 115 long monitorstart = -1; /* -m flag */ 116 long monitorend = -1; /* -m flag */ 117 int lite = 0; /* -L flag */ 118 long numops = -1; /* -N flag */ 119 int randomoplen = 1; /* -O flag disables it */ 120 int seed = 1; /* -S flag */ 121 int mapped_writes = 1; /* -W flag disables */ 122 int mapped_reads = 1; /* -R flag disables it */ 123 int fsxgoodfd = 0; 124 FILE * fsxlogf = NULL; 125 int badoff = -1; 126 int closeopen = 0; 127 128 129 void 130 vwarnc(code, fmt, ap) 131 int code; 132 const char *fmt; 133 va_list ap; 134 { 135 fprintf(stderr, "fsx: "); 136 if (fmt != NULL) { 137 vfprintf(stderr, fmt, ap); 138 fprintf(stderr, ": "); 139 } 140 fprintf(stderr, "%s\n", strerror(code)); 141 } 142 143 144 void 145 warn(const char * fmt, ...) 146 { 147 va_list ap; 148 va_start(ap, fmt); 149 vwarnc(errno, fmt, ap); 150 va_end(ap); 151 } 152 153 154 void 155 prt(char *fmt, ...) 156 { 157 va_list args; 158 159 va_start(args, fmt); 160 vfprintf(stdout, fmt, args); 161 if (fsxlogf) 162 vfprintf(fsxlogf, fmt, args); 163 va_end(args); 164 } 165 166 void 167 prterr(char *prefix) 168 { 169 prt("%s%s%s\n", prefix, prefix ? ": " : "", strerror(errno)); 170 } 171 172 173 void 174 log4(int operation, int arg0, int arg1, int arg2) 175 { 176 struct log_entry *le; 177 178 le = &oplog[logptr]; 179 le->operation = operation; 180 if (closeopen) 181 le->operation = ~ le->operation; 182 le->args[0] = arg0; 183 le->args[1] = arg1; 184 le->args[2] = arg2; 185 logptr++; 186 logcount++; 187 if (logptr >= LOGSIZE) 188 logptr = 0; 189 } 190 191 192 void 193 logdump(void) 194 { 195 int i, count, down; 196 struct log_entry *lp; 197 198 prt("LOG DUMP (%d total operations):\n", logcount); 199 if (logcount < LOGSIZE) { 200 i = 0; 201 count = logcount; 202 } else { 203 i = logptr; 204 count = LOGSIZE; 205 } 206 for ( ; count > 0; count--) { 207 int opnum; 208 209 opnum = i+1 + (logcount/LOGSIZE)*LOGSIZE; 210 prt("%d(%d mod 256): ", opnum, opnum%256); 211 lp = &oplog[i]; 212 if ((closeopen = lp->operation < 0)) 213 lp->operation = ~ lp->operation; 214 215 switch (lp->operation) { 216 case OP_MAPREAD: 217 prt("MAPREAD\t0x%x thru 0x%x\t(0x%x bytes)", 218 lp->args[0], lp->args[0] + lp->args[1] - 1, 219 lp->args[1]); 220 if (badoff >= lp->args[0] && badoff < 221 lp->args[0] + lp->args[1]) 222 prt("\t***RRRR***"); 223 break; 224 case OP_MAPWRITE: 225 prt("MAPWRITE 0x%x thru 0x%x\t(0x%x bytes)", 226 lp->args[0], lp->args[0] + lp->args[1] - 1, 227 lp->args[1]); 228 if (badoff >= lp->args[0] && badoff < 229 lp->args[0] + lp->args[1]) 230 prt("\t******WWWW"); 231 break; 232 case OP_READ: 233 prt("READ\t0x%x thru 0x%x\t(0x%x bytes)", 234 lp->args[0], lp->args[0] + lp->args[1] - 1, 235 lp->args[1]); 236 if (badoff >= lp->args[0] && 237 badoff < lp->args[0] + lp->args[1]) 238 prt("\t***RRRR***"); 239 break; 240 case OP_WRITE: 241 prt("WRITE\t0x%x thru 0x%x\t(0x%x bytes)", 242 lp->args[0], lp->args[0] + lp->args[1] - 1, 243 lp->args[1]); 244 if (lp->args[0] > lp->args[2]) 245 prt(" HOLE"); 246 else if (lp->args[0] + lp->args[1] > lp->args[2]) 247 prt(" EXTEND"); 248 if ((badoff >= lp->args[0] || badoff >=lp->args[2]) && 249 badoff < lp->args[0] + lp->args[1]) 250 prt("\t***WWWW"); 251 break; 252 case OP_TRUNCATE: 253 down = lp->args[0] < lp->args[1]; 254 prt("TRUNCATE %s\tfrom 0x%x to 0x%x", 255 down ? "DOWN" : "UP", lp->args[1], lp->args[0]); 256 if (badoff >= lp->args[!down] && 257 badoff < lp->args[!!down]) 258 prt("\t******WWWW"); 259 break; 260 case OP_SKIPPED: 261 prt("SKIPPED (no operation)"); 262 break; 263 default: 264 prt("BOGUS LOG ENTRY (operation code = %d)!", 265 lp->operation); 266 } 267 if (closeopen) 268 prt("\n\t\tCLOSE/OPEN"); 269 prt("\n"); 270 i++; 271 if (i == LOGSIZE) 272 i = 0; 273 } 274 } 275 276 277 void 278 save_buffer(char *buffer, off_t bufferlength, int fd) 279 { 280 off_t ret; 281 ssize_t byteswritten; 282 283 if (fd <= 0 || bufferlength == 0) 284 return; 285 286 if (bufferlength > SSIZE_MAX) { 287 prt("fsx flaw: overflow in save_buffer\n"); 288 exit(67); 289 } 290 if (lite) { 291 off_t size_by_seek = lseek(fd, (off_t)0, SEEK_END); 292 if (size_by_seek == (off_t)-1) 293 prterr("save_buffer: lseek eof"); 294 else if (bufferlength > size_by_seek) { 295 warn("save_buffer: .fsxgood file too short... will 296 save 0x%llx bytes instead of 0x%llx\n", (unsigned long long)size_by_seek, 297 (unsigned long long)bufferlength); 298 bufferlength = size_by_seek; 299 } 300 } 301 302 ret = lseek(fd, (off_t)0, SEEK_SET); 303 if (ret == (off_t)-1) 304 prterr("save_buffer: lseek 0"); 305 306 byteswritten = write(fd, buffer, (size_t)bufferlength); 307 if (byteswritten != bufferlength) { 308 if (byteswritten == -1) 309 prterr("save_buffer write"); 310 else 311 warn("save_buffer: short write, 0x%x bytes instead 312 of 0x%llx\n", 313 (unsigned)byteswritten, 314 (unsigned long long)bufferlength); 315 } 316 } 317 318 319 void 320 report_failure(int status) 321 { 322 logdump(); 323 324 if (fsxgoodfd) { 325 if (good_buf) { 326 save_buffer(good_buf, file_size, fsxgoodfd); 327 prt("Correct content saved for comparison\n"); 328 prt("(maybe hexdump \"%s\" vs \"%s.fsxgood\")\n", 329 fname, fname); 330 } 331 close(fsxgoodfd); 332 } 333 exit(status); 334 } 335 336 337 #define short_at(cp) ((unsigned short)((*((unsigned char *)(cp)) << 8) | \ 338 *(((unsigned char *)(cp)) + 1))) 339 340 void 341 check_buffers(unsigned offset, unsigned size) 342 { 343 unsigned char c, t; 344 unsigned i = 0; 345 unsigned n = 0; 346 unsigned op = 0; 347 unsigned bad = 0; 348 349 if (memcmp(good_buf + offset, temp_buf, size) != 0) { 350 prt("READ BAD DATA: offset = 0x%x, size = 0x%x\n", 351 offset, size); 352 prt("OFFSET\tGOOD\tBAD\tRANGE\n"); 353 while (size > 0) { 354 c = good_buf[offset]; 355 t = temp_buf[i]; 356 if (c != t) { 357 if (n == 0) { 358 bad = short_at(&temp_buf[i]); 359 prt("0x%5x\t0x%04x\t0x%04x", offset, 360 short_at(&good_buf[offset]), bad); 361 op = temp_buf[offset & 1 ? i+1 : i]; 362 } 363 n++; 364 badoff = offset; 365 } 366 offset++; 367 i++; 368 size--; 369 } 370 if (n) { 371 prt("\t0x%5x\n", n); 372 if (bad) 373 prt("operation# (mod 256) for the bad data 374 may be %u\n", ((unsigned)op & 0xff)); 375 else 376 prt("operation# (mod 256) for the bad data 377 unknown, check HOLE and EXTEND ops\n"); 378 } else 379 prt("????????????????\n"); 380 report_failure(110); 381 } 382 } 383 384 385 void 386 check_size(void) 387 { 388 struct stat statbuf; 389 off_t size_by_seek; 390 391 if (fstat(fd, &statbuf)) { 392 prterr("check_size: fstat"); 393 statbuf.st_size = -1; 394 } 395 size_by_seek = lseek(fd, (off_t)0, SEEK_END); 396 if (file_size != statbuf.st_size || file_size != size_by_seek) { 397 prt("Size error: expected 0x%llx stat 0x%llx seek 0x%llx\n", 398 (unsigned long long)file_size, 399 (unsigned long long)statbuf.st_size, 400 (unsigned long long)size_by_seek); 401 report_failure(120); 402 } 403 } 404 405 406 void 407 check_trunc_hack(void) 408 { 409 struct stat statbuf; 410 411 ftruncate(fd, (off_t)0); 412 ftruncate(fd, (off_t)100000); 413 fstat(fd, &statbuf); 414 if (statbuf.st_size != (off_t)100000) { 415 prt("no extend on truncate! not posix!\n"); 416 exit(130); 417 } 418 ftruncate(fd, 0); 419 } 420 421 422 void 423 doread(unsigned offset, unsigned size) 424 { 425 off_t ret; 426 unsigned iret; 427 428 offset -= offset % readbdy; 429 if (size == 0) { 430 if (!quiet && testcalls > simulatedopcount) 431 prt("skipping zero size read\n"); 432 log4(OP_SKIPPED, OP_READ, offset, size); 433 return; 434 } 435 if (size + offset > file_size) { 436 if (!quiet && testcalls > simulatedopcount) 437 prt("skipping seek/read past end of file\n"); 438 log4(OP_SKIPPED, OP_READ, offset, size); 439 return; 440 } 441 442 log4(OP_READ, offset, size, 0); 443 444 if (testcalls <= simulatedopcount) 445 return; 446 447 if (!quiet && ((progressinterval && 448 testcalls % progressinterval == 0) || 449 (debug && 450 (monitorstart == -1 || 451 (offset + size > monitorstart && 452 (monitorend == -1 || offset <= monitorend)))))) 453 prt("%lu read\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls, 454 offset, offset + size - 1, size); 455 ret = lseek(fd, (off_t)offset, SEEK_SET); 456 if (ret == (off_t)-1) { 457 prterr("doread: lseek"); 458 report_failure(140); 459 } 460 iret = read(fd, temp_buf, size); 461 if (iret != size) { 462 if (iret == -1) 463 prterr("doread: read"); 464 else 465 prt("short read: 0x%x bytes instead of 0x%x\n", 466 iret, size); 467 report_failure(141); 468 } 469 check_buffers(offset, size); 470 } 471 472 473 void 474 domapread(unsigned offset, unsigned size) 475 { 476 unsigned pg_offset; 477 unsigned map_size; 478 char *p; 479 480 offset -= offset % readbdy; 481 if (size == 0) { 482 if (!quiet && testcalls > simulatedopcount) 483 prt("skipping zero size read\n"); 484 log4(OP_SKIPPED, OP_MAPREAD, offset, size); 485 return; 486 } 487 if (size + offset > file_size) { 488 if (!quiet && testcalls > simulatedopcount) 489 prt("skipping seek/read past end of file\n"); 490 log4(OP_SKIPPED, OP_MAPREAD, offset, size); 491 return; 492 } 493 494 log4(OP_MAPREAD, offset, size, 0); 495 496 if (testcalls <= simulatedopcount) 497 return; 498 499 if (!quiet && ((progressinterval && 500 testcalls % progressinterval == 0) || 501 (debug && 502 (monitorstart == -1 || 503 (offset + size > monitorstart && 504 (monitorend == -1 || offset <= monitorend)))))) 505 prt("%lu mapread\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls, 506 offset, offset + size - 1, size); 507 508 pg_offset = offset & page_mask; 509 map_size = pg_offset + size; 510 511 if ((p = (char *)mmap(0, map_size, PROT_READ, MAP_FILE | 512 MAP_SHARED, fd, 513 (off_t)(offset - pg_offset))) == (char *)-1) { 514 prterr("domapread: mmap"); 515 report_failure(190); 516 } 517 memcpy(temp_buf, p + pg_offset, size); 518 if (munmap(p, map_size) != 0) { 519 prterr("domapread: munmap"); 520 report_failure(191); 521 } 522 523 check_buffers(offset, size); 524 } 525 526 527 void 528 gendata(char *original_buf, char *good_buf, unsigned offset, unsigned size) 529 { 530 while (size--) { 531 good_buf[offset] = testcalls % 256; 532 if (offset % 2) 533 good_buf[offset] += original_buf[offset]; 534 offset++; 535 } 536 } 537 538 539 void 540 dowrite(unsigned offset, unsigned size) 541 { 542 off_t ret; 543 unsigned iret; 544 545 offset -= offset % writebdy; 546 if (size == 0) { 547 if (!quiet && testcalls > simulatedopcount) 548 prt("skipping zero size write\n"); 549 log4(OP_SKIPPED, OP_WRITE, offset, size); 550 return; 551 } 552 553 log4(OP_WRITE, offset, size, file_size); 554 555 gendata(original_buf, good_buf, offset, size); 556 if (file_size < offset + size) { 557 if (file_size < offset) 558 memset(good_buf + file_size, '\0', offset - file_size); 559 file_size = offset + size; 560 if (lite) { 561 warn("Lite file size bug in fsx!"); 562 report_failure(149); 563 } 564 } 565 566 if (testcalls <= simulatedopcount) 567 return; 568 569 if (!quiet && ((progressinterval && 570 testcalls % progressinterval == 0) || 571 (debug && 572 (monitorstart == -1 || 573 (offset + size > monitorstart && 574 (monitorend == -1 || offset <= monitorend)))))) 575 prt("%lu write\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls, 576 offset, offset + size - 1, size); 577 ret = lseek(fd, (off_t)offset, SEEK_SET); 578 if (ret == (off_t)-1) { 579 prterr("dowrite: lseek"); 580 report_failure(150); 581 } 582 iret = write(fd, good_buf + offset, size); 583 if (iret != size) { 584 if (iret == -1) 585 prterr("dowrite: write"); 586 else 587 prt("short write: 0x%x bytes instead of 0x%x\n", 588 iret, size); 589 report_failure(151); 590 } 591 } 592 593 594 void 595 domapwrite(unsigned offset, unsigned size) 596 { 597 unsigned pg_offset; 598 unsigned map_size; 599 off_t cur_filesize; 600 char *p; 601 602 offset -= offset % writebdy; 603 if (size == 0) { 604 if (!quiet && testcalls > simulatedopcount) 605 prt("skipping zero size write\n"); 606 log4(OP_SKIPPED, OP_MAPWRITE, offset, size); 607 return; 608 } 609 cur_filesize = file_size; 610 611 log4(OP_MAPWRITE, offset, size, 0); 612 613 gendata(original_buf, good_buf, offset, size); 614 if (file_size < offset + size) { 615 if (file_size < offset) 616 memset(good_buf + file_size, '\0', offset - file_size); 617 file_size = offset + size; 618 if (lite) { 619 warn("Lite file size bug in fsx!"); 620 report_failure(200); 621 } 622 } 623 624 if (testcalls <= simulatedopcount) 625 return; 626 627 if (!quiet && ((progressinterval && 628 testcalls % progressinterval == 0) || 629 (debug && 630 (monitorstart == -1 || 631 (offset + size > monitorstart && 632 (monitorend == -1 || offset <= monitorend)))))) 633 prt("%lu mapwrite\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls, 634 offset, offset + size - 1, size); 635 636 if (file_size > cur_filesize) { 637 if (ftruncate(fd, file_size) == -1) { 638 prterr("domapwrite: ftruncate"); 639 exit(201); 640 } 641 } 642 pg_offset = offset & page_mask; 643 map_size = pg_offset + size; 644 645 if ((p = (char *)mmap(0, map_size, PROT_READ | PROT_WRITE, 646 MAP_FILE | MAP_SHARED, fd, 647 (off_t)(offset - pg_offset))) == (char *)-1) { 648 prterr("domapwrite: mmap"); 649 report_failure(202); 650 } 651 memcpy(p + pg_offset, good_buf + offset, size); 652 if (msync(p, map_size, 0) != 0) { 653 prterr("domapwrite: msync"); 654 report_failure(203); 655 } 656 if (munmap(p, map_size) != 0) { 657 prterr("domapwrite: munmap"); 658 report_failure(204); 659 } 660 } 661 662 663 void 664 dotruncate(unsigned size) 665 { 666 int oldsize = file_size; 667 668 size -= size % truncbdy; 669 if (size > biggest) { 670 biggest = size; 671 if (!quiet && testcalls > simulatedopcount) 672 prt("truncating to largest ever: 0x%x\n", size); 673 } 674 675 log4(OP_TRUNCATE, size, (unsigned)file_size, 0); 676 677 if (size > file_size) 678 memset(good_buf + file_size, '\0', size - file_size); 679 file_size = size; 680 681 if (testcalls <= simulatedopcount) 682 return; 683 684 if ((progressinterval && testcalls % progressinterval == 0) || 685 (debug && (monitorstart == -1 || monitorend == -1 || 686 size <= monitorend))) 687 prt("%lu trunc\tfrom 0x%x to 0x%x\n", testcalls, oldsize, 688 size); 689 if (ftruncate(fd, (off_t)size) == -1) { 690 prt("ftruncate1: %x\n", size); 691 prterr("dotruncate: ftruncate"); 692 report_failure(160); 693 } 694 } 695 696 697 void 698 writefileimage() 699 { 700 ssize_t iret; 701 702 if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) { 703 prterr("writefileimage: lseek"); 704 report_failure(171); 705 } 706 iret = write(fd, good_buf, file_size); 707 if ((off_t)iret != file_size) { 708 if (iret == -1) 709 prterr("writefileimage: write"); 710 else 711 prt("short write: 0x%x bytes instead of 0x%llx\n", 712 iret, (unsigned long long)file_size); 713 report_failure(172); 714 } 715 if (lite ? 0 : ftruncate(fd, file_size) == -1) { 716 prt("ftruncate2: %llx\n", (unsigned long long)file_size); 717 prterr("writefileimage: ftruncate"); 718 report_failure(173); 719 } 720 } 721 722 723 void 724 docloseopen(void) 725 { 726 if (testcalls <= simulatedopcount) 727 return; 728 729 if (debug) 730 prt("%lu close/open\n", testcalls); 731 if (close(fd)) { 732 prterr("docloseopen: close"); 733 report_failure(180); 734 } 735 fd = open(fname, O_RDWR, 0); 736 if (fd < 0) { 737 prterr("docloseopen: open"); 738 report_failure(181); 739 } 740 } 741 742 743 void 744 test(void) 745 { 746 unsigned long offset; 747 unsigned long size = maxoplen; 748 unsigned long rv = random(); 749 unsigned long op = rv % (3 + !lite + mapped_writes); 750 751 /* turn off the map read if necessary */ 752 753 if (op == 2 && !mapped_reads) 754 op = 0; 755 756 if (simulatedopcount > 0 && testcalls == simulatedopcount) 757 writefileimage(); 758 759 testcalls++; 760 761 if (closeprob) 762 closeopen = (rv >> 3) < (1 << 28) / closeprob; 763 764 if (debugstart > 0 && testcalls >= debugstart) 765 debug = 1; 766 767 if (!quiet && testcalls < simulatedopcount && testcalls % 100000 == 0) 768 prt("%lu...\n", testcalls); 769 770 /* 771 * READ: op = 0 772 * WRITE: op = 1 773 * MAPREAD: op = 2 774 * TRUNCATE: op = 3 775 * MAPWRITE: op = 3 or 4 776 */ 777 if (lite ? 0 : op == 3 && (style & 1) == 0) /* vanilla truncate? */ 778 dotruncate(random() % maxfilelen); 779 else { 780 if (randomoplen) 781 size = random() % (maxoplen+1); 782 if (lite ? 0 : op == 3) 783 dotruncate(size); 784 else { 785 offset = random(); 786 if (op == 1 || op == (lite ? 3 : 4)) { 787 offset %= maxfilelen; 788 if (offset + size > maxfilelen) 789 size = maxfilelen - offset; 790 if (op != 1) 791 domapwrite(offset, size); 792 else 793 dowrite(offset, size); 794 } else { 795 if (file_size) 796 offset %= file_size; 797 else 798 offset = 0; 799 if (offset + size > file_size) 800 size = file_size - offset; 801 if (op != 0) 802 domapread(offset, size); 803 else 804 doread(offset, size); 805 } 806 } 807 } 808 if (sizechecks && testcalls > simulatedopcount) 809 check_size(); 810 if (closeopen) 811 docloseopen(); 812 } 813 814 815 void 816 cleanup(sig) 817 int sig; 818 { 819 if (sig) 820 prt("signal %d\n", sig); 821 prt("testcalls = %lu\n", testcalls); 822 exit(sig); 823 } 824 825 826 void 827 usage(void) 828 { 829 fprintf(stdout, "usage: %s", 830 "fsx [-dnqLOW] [-b opnum] [-c Prob] [-l flen] [-m 831 start:end] [-o oplen] [-p progressinterval] [-r readbdy] [-s style] [-t 832 truncbdy] [-w writebdy] [-D startingop] [-N numops] [-P dirpath] [-S seed] 833 fname\n\ 834 -b opnum: beginning operation number (default 1)\n\ 835 -c P: 1 in P chance of file close+open at each op (default infinity)\n\ 836 -d: debug output for all operations\n\ 837 -l flen: the upper bound on file size (default 262144)\n\ 838 -m startop:endop: monitor (print debug output) specified byte range 839 (default 0:infinity)\n\ 840 -n: no verifications of file size\n\ 841 -o oplen: the upper bound on operation size (default 65536)\n\ 842 -p progressinterval: debug output at specified operation interval\n\ 843 -q: quieter operation\n\ 844 -r readbdy: 4096 would make reads page aligned (default 1)\n\ 845 -s style: 1 gives smaller truncates (default 0)\n\ 846 -t truncbdy: 4096 would make truncates page aligned (default 1)\n\ 847 -w writebdy: 4096 would make writes page aligned (default 1)\n\ 848 -D startingop: debug output starting at specified operation\n\ 849 -L: fsxLite - no file creations & no file size changes\n\ 850 -N numops: total # operations to do (default infinity)\n\ 851 -O: use oplen (see -o flag) for every op (default random)\n\ 852 -P: save .fsxlog and .fsxgood files in dirpath (default ./)\n\ 853 -S seed: for random # generator (default 1) 0 gets timestamp\n\ 854 -W: mapped write operations DISabled\n\ 855 -R: read() system calls only (mapped reads disabled)\n\ 856 fname: this filename is REQUIRED (no default)\n"); 857 exit(90); 858 } 859 860 861 int 862 getnum(char *s, char **e) 863 { 864 int ret = -1; 865 866 *e = (char *) 0; 867 ret = strtol(s, e, 0); 868 if (*e) 869 switch (**e) { 870 case 'b': 871 case 'B': 872 ret *= 512; 873 *e = *e + 1; 874 break; 875 case 'k': 876 case 'K': 877 ret *= 1024; 878 *e = *e + 1; 879 break; 880 case 'm': 881 case 'M': 882 ret *= 1024*1024; 883 *e = *e + 1; 884 break; 885 case 'w': 886 case 'W': 887 ret *= 4; 888 *e = *e + 1; 889 break; 890 } 891 return (ret); 892 } 893 894 895 int 896 main(int argc, char **argv) 897 { 898 int i, style, ch; 899 char *endp; 900 char goodfile[1024]; 901 char logfile[1024]; 902 903 goodfile[0] = 0; 904 logfile[0] = 0; 905 906 page_size = getpagesize(); 907 page_mask = page_size - 1; 908 909 setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */ 910 911 while ((ch = getopt(argc, argv, "b:c:dl:m:no:p:qr:s:t:w:D:LN:OP:RS:W")) 912 != EOF) 913 switch (ch) { 914 case 'b': 915 simulatedopcount = getnum(optarg, &endp); 916 if (!quiet) 917 fprintf(stdout, "Will begin at operation 918 %ld\n", 919 simulatedopcount); 920 if (simulatedopcount == 0) 921 usage(); 922 simulatedopcount -= 1; 923 break; 924 case 'c': 925 closeprob = getnum(optarg, &endp); 926 if (!quiet) 927 fprintf(stdout, 928 "Chance of close/open is 1 in %d\n", 929 closeprob); 930 if (closeprob <= 0) 931 usage(); 932 break; 933 case 'd': 934 debug = 1; 935 break; 936 case 'l': 937 maxfilelen = getnum(optarg, &endp); 938 if (maxfilelen <= 0) 939 usage(); 940 break; 941 case 'm': 942 monitorstart = getnum(optarg, &endp); 943 if (monitorstart < 0) 944 usage(); 945 if (!endp || *endp++ != ':') 946 usage(); 947 monitorend = getnum(endp, &endp); 948 if (monitorend < 0) 949 usage(); 950 if (monitorend == 0) 951 monitorend = -1; /* aka infinity */ 952 debug = 1; 953 case 'n': 954 sizechecks = 0; 955 break; 956 case 'o': 957 maxoplen = getnum(optarg, &endp); 958 if (maxoplen <= 0) 959 usage(); 960 break; 961 case 'p': 962 progressinterval = getnum(optarg, &endp); 963 if (progressinterval < 0) 964 usage(); 965 break; 966 case 'q': 967 quiet = 1; 968 break; 969 case 'r': 970 readbdy = getnum(optarg, &endp); 971 if (readbdy <= 0) 972 usage(); 973 break; 974 case 's': 975 style = getnum(optarg, &endp); 976 if (style < 0 || style > 1) 977 usage(); 978 break; 979 case 't': 980 truncbdy = getnum(optarg, &endp); 981 if (truncbdy <= 0) 982 usage(); 983 break; 984 case 'w': 985 writebdy = getnum(optarg, &endp); 986 if (writebdy <= 0) 987 usage(); 988 break; 989 case 'D': 990 debugstart = getnum(optarg, &endp); 991 if (debugstart < 1) 992 usage(); 993 break; 994 case 'L': 995 lite = 1; 996 break; 997 case 'N': 998 numops = getnum(optarg, &endp); 999 if (numops < 0) 1000 usage(); 1001 break; 1002 case 'O': 1003 randomoplen = 0; 1004 break; 1005 case 'P': 1006 strncpy(goodfile, optarg, sizeof(goodfile)); 1007 strcat(goodfile, "/"); 1008 strncpy(logfile, optarg, sizeof(logfile)); 1009 strcat(logfile, "/"); 1010 break; 1011 case 'R': 1012 mapped_reads = 0; 1013 break; 1014 case 'S': 1015 seed = getnum(optarg, &endp); 1016 if (seed == 0) 1017 seed = time(0) % 10000; 1018 if (!quiet) 1019 fprintf(stdout, "Seed set to %d\n", seed); 1020 if (seed < 0) 1021 usage(); 1022 break; 1023 case 'W': 1024 mapped_writes = 0; 1025 if (!quiet) 1026 fprintf(stdout, "mapped writes DISABLED\n"); 1027 break; 1028 1029 default: 1030 usage(); 1031 /* NOTREACHED */ 1032 } 1033 argc -= optind; 1034 argv += optind; 1035 if (argc != 1) 1036 usage(); 1037 fname = argv[0]; 1038 1039 signal(SIGHUP, cleanup); 1040 signal(SIGINT, cleanup); 1041 signal(SIGPIPE, cleanup); 1042 signal(SIGALRM, cleanup); 1043 signal(SIGTERM, cleanup); 1044 signal(SIGXCPU, cleanup); 1045 signal(SIGXFSZ, cleanup); 1046 signal(SIGVTALRM, cleanup); 1047 signal(SIGUSR1, cleanup); 1048 signal(SIGUSR2, cleanup); 1049 1050 initstate(seed, state, 256); 1051 setstate(state); 1052 fd = open(fname, O_RDWR|(lite ? 0 : O_CREAT|O_TRUNC), 0666); 1053 if (fd < 0) { 1054 prterr(fname); 1055 exit(91); 1056 } 1057 strncat(goodfile, fname, 256); 1058 strcat (goodfile, ".fsxgood"); 1059 fsxgoodfd = open(goodfile, O_RDWR|O_CREAT|O_TRUNC, 0666); 1060 if (fsxgoodfd < 0) { 1061 prterr(goodfile); 1062 exit(92); 1063 } 1064 strncat(logfile, fname, 256); 1065 strcat (logfile, ".fsxlog"); 1066 fsxlogf = fopen(logfile, "w"); 1067 if (fsxlogf == NULL) { 1068 prterr(logfile); 1069 exit(93); 1070 } 1071 if (lite) { 1072 off_t ret; 1073 file_size = maxfilelen = lseek(fd, (off_t)0, SEEK_END); 1074 if (file_size == (off_t)-1) { 1075 prterr(fname); 1076 warn("main: lseek eof"); 1077 exit(94); 1078 } 1079 ret = lseek(fd, (off_t)0, SEEK_SET); 1080 if (ret == (off_t)-1) { 1081 prterr(fname); 1082 warn("main: lseek 0"); 1083 exit(95); 1084 } 1085 } 1086 original_buf = (char *) malloc(maxfilelen); 1087 for (i = 0; i < maxfilelen; i++) 1088 original_buf[i] = random() % 256; 1089 good_buf = (char *) malloc(maxfilelen); 1090 memset(good_buf, '\0', maxfilelen); 1091 temp_buf = (char *) malloc(maxoplen); 1092 memset(temp_buf, '\0', maxoplen); 1093 if (lite) { /* zero entire existing file */ 1094 ssize_t written; 1095 1096 written = write(fd, good_buf, (size_t)maxfilelen); 1097 if (written != maxfilelen) { 1098 if (written == -1) { 1099 prterr(fname); 1100 warn("main: error on write"); 1101 } else 1102 warn("main: short write, 0x%x bytes instead 1103 of 0x%x\n", 1104 (unsigned)written, maxfilelen); 1105 exit(98); 1106 } 1107 } else 1108 check_trunc_hack(); 1109 1110 while (numops == -1 || numops--) 1111 test(); 1112 1113 if (close(fd)) { 1114 prterr("close"); 1115 report_failure(99); 1116 } 1117 prt("All operations completed A-OK!\n"); 1118 1119 exit(0); 1120 return 0; 1121 } 1122