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