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