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