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