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