188242Sjkh/*
288242Sjkh * Copyright (c) 1998-2001 Apple Computer, Inc. All rights reserved.
388242Sjkh *
488242Sjkh * @APPLE_LICENSE_HEADER_START@
588242Sjkh *
688242Sjkh * The contents of this file constitute Original Code as defined in and
7132704Sjkh * are subject to the Apple Public Source License Version 2.0 (the
888242Sjkh * "License").  You may not use this file except in compliance with the
988242Sjkh * License.  Please obtain a copy of the License at
10132704Sjkh * http://www.opensource.apple.com/apsl/ and read it before using this file.
1188242Sjkh *
1288242Sjkh * This Original Code and all software distributed under the License are
1388242Sjkh * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1488242Sjkh * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
1588242Sjkh * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
1688242Sjkh * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
1788242Sjkh * License for the specific language governing rights and limitations
1888242Sjkh * under the License.
1988242Sjkh *
2088242Sjkh * @APPLE_LICENSE_HEADER_END@
2188242Sjkh *
2288242Sjkh *	File:	fsx.c
2388242Sjkh *	Author:	Avadis Tevanian, Jr.
2488242Sjkh *
25113949Sjkh *	File system exerciser.
2688242Sjkh *
2788242Sjkh *	Rewrite and enhancements 1998-2001 Conrad Minshall -- conrad@mac.com
2888242Sjkh *
2988242Sjkh *	Various features from Joe Sokol, Pat Dirks, and Clark Warner.
3088242Sjkh *
3188242Sjkh *	Small changes to work under Linux -- davej@suse.de
3288242Sjkh *
3388242Sjkh *	Sundry porting patches from Guy Harris 12/2001
34113949Sjkh *
35113949Sjkh *	Checks for mmap last-page zero fill.
36113949Sjkh *
37132704Sjkh *	Updated license to APSL 2.0, 2004/7/27 - Jordan Hubbard
38132704Sjkh *
3988242Sjkh * $FreeBSD$
40113949Sjkh *
4188242Sjkh */
4288242Sjkh
4388242Sjkh#include <sys/types.h>
4488242Sjkh#include <sys/stat.h>
4588242Sjkh#ifdef _UWIN
4688242Sjkh# include <sys/param.h>
4788242Sjkh# include <limits.h>
4888242Sjkh# include <time.h>
4988242Sjkh# include <strings.h>
5088242Sjkh#endif
5188242Sjkh#include <fcntl.h>
5288242Sjkh#include <sys/mman.h>
5388242Sjkh#ifndef MAP_FILE
5488242Sjkh# define MAP_FILE 0
5588242Sjkh#endif
5688242Sjkh#include <limits.h>
5788242Sjkh#include <signal.h>
5888242Sjkh#include <stdio.h>
5988242Sjkh#include <stdlib.h>
6088242Sjkh#include <string.h>
6188242Sjkh#include <unistd.h>
6288242Sjkh#include <stdarg.h>
6388242Sjkh#include <errno.h>
6488242Sjkh
6588242Sjkh#define NUMPRINTCOLUMNS 32	/* # columns of data to print on each line */
6688242Sjkh
6788242Sjkh/*
6888242Sjkh *	A log entry is an operation and a bunch of arguments.
6988242Sjkh */
7088242Sjkh
7188242Sjkhstruct log_entry {
7288242Sjkh	int	operation;
7388242Sjkh	int	args[3];
7488242Sjkh};
7588242Sjkh
7688242Sjkh#define	LOGSIZE	1000
7788242Sjkh
7888242Sjkhstruct log_entry	oplog[LOGSIZE];	/* the log */
7988242Sjkhint			logptr = 0;	/* current position in log */
8088242Sjkhint			logcount = 0;	/* total ops */
8188242Sjkh
8288242Sjkh/*
8388242Sjkh *	Define operations
8488242Sjkh */
8588242Sjkh
8688242Sjkh#define	OP_READ		1
8788242Sjkh#define OP_WRITE	2
8888242Sjkh#define OP_TRUNCATE	3
8988242Sjkh#define OP_CLOSEOPEN	4
9088242Sjkh#define OP_MAPREAD	5
9188242Sjkh#define OP_MAPWRITE	6
9288242Sjkh#define OP_SKIPPED	7
93260729Savg#define OP_INVALIDATE	8
9488242Sjkh
9588242Sjkhint page_size;
9688242Sjkhint page_mask;
9788242Sjkh
9888242Sjkhchar	*original_buf;			/* a pointer to the original data */
9988242Sjkhchar	*good_buf;			/* a pointer to the correct data */
10088242Sjkhchar	*temp_buf;			/* a pointer to the current data */
10188242Sjkhchar	*fname;				/* name of our test file */
10288242Sjkhint	fd;				/* fd for our test file */
10388242Sjkh
10488242Sjkhoff_t		file_size = 0;
10588242Sjkhoff_t		biggest = 0;
10688242Sjkhchar		state[256];
10788242Sjkhunsigned long	testcalls = 0;		/* calls to function "test" */
10888242Sjkh
10988242Sjkhunsigned long	simulatedopcount = 0;	/* -b flag */
11088242Sjkhint	closeprob = 0;			/* -c flag */
111260729Savgint	invlprob = 0;			/* -i flag */
11288242Sjkhint	debug = 0;			/* -d flag */
11388242Sjkhunsigned long	debugstart = 0;		/* -D flag */
11488242Sjkhunsigned long	maxfilelen = 256 * 1024;	/* -l flag */
11588242Sjkhint	sizechecks = 1;			/* -n flag disables them */
11688242Sjkhint	maxoplen = 64 * 1024;		/* -o flag */
11788242Sjkhint	quiet = 0;			/* -q flag */
11888242Sjkhunsigned long progressinterval = 0;	/* -p flag */
11988242Sjkhint	readbdy = 1;			/* -r flag */
12088242Sjkhint	style = 0;			/* -s flag */
12188242Sjkhint	truncbdy = 1;			/* -t flag */
12288242Sjkhint	writebdy = 1;			/* -w flag */
12388242Sjkhlong	monitorstart = -1;		/* -m flag */
12488242Sjkhlong	monitorend = -1;		/* -m flag */
12588242Sjkhint	lite = 0;			/* -L flag */
12688242Sjkhlong	numops = -1;			/* -N flag */
12788242Sjkhint	randomoplen = 1;		/* -O flag disables it */
12888242Sjkhint	seed = 1;			/* -S flag */
129113949Sjkhint     mapped_writes = 1;	      /* -W flag disables */
13088242Sjkhint 	mapped_reads = 1;		/* -R flag disables it */
131260726Savgint     mapped_msync = 1;	      /* -U flag disables */
13288242Sjkhint	fsxgoodfd = 0;
13388242SjkhFILE *	fsxlogf = NULL;
13488242Sjkhint badoff = -1;
13588242Sjkhint closeopen = 0;
136260729Savgint invl = 0;
13788242Sjkh
13888242Sjkh
13988242Sjkhvoid
14088242Sjkhvwarnc(code, fmt, ap)
14188242Sjkh	int code;
14288242Sjkh	const char *fmt;
14388242Sjkh	va_list ap;
14488242Sjkh{
14588242Sjkh	fprintf(stderr, "fsx: ");
14688242Sjkh	if (fmt != NULL) {
14788242Sjkh		vfprintf(stderr, fmt, ap);
14888242Sjkh		fprintf(stderr, ": ");
14988242Sjkh	}
15088242Sjkh	fprintf(stderr, "%s\n", strerror(code));
15188242Sjkh}
15288242Sjkh
15388242Sjkh
15488242Sjkhvoid
15588242Sjkhwarn(const char * fmt, ...)
15688242Sjkh{
15788242Sjkh	va_list ap;
15888242Sjkh	va_start(ap, fmt);
15988242Sjkh	vwarnc(errno, fmt, ap);
16088242Sjkh	va_end(ap);
16188242Sjkh}
16288242Sjkh
16388242Sjkh
16488242Sjkhvoid
16588242Sjkhprt(char *fmt, ...)
16688242Sjkh{
16788242Sjkh	va_list args;
16888242Sjkh
16988242Sjkh	va_start(args, fmt);
17088242Sjkh	vfprintf(stdout, fmt, args);
171171046Sdelphij	va_end(args);
172171046Sdelphij
173171046Sdelphij	if (fsxlogf) {
174171046Sdelphij		va_start(args, fmt);
17588242Sjkh		vfprintf(fsxlogf, fmt, args);
176171046Sdelphij		va_end(args);
177171046Sdelphij	}
17888242Sjkh}
17988242Sjkh
18088242Sjkhvoid
18188242Sjkhprterr(char *prefix)
18288242Sjkh{
18388242Sjkh	prt("%s%s%s\n", prefix, prefix ? ": " : "", strerror(errno));
18488242Sjkh}
18588242Sjkh
18688242Sjkh
18788242Sjkhvoid
188260729Savgdo_log4(int operation, int arg0, int arg1, int arg2)
18988242Sjkh{
19088242Sjkh	struct log_entry *le;
19188242Sjkh
19288242Sjkh	le = &oplog[logptr];
19388242Sjkh	le->operation = operation;
19488242Sjkh	le->args[0] = arg0;
19588242Sjkh	le->args[1] = arg1;
19688242Sjkh	le->args[2] = arg2;
19788242Sjkh	logptr++;
19888242Sjkh	logcount++;
19988242Sjkh	if (logptr >= LOGSIZE)
20088242Sjkh		logptr = 0;
20188242Sjkh}
20288242Sjkh
20388242Sjkh
20488242Sjkhvoid
205260729Savglog4(int operation, int arg0, int arg1, int arg2)
206260729Savg{
207260729Savg	do_log4(operation, arg0, arg1, arg2);
208260729Savg	if (closeopen)
209260729Savg		do_log4(OP_CLOSEOPEN, 0, 0, 0);
210260729Savg	if (invl)
211260729Savg		do_log4(OP_INVALIDATE, 0, 0, 0);
212260729Savg}
213260729Savg
214260729Savg
215260729Savgvoid
21688242Sjkhlogdump(void)
21788242Sjkh{
21888242Sjkh	struct log_entry	*lp;
219260729Savg	int	i, count, down, opnum;
22088242Sjkh
22188242Sjkh	prt("LOG DUMP (%d total operations):\n", logcount);
22288242Sjkh	if (logcount < LOGSIZE) {
22388242Sjkh		i = 0;
22488242Sjkh		count = logcount;
22588242Sjkh	} else {
22688242Sjkh		i = logptr;
22788242Sjkh		count = LOGSIZE;
22888242Sjkh	}
229260729Savg
230260729Savg	opnum = i + 1 + (logcount/LOGSIZE)*LOGSIZE;
23188242Sjkh	for ( ; count > 0; count--) {
232260729Savg		lp = &oplog[i];
23388242Sjkh
234260729Savg		if (lp->operation == OP_CLOSEOPEN ||
235260729Savg		    lp->operation == OP_INVALIDATE) {
236260729Savg			switch (lp->operation) {
237260729Savg			case OP_CLOSEOPEN:
238260729Savg				prt("\t\tCLOSE/OPEN\n");
239260729Savg				break;
240260729Savg			case OP_INVALIDATE:
241260729Savg				prt("\t\tMS_INVALIDATE\n");
242260729Savg				break;
243260729Savg			}
244260729Savg			i++;
245260729Savg			if (i == LOGSIZE)
246260729Savg				i = 0;
247260729Savg			continue;
248260729Savg		}
249260729Savg
25088242Sjkh		prt("%d(%d mod 256): ", opnum, opnum%256);
25188242Sjkh		switch (lp->operation) {
25288242Sjkh		case OP_MAPREAD:
25388242Sjkh			prt("MAPREAD\t0x%x thru 0x%x\t(0x%x bytes)",
25488242Sjkh			    lp->args[0], lp->args[0] + lp->args[1] - 1,
25588242Sjkh			    lp->args[1]);
25688242Sjkh			if (badoff >= lp->args[0] && badoff <
25788242Sjkh						     lp->args[0] + lp->args[1])
25888242Sjkh				prt("\t***RRRR***");
25988242Sjkh			break;
26088242Sjkh		case OP_MAPWRITE:
26188242Sjkh			prt("MAPWRITE 0x%x thru 0x%x\t(0x%x bytes)",
26288242Sjkh			    lp->args[0], lp->args[0] + lp->args[1] - 1,
26388242Sjkh			    lp->args[1]);
26488242Sjkh			if (badoff >= lp->args[0] && badoff <
26588242Sjkh						     lp->args[0] + lp->args[1])
26688242Sjkh				prt("\t******WWWW");
26788242Sjkh			break;
26888242Sjkh		case OP_READ:
26988242Sjkh			prt("READ\t0x%x thru 0x%x\t(0x%x bytes)",
27088242Sjkh			    lp->args[0], lp->args[0] + lp->args[1] - 1,
27188242Sjkh			    lp->args[1]);
27288242Sjkh			if (badoff >= lp->args[0] &&
27388242Sjkh			    badoff < lp->args[0] + lp->args[1])
27488242Sjkh				prt("\t***RRRR***");
27588242Sjkh			break;
27688242Sjkh		case OP_WRITE:
27788242Sjkh			prt("WRITE\t0x%x thru 0x%x\t(0x%x bytes)",
27888242Sjkh			    lp->args[0], lp->args[0] + lp->args[1] - 1,
27988242Sjkh			    lp->args[1]);
28088242Sjkh			if (lp->args[0] > lp->args[2])
28188242Sjkh				prt(" HOLE");
28288242Sjkh			else if (lp->args[0] + lp->args[1] > lp->args[2])
28388242Sjkh				prt(" EXTEND");
28488242Sjkh			if ((badoff >= lp->args[0] || badoff >=lp->args[2]) &&
28588242Sjkh			    badoff < lp->args[0] + lp->args[1])
28688242Sjkh				prt("\t***WWWW");
28788242Sjkh			break;
28888242Sjkh		case OP_TRUNCATE:
28988242Sjkh			down = lp->args[0] < lp->args[1];
29088242Sjkh			prt("TRUNCATE %s\tfrom 0x%x to 0x%x",
29188242Sjkh			    down ? "DOWN" : "UP", lp->args[1], lp->args[0]);
29288242Sjkh			if (badoff >= lp->args[!down] &&
29388242Sjkh			    badoff < lp->args[!!down])
29488242Sjkh				prt("\t******WWWW");
29588242Sjkh			break;
29688242Sjkh		case OP_SKIPPED:
29788242Sjkh			prt("SKIPPED (no operation)");
29888242Sjkh			break;
29988242Sjkh		default:
30088242Sjkh			prt("BOGUS LOG ENTRY (operation code = %d)!",
30188242Sjkh			    lp->operation);
30288242Sjkh		}
30388242Sjkh		prt("\n");
304260729Savg		opnum++;
30588242Sjkh		i++;
30688242Sjkh		if (i == LOGSIZE)
30788242Sjkh			i = 0;
30888242Sjkh	}
30988242Sjkh}
31088242Sjkh
31188242Sjkh
31288242Sjkhvoid
31388242Sjkhsave_buffer(char *buffer, off_t bufferlength, int fd)
31488242Sjkh{
31588242Sjkh	off_t ret;
31688242Sjkh	ssize_t byteswritten;
31788242Sjkh
31888242Sjkh	if (fd <= 0 || bufferlength == 0)
31988242Sjkh		return;
32088242Sjkh
32188242Sjkh	if (bufferlength > SSIZE_MAX) {
32288242Sjkh		prt("fsx flaw: overflow in save_buffer\n");
32388242Sjkh		exit(67);
32488242Sjkh	}
32588242Sjkh	if (lite) {
32688242Sjkh		off_t size_by_seek = lseek(fd, (off_t)0, SEEK_END);
32788242Sjkh		if (size_by_seek == (off_t)-1)
32888242Sjkh			prterr("save_buffer: lseek eof");
32988242Sjkh		else if (bufferlength > size_by_seek) {
330113949Sjkh			warn("save_buffer: .fsxgood file too short... will save 0x%llx bytes instead of 0x%llx\n", (unsigned long long)size_by_seek,
33188242Sjkh			     (unsigned long long)bufferlength);
33288242Sjkh			bufferlength = size_by_seek;
33388242Sjkh		}
33488242Sjkh	}
33588242Sjkh
33688242Sjkh	ret = lseek(fd, (off_t)0, SEEK_SET);
33788242Sjkh	if (ret == (off_t)-1)
33888242Sjkh		prterr("save_buffer: lseek 0");
339113949Sjkh
34088242Sjkh	byteswritten = write(fd, buffer, (size_t)bufferlength);
34188242Sjkh	if (byteswritten != bufferlength) {
34288242Sjkh		if (byteswritten == -1)
34388242Sjkh			prterr("save_buffer write");
34488242Sjkh		else
345113949Sjkh			warn("save_buffer: short write, 0x%x bytes instead of 0x%llx\n",
34688242Sjkh			     (unsigned)byteswritten,
34788242Sjkh			     (unsigned long long)bufferlength);
34888242Sjkh	}
34988242Sjkh}
35088242Sjkh
35188242Sjkh
35288242Sjkhvoid
35388242Sjkhreport_failure(int status)
35488242Sjkh{
35588242Sjkh	logdump();
356113949Sjkh
35788242Sjkh	if (fsxgoodfd) {
35888242Sjkh		if (good_buf) {
35988242Sjkh			save_buffer(good_buf, file_size, fsxgoodfd);
36088242Sjkh			prt("Correct content saved for comparison\n");
36188242Sjkh			prt("(maybe hexdump \"%s\" vs \"%s.fsxgood\")\n",
36288242Sjkh			    fname, fname);
36388242Sjkh		}
36488242Sjkh		close(fsxgoodfd);
36588242Sjkh	}
36688242Sjkh	exit(status);
36788242Sjkh}
36888242Sjkh
36988242Sjkh
37088242Sjkh#define short_at(cp) ((unsigned short)((*((unsigned char *)(cp)) << 8) | \
371113949Sjkh					*(((unsigned char *)(cp)) + 1)))
37288242Sjkh
37388242Sjkhvoid
37488242Sjkhcheck_buffers(unsigned offset, unsigned size)
37588242Sjkh{
37688242Sjkh	unsigned char c, t;
37788242Sjkh	unsigned i = 0;
37888242Sjkh	unsigned n = 0;
37988242Sjkh	unsigned op = 0;
38088242Sjkh	unsigned bad = 0;
38188242Sjkh
38288242Sjkh	if (memcmp(good_buf + offset, temp_buf, size) != 0) {
38388242Sjkh		prt("READ BAD DATA: offset = 0x%x, size = 0x%x\n",
38488242Sjkh		    offset, size);
38588242Sjkh		prt("OFFSET\tGOOD\tBAD\tRANGE\n");
38688242Sjkh		while (size > 0) {
38788242Sjkh			c = good_buf[offset];
38888242Sjkh			t = temp_buf[i];
38988242Sjkh			if (c != t) {
390113949Sjkh				if (n == 0) {
39188242Sjkh					bad = short_at(&temp_buf[i]);
392113949Sjkh					prt("0x%5x\t0x%04x\t0x%04x", offset,
393113949Sjkh					    short_at(&good_buf[offset]), bad);
39488242Sjkh					op = temp_buf[offset & 1 ? i+1 : i];
39588242Sjkh				}
39688242Sjkh				n++;
39788242Sjkh				badoff = offset;
39888242Sjkh			}
39988242Sjkh			offset++;
40088242Sjkh			i++;
40188242Sjkh			size--;
40288242Sjkh		}
40388242Sjkh		if (n) {
404113949Sjkh			prt("\t0x%5x\n", n);
40588242Sjkh			if (bad)
406113949Sjkh				prt("operation# (mod 256) for the bad data may be %u\n", ((unsigned)op & 0xff));
40788242Sjkh			else
408113949Sjkh				prt("operation# (mod 256) for the bad data unknown, check HOLE and EXTEND ops\n");
40988242Sjkh		} else
410113949Sjkh			prt("????????????????\n");
41188242Sjkh		report_failure(110);
41288242Sjkh	}
41388242Sjkh}
41488242Sjkh
41588242Sjkh
41688242Sjkhvoid
41788242Sjkhcheck_size(void)
41888242Sjkh{
41988242Sjkh	struct stat	statbuf;
42088242Sjkh	off_t	size_by_seek;
42188242Sjkh
42288242Sjkh	if (fstat(fd, &statbuf)) {
42388242Sjkh		prterr("check_size: fstat");
42488242Sjkh		statbuf.st_size = -1;
42588242Sjkh	}
42688242Sjkh	size_by_seek = lseek(fd, (off_t)0, SEEK_END);
42788242Sjkh	if (file_size != statbuf.st_size || file_size != size_by_seek) {
42888242Sjkh		prt("Size error: expected 0x%llx stat 0x%llx seek 0x%llx\n",
42988242Sjkh		    (unsigned long long)file_size,
43088242Sjkh		    (unsigned long long)statbuf.st_size,
43188242Sjkh		    (unsigned long long)size_by_seek);
43288242Sjkh		report_failure(120);
43388242Sjkh	}
43488242Sjkh}
43588242Sjkh
43688242Sjkh
43788242Sjkhvoid
43888242Sjkhcheck_trunc_hack(void)
43988242Sjkh{
44088242Sjkh	struct stat statbuf;
44188242Sjkh
44288242Sjkh	ftruncate(fd, (off_t)0);
44388242Sjkh	ftruncate(fd, (off_t)100000);
44488242Sjkh	fstat(fd, &statbuf);
44588242Sjkh	if (statbuf.st_size != (off_t)100000) {
44688242Sjkh		prt("no extend on truncate! not posix!\n");
44788242Sjkh		exit(130);
44888242Sjkh	}
449113949Sjkh	ftruncate(fd, (off_t)0);
45088242Sjkh}
45188242Sjkh
45288242Sjkh
45388242Sjkhvoid
45488242Sjkhdoread(unsigned offset, unsigned size)
45588242Sjkh{
45688242Sjkh	off_t ret;
45788242Sjkh	unsigned iret;
45888242Sjkh
45988242Sjkh	offset -= offset % readbdy;
46088242Sjkh	if (size == 0) {
46188242Sjkh		if (!quiet && testcalls > simulatedopcount)
46288242Sjkh			prt("skipping zero size read\n");
46388242Sjkh		log4(OP_SKIPPED, OP_READ, offset, size);
46488242Sjkh		return;
46588242Sjkh	}
46688242Sjkh	if (size + offset > file_size) {
46788242Sjkh		if (!quiet && testcalls > simulatedopcount)
46888242Sjkh			prt("skipping seek/read past end of file\n");
46988242Sjkh		log4(OP_SKIPPED, OP_READ, offset, size);
47088242Sjkh		return;
47188242Sjkh	}
47288242Sjkh
47388242Sjkh	log4(OP_READ, offset, size, 0);
47488242Sjkh
47588242Sjkh	if (testcalls <= simulatedopcount)
47688242Sjkh		return;
47788242Sjkh
47888242Sjkh	if (!quiet && ((progressinterval &&
47988242Sjkh			testcalls % progressinterval == 0) ||
48088242Sjkh		       (debug &&
481113949Sjkh			(monitorstart == -1 ||
48288242Sjkh			 (offset + size > monitorstart &&
48388242Sjkh			  (monitorend == -1 || offset <= monitorend))))))
48488242Sjkh		prt("%lu read\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
48588242Sjkh		    offset, offset + size - 1, size);
48688242Sjkh	ret = lseek(fd, (off_t)offset, SEEK_SET);
48788242Sjkh	if (ret == (off_t)-1) {
48888242Sjkh		prterr("doread: lseek");
48988242Sjkh		report_failure(140);
49088242Sjkh	}
49188242Sjkh	iret = read(fd, temp_buf, size);
49288242Sjkh	if (iret != size) {
49388242Sjkh		if (iret == -1)
49488242Sjkh			prterr("doread: read");
49588242Sjkh		else
49688242Sjkh			prt("short read: 0x%x bytes instead of 0x%x\n",
49788242Sjkh			    iret, size);
49888242Sjkh		report_failure(141);
49988242Sjkh	}
50088242Sjkh	check_buffers(offset, size);
50188242Sjkh}
50288242Sjkh
50388242Sjkh
50488242Sjkhvoid
505113949Sjkhcheck_eofpage(char *s, unsigned offset, char *p, int size)
506113949Sjkh{
507160985Sjb	uintptr_t last_page, should_be_zero;
508113949Sjkh
509113949Sjkh	if (offset + size <= (file_size & ~page_mask))
510113949Sjkh		return;
511113949Sjkh	/*
512113949Sjkh	 * we landed in the last page of the file
513113949Sjkh	 * test to make sure the VM system provided 0's
514113949Sjkh	 * beyond the true end of the file mapping
515113949Sjkh	 * (as required by mmap def in 1996 posix 1003.1)
516113949Sjkh	 */
517160985Sjb	last_page = ((uintptr_t)p + (offset & page_mask) + size) & ~page_mask;
518113949Sjkh
519113949Sjkh	for (should_be_zero = last_page + (file_size & page_mask);
520113949Sjkh	     should_be_zero < last_page + page_size;
521113949Sjkh	     should_be_zero++)
522113949Sjkh		if (*(char *)should_be_zero) {
523113949Sjkh			prt("Mapped %s: non-zero data past EOF (0x%llx) page offset 0x%x is 0x%04x\n",
524113949Sjkh			    s, file_size - 1, should_be_zero & page_mask,
525113949Sjkh			    short_at(should_be_zero));
526113949Sjkh			report_failure(205);
527113949Sjkh		}
528113949Sjkh}
529113949Sjkh
530113949Sjkh
531113949Sjkhvoid
53288242Sjkhdomapread(unsigned offset, unsigned size)
53388242Sjkh{
53488242Sjkh	unsigned pg_offset;
53588242Sjkh	unsigned map_size;
53688242Sjkh	char    *p;
53788242Sjkh
53888242Sjkh	offset -= offset % readbdy;
53988242Sjkh	if (size == 0) {
54088242Sjkh		if (!quiet && testcalls > simulatedopcount)
54188242Sjkh			prt("skipping zero size read\n");
54288242Sjkh		log4(OP_SKIPPED, OP_MAPREAD, offset, size);
54388242Sjkh		return;
54488242Sjkh	}
54588242Sjkh	if (size + offset > file_size) {
54688242Sjkh		if (!quiet && testcalls > simulatedopcount)
54788242Sjkh			prt("skipping seek/read past end of file\n");
54888242Sjkh		log4(OP_SKIPPED, OP_MAPREAD, offset, size);
54988242Sjkh		return;
55088242Sjkh	}
55188242Sjkh
55288242Sjkh	log4(OP_MAPREAD, offset, size, 0);
55388242Sjkh
55488242Sjkh	if (testcalls <= simulatedopcount)
55588242Sjkh		return;
55688242Sjkh
55788242Sjkh	if (!quiet && ((progressinterval &&
55888242Sjkh			testcalls % progressinterval == 0) ||
55988242Sjkh		       (debug &&
560113949Sjkh			(monitorstart == -1 ||
56188242Sjkh			 (offset + size > monitorstart &&
56288242Sjkh			  (monitorend == -1 || offset <= monitorend))))))
56388242Sjkh		prt("%lu mapread\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
56488242Sjkh		    offset, offset + size - 1, size);
56588242Sjkh
56688242Sjkh	pg_offset = offset & page_mask;
56788242Sjkh	map_size  = pg_offset + size;
56888242Sjkh
569113949Sjkh	if ((p = (char *)mmap(0, map_size, PROT_READ, MAP_FILE | MAP_SHARED, fd,
57088242Sjkh			      (off_t)(offset - pg_offset))) == (char *)-1) {
571113949Sjkh		prterr("domapread: mmap");
57288242Sjkh		report_failure(190);
57388242Sjkh	}
57488242Sjkh	memcpy(temp_buf, p + pg_offset, size);
575113949Sjkh
576113949Sjkh	check_eofpage("Read", offset, p, size);
577113949Sjkh
57888242Sjkh	if (munmap(p, map_size) != 0) {
57988242Sjkh		prterr("domapread: munmap");
58088242Sjkh		report_failure(191);
58188242Sjkh	}
58288242Sjkh
58388242Sjkh	check_buffers(offset, size);
58488242Sjkh}
58588242Sjkh
58688242Sjkh
58788242Sjkhvoid
58888242Sjkhgendata(char *original_buf, char *good_buf, unsigned offset, unsigned size)
58988242Sjkh{
59088242Sjkh	while (size--) {
591113949Sjkh		good_buf[offset] = testcalls % 256;
59288242Sjkh		if (offset % 2)
59388242Sjkh			good_buf[offset] += original_buf[offset];
59488242Sjkh		offset++;
59588242Sjkh	}
59688242Sjkh}
59788242Sjkh
59888242Sjkh
59988242Sjkhvoid
60088242Sjkhdowrite(unsigned offset, unsigned size)
60188242Sjkh{
60288242Sjkh	off_t ret;
60388242Sjkh	unsigned iret;
60488242Sjkh
60588242Sjkh	offset -= offset % writebdy;
60688242Sjkh	if (size == 0) {
60788242Sjkh		if (!quiet && testcalls > simulatedopcount)
60888242Sjkh			prt("skipping zero size write\n");
60988242Sjkh		log4(OP_SKIPPED, OP_WRITE, offset, size);
61088242Sjkh		return;
61188242Sjkh	}
61288242Sjkh
61388242Sjkh	log4(OP_WRITE, offset, size, file_size);
61488242Sjkh
61588242Sjkh	gendata(original_buf, good_buf, offset, size);
61688242Sjkh	if (file_size < offset + size) {
61788242Sjkh		if (file_size < offset)
61888242Sjkh			memset(good_buf + file_size, '\0', offset - file_size);
61988242Sjkh		file_size = offset + size;
62088242Sjkh		if (lite) {
62188242Sjkh			warn("Lite file size bug in fsx!");
62288242Sjkh			report_failure(149);
62388242Sjkh		}
62488242Sjkh	}
62588242Sjkh
62688242Sjkh	if (testcalls <= simulatedopcount)
62788242Sjkh		return;
62888242Sjkh
62988242Sjkh	if (!quiet && ((progressinterval &&
63088242Sjkh			testcalls % progressinterval == 0) ||
63188242Sjkh		       (debug &&
632113949Sjkh			(monitorstart == -1 ||
63388242Sjkh			 (offset + size > monitorstart &&
63488242Sjkh			  (monitorend == -1 || offset <= monitorend))))))
63588242Sjkh		prt("%lu write\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
63688242Sjkh		    offset, offset + size - 1, size);
63788242Sjkh	ret = lseek(fd, (off_t)offset, SEEK_SET);
63888242Sjkh	if (ret == (off_t)-1) {
63988242Sjkh		prterr("dowrite: lseek");
64088242Sjkh		report_failure(150);
64188242Sjkh	}
64288242Sjkh	iret = write(fd, good_buf + offset, size);
64388242Sjkh	if (iret != size) {
64488242Sjkh		if (iret == -1)
64588242Sjkh			prterr("dowrite: write");
64688242Sjkh		else
64788242Sjkh			prt("short write: 0x%x bytes instead of 0x%x\n",
64888242Sjkh			    iret, size);
64988242Sjkh		report_failure(151);
65088242Sjkh	}
65188242Sjkh}
65288242Sjkh
65388242Sjkh
65488242Sjkhvoid
65588242Sjkhdomapwrite(unsigned offset, unsigned size)
65688242Sjkh{
65788242Sjkh	unsigned pg_offset;
65888242Sjkh	unsigned map_size;
65988242Sjkh	off_t    cur_filesize;
66088242Sjkh	char    *p;
66188242Sjkh
66288242Sjkh	offset -= offset % writebdy;
66388242Sjkh	if (size == 0) {
66488242Sjkh		if (!quiet && testcalls > simulatedopcount)
66588242Sjkh			prt("skipping zero size write\n");
66688242Sjkh		log4(OP_SKIPPED, OP_MAPWRITE, offset, size);
66788242Sjkh		return;
66888242Sjkh	}
66988242Sjkh	cur_filesize = file_size;
67088242Sjkh
67188242Sjkh	log4(OP_MAPWRITE, offset, size, 0);
67288242Sjkh
67388242Sjkh	gendata(original_buf, good_buf, offset, size);
67488242Sjkh	if (file_size < offset + size) {
67588242Sjkh		if (file_size < offset)
67688242Sjkh			memset(good_buf + file_size, '\0', offset - file_size);
67788242Sjkh		file_size = offset + size;
67888242Sjkh		if (lite) {
67988242Sjkh			warn("Lite file size bug in fsx!");
68088242Sjkh			report_failure(200);
68188242Sjkh		}
68288242Sjkh	}
68388242Sjkh
68488242Sjkh	if (testcalls <= simulatedopcount)
68588242Sjkh		return;
68688242Sjkh
68788242Sjkh	if (!quiet && ((progressinterval &&
68888242Sjkh			testcalls % progressinterval == 0) ||
68988242Sjkh		       (debug &&
690113949Sjkh			(monitorstart == -1 ||
69188242Sjkh			 (offset + size > monitorstart &&
69288242Sjkh			  (monitorend == -1 || offset <= monitorend))))))
69388242Sjkh		prt("%lu mapwrite\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
69488242Sjkh		    offset, offset + size - 1, size);
69588242Sjkh
69688242Sjkh	if (file_size > cur_filesize) {
697113949Sjkh		if (ftruncate(fd, file_size) == -1) {
698113949Sjkh			prterr("domapwrite: ftruncate");
69988242Sjkh			exit(201);
70088242Sjkh		}
70188242Sjkh	}
70288242Sjkh	pg_offset = offset & page_mask;
70388242Sjkh	map_size  = pg_offset + size;
70488242Sjkh
70588242Sjkh	if ((p = (char *)mmap(0, map_size, PROT_READ | PROT_WRITE,
70688242Sjkh			      MAP_FILE | MAP_SHARED, fd,
707260726Savg			      (off_t)(offset - pg_offset))) == MAP_FAILED) {
708113949Sjkh		prterr("domapwrite: mmap");
70988242Sjkh		report_failure(202);
71088242Sjkh	}
71188242Sjkh	memcpy(p + pg_offset, good_buf + offset, size);
712260726Savg	if (mapped_msync && msync(p, map_size, MS_SYNC) != 0) {
71388242Sjkh		prterr("domapwrite: msync");
71488242Sjkh		report_failure(203);
71588242Sjkh	}
716113949Sjkh
717113949Sjkh	check_eofpage("Write", offset, p, size);
718113949Sjkh
71988242Sjkh	if (munmap(p, map_size) != 0) {
72088242Sjkh		prterr("domapwrite: munmap");
72188242Sjkh		report_failure(204);
72288242Sjkh	}
72388242Sjkh}
72488242Sjkh
72588242Sjkh
72688242Sjkhvoid
72788242Sjkhdotruncate(unsigned size)
72888242Sjkh{
72988242Sjkh	int oldsize = file_size;
73088242Sjkh
73188242Sjkh	size -= size % truncbdy;
73288242Sjkh	if (size > biggest) {
73388242Sjkh		biggest = size;
73488242Sjkh		if (!quiet && testcalls > simulatedopcount)
73588242Sjkh			prt("truncating to largest ever: 0x%x\n", size);
73688242Sjkh	}
73788242Sjkh
73888242Sjkh	log4(OP_TRUNCATE, size, (unsigned)file_size, 0);
73988242Sjkh
74088242Sjkh	if (size > file_size)
74188242Sjkh		memset(good_buf + file_size, '\0', size - file_size);
74288242Sjkh	file_size = size;
74388242Sjkh
74488242Sjkh	if (testcalls <= simulatedopcount)
74588242Sjkh		return;
746113949Sjkh
74788242Sjkh	if ((progressinterval && testcalls % progressinterval == 0) ||
74888242Sjkh	    (debug && (monitorstart == -1 || monitorend == -1 ||
74988242Sjkh		       size <= monitorend)))
750113949Sjkh		prt("%lu trunc\tfrom 0x%x to 0x%x\n", testcalls, oldsize, size);
75188242Sjkh	if (ftruncate(fd, (off_t)size) == -1) {
752113949Sjkh		prt("ftruncate1: %x\n", size);
75388242Sjkh		prterr("dotruncate: ftruncate");
75488242Sjkh		report_failure(160);
75588242Sjkh	}
75688242Sjkh}
75788242Sjkh
75888242Sjkh
75988242Sjkhvoid
76088242Sjkhwritefileimage()
76188242Sjkh{
76288242Sjkh	ssize_t iret;
76388242Sjkh
76488242Sjkh	if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
76588242Sjkh		prterr("writefileimage: lseek");
76688242Sjkh		report_failure(171);
76788242Sjkh	}
76888242Sjkh	iret = write(fd, good_buf, file_size);
76988242Sjkh	if ((off_t)iret != file_size) {
77088242Sjkh		if (iret == -1)
77188242Sjkh			prterr("writefileimage: write");
77288242Sjkh		else
77388242Sjkh			prt("short write: 0x%x bytes instead of 0x%llx\n",
77488242Sjkh			    iret, (unsigned long long)file_size);
77588242Sjkh		report_failure(172);
77688242Sjkh	}
77788242Sjkh	if (lite ? 0 : ftruncate(fd, file_size) == -1) {
778113949Sjkh		prt("ftruncate2: %llx\n", (unsigned long long)file_size);
77988242Sjkh		prterr("writefileimage: ftruncate");
78088242Sjkh		report_failure(173);
78188242Sjkh	}
78288242Sjkh}
78388242Sjkh
78488242Sjkh
78588242Sjkhvoid
78688242Sjkhdocloseopen(void)
787113949Sjkh{
78888242Sjkh	if (testcalls <= simulatedopcount)
78988242Sjkh		return;
79088242Sjkh
79188242Sjkh	if (debug)
79288242Sjkh		prt("%lu close/open\n", testcalls);
79388242Sjkh	if (close(fd)) {
79488242Sjkh		prterr("docloseopen: close");
79588242Sjkh		report_failure(180);
79688242Sjkh	}
79788242Sjkh	fd = open(fname, O_RDWR, 0);
79888242Sjkh	if (fd < 0) {
79988242Sjkh		prterr("docloseopen: open");
80088242Sjkh		report_failure(181);
80188242Sjkh	}
80288242Sjkh}
80388242Sjkh
80488242Sjkh
80588242Sjkhvoid
806260729Savgdoinvl(void)
807260729Savg{
808260729Savg	char *p;
809260729Savg
810260729Savg	if (file_size == 0)
811260729Savg		return;
812260729Savg	if (testcalls <= simulatedopcount)
813260729Savg		return;
814260729Savg	if (debug)
815260729Savg		prt("%lu msync(MS_INVALIDATE)\n", testcalls);
816260729Savg
817260729Savg	if ((p = (char *)mmap(0, file_size, PROT_READ | PROT_WRITE,
818260729Savg			      MAP_FILE | MAP_SHARED, fd, 0)) == MAP_FAILED) {
819260729Savg		prterr("doinvl: mmap");
820260729Savg		report_failure(205);
821260729Savg	}
822260729Savg
823260729Savg	if (msync(p, 0, MS_SYNC | MS_INVALIDATE) != 0) {
824260729Savg		prterr("doinvl: msync");
825260729Savg		report_failure(206);
826260729Savg	}
827260729Savg
828260729Savg	if (munmap(p, file_size) != 0) {
829260729Savg		prterr("doinvl: munmap");
830260729Savg		report_failure(207);
831260729Savg	}
832260729Savg}
833260729Savg
834260729Savg
835260729Savgvoid
83688242Sjkhtest(void)
83788242Sjkh{
83888242Sjkh	unsigned long	offset;
83988242Sjkh	unsigned long	size = maxoplen;
84088242Sjkh	unsigned long	rv = random();
84188242Sjkh	unsigned long	op = rv % (3 + !lite + mapped_writes);
84288242Sjkh
843113949Sjkh	/* turn off the map read if necessary */
84488242Sjkh
845113949Sjkh	if (op == 2 && !mapped_reads)
846113949Sjkh	    op = 0;
84788242Sjkh
84888242Sjkh	if (simulatedopcount > 0 && testcalls == simulatedopcount)
84988242Sjkh		writefileimage();
85088242Sjkh
85188242Sjkh	testcalls++;
85288242Sjkh
85388242Sjkh	if (closeprob)
85488242Sjkh		closeopen = (rv >> 3) < (1 << 28) / closeprob;
855260729Savg	if (invlprob)
856260729Savg		invl = (rv >> 3) < (1 << 28) / invlprob;
85788242Sjkh
85888242Sjkh	if (debugstart > 0 && testcalls >= debugstart)
85988242Sjkh		debug = 1;
86088242Sjkh
86188242Sjkh	if (!quiet && testcalls < simulatedopcount && testcalls % 100000 == 0)
86288242Sjkh		prt("%lu...\n", testcalls);
86388242Sjkh
86488242Sjkh	/*
86588242Sjkh	 * READ:	op = 0
86688242Sjkh	 * WRITE:	op = 1
86788242Sjkh	 * MAPREAD:     op = 2
86888242Sjkh	 * TRUNCATE:	op = 3
86988242Sjkh	 * MAPWRITE:    op = 3 or 4
87088242Sjkh	 */
871113949Sjkh	if (lite ? 0 : op == 3 && style == 0) /* vanilla truncate? */
87288242Sjkh		dotruncate(random() % maxfilelen);
87388242Sjkh	else {
87488242Sjkh		if (randomoplen)
87588242Sjkh			size = random() % (maxoplen+1);
87688242Sjkh		if (lite ? 0 : op == 3)
87788242Sjkh			dotruncate(size);
87888242Sjkh		else {
87988242Sjkh			offset = random();
88088242Sjkh			if (op == 1 || op == (lite ? 3 : 4)) {
88188242Sjkh				offset %= maxfilelen;
88288242Sjkh				if (offset + size > maxfilelen)
88388242Sjkh					size = maxfilelen - offset;
88488242Sjkh				if (op != 1)
88588242Sjkh					domapwrite(offset, size);
88688242Sjkh				else
88788242Sjkh					dowrite(offset, size);
88888242Sjkh			} else {
88988242Sjkh				if (file_size)
89088242Sjkh					offset %= file_size;
89188242Sjkh				else
89288242Sjkh					offset = 0;
89388242Sjkh				if (offset + size > file_size)
89488242Sjkh					size = file_size - offset;
89588242Sjkh				if (op != 0)
89688242Sjkh					domapread(offset, size);
89788242Sjkh				else
89888242Sjkh					doread(offset, size);
89988242Sjkh			}
90088242Sjkh		}
90188242Sjkh	}
90288242Sjkh	if (sizechecks && testcalls > simulatedopcount)
90388242Sjkh		check_size();
904260729Savg	if (invl)
905260729Savg		doinvl();
90688242Sjkh	if (closeopen)
90788242Sjkh		docloseopen();
90888242Sjkh}
90988242Sjkh
91088242Sjkh
91188242Sjkhvoid
91288242Sjkhcleanup(sig)
91388242Sjkh	int	sig;
91488242Sjkh{
91588242Sjkh	if (sig)
91688242Sjkh		prt("signal %d\n", sig);
91788242Sjkh	prt("testcalls = %lu\n", testcalls);
91888242Sjkh	exit(sig);
91988242Sjkh}
92088242Sjkh
92188242Sjkh
92288242Sjkhvoid
92388242Sjkhusage(void)
92488242Sjkh{
92588242Sjkh	fprintf(stdout, "usage: %s",
926113949Sjkh		"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\
92788242Sjkh	-b opnum: beginning operation number (default 1)\n\
92888242Sjkh	-c P: 1 in P chance of file close+open at each op (default infinity)\n\
92988242Sjkh	-d: debug output for all operations\n\
930260729Savg	-i P: 1 in P chance of calling msync(MS_INVALIDATE) (default infinity)\n\
93188242Sjkh	-l flen: the upper bound on file size (default 262144)\n\
932113949Sjkh	-m startop:endop: monitor (print debug output) specified byte range (default 0:infinity)\n\
93388242Sjkh	-n: no verifications of file size\n\
93488242Sjkh	-o oplen: the upper bound on operation size (default 65536)\n\
93588242Sjkh	-p progressinterval: debug output at specified operation interval\n\
93688242Sjkh	-q: quieter operation\n\
93788242Sjkh	-r readbdy: 4096 would make reads page aligned (default 1)\n\
93888242Sjkh	-s style: 1 gives smaller truncates (default 0)\n\
93988242Sjkh	-t truncbdy: 4096 would make truncates page aligned (default 1)\n\
94088242Sjkh	-w writebdy: 4096 would make writes page aligned (default 1)\n\
94188242Sjkh	-D startingop: debug output starting at specified operation\n\
94288242Sjkh	-L: fsxLite - no file creations & no file size changes\n\
94388242Sjkh	-N numops: total # operations to do (default infinity)\n\
94488242Sjkh	-O: use oplen (see -o flag) for every op (default random)\n\
945113949Sjkh	-P dirpath: save .fsxlog and .fsxgood files in dirpath (default ./)\n\
94688242Sjkh	-S seed: for random # generator (default 1) 0 gets timestamp\n\
94788242Sjkh	-W: mapped write operations DISabled\n\
948113949Sjkh	-R: mapped read operations DISabled)\n\
949260726Savg	-U: msync after mapped write operations DISabled\n\
95088242Sjkh	fname: this filename is REQUIRED (no default)\n");
95188242Sjkh	exit(90);
95288242Sjkh}
95388242Sjkh
95488242Sjkh
95588242Sjkhint
95688242Sjkhgetnum(char *s, char **e)
95788242Sjkh{
95888242Sjkh	int ret = -1;
95988242Sjkh
96088242Sjkh	*e = (char *) 0;
96188242Sjkh	ret = strtol(s, e, 0);
96288242Sjkh	if (*e)
96388242Sjkh		switch (**e) {
96488242Sjkh		case 'b':
96588242Sjkh		case 'B':
96688242Sjkh			ret *= 512;
96788242Sjkh			*e = *e + 1;
96888242Sjkh			break;
96988242Sjkh		case 'k':
97088242Sjkh		case 'K':
97188242Sjkh			ret *= 1024;
97288242Sjkh			*e = *e + 1;
97388242Sjkh			break;
97488242Sjkh		case 'm':
97588242Sjkh		case 'M':
97688242Sjkh			ret *= 1024*1024;
97788242Sjkh			*e = *e + 1;
97888242Sjkh			break;
97988242Sjkh		case 'w':
98088242Sjkh		case 'W':
98188242Sjkh			ret *= 4;
98288242Sjkh			*e = *e + 1;
98388242Sjkh			break;
98488242Sjkh		}
98588242Sjkh	return (ret);
98688242Sjkh}
98788242Sjkh
98888242Sjkh
98988242Sjkhint
99088242Sjkhmain(int argc, char **argv)
99188242Sjkh{
992113949Sjkh	int	i, ch;
99388242Sjkh	char	*endp;
99488242Sjkh	char goodfile[1024];
99588242Sjkh	char logfile[1024];
99688242Sjkh
99788242Sjkh	goodfile[0] = 0;
99888242Sjkh	logfile[0] = 0;
99988242Sjkh
100088242Sjkh	page_size = getpagesize();
100188242Sjkh	page_mask = page_size - 1;
100288242Sjkh
100388242Sjkh	setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */
100488242Sjkh
1005260726Savg	while ((ch = getopt(argc, argv,
1006260729Savg	    "b:c:di:l:m:no:p:qr:s:t:w:D:LN:OP:RS:UW")) != -1)
100788242Sjkh		switch (ch) {
100888242Sjkh		case 'b':
100988242Sjkh			simulatedopcount = getnum(optarg, &endp);
101088242Sjkh			if (!quiet)
1011113949Sjkh				fprintf(stdout, "Will begin at operation %ld\n",
101288242Sjkh					simulatedopcount);
101388242Sjkh			if (simulatedopcount == 0)
101488242Sjkh				usage();
101588242Sjkh			simulatedopcount -= 1;
101688242Sjkh			break;
101788242Sjkh		case 'c':
101888242Sjkh			closeprob = getnum(optarg, &endp);
101988242Sjkh			if (!quiet)
102088242Sjkh				fprintf(stdout,
102188242Sjkh					"Chance of close/open is 1 in %d\n",
102288242Sjkh					closeprob);
102388242Sjkh			if (closeprob <= 0)
102488242Sjkh				usage();
102588242Sjkh			break;
102688242Sjkh		case 'd':
102788242Sjkh			debug = 1;
102888242Sjkh			break;
1029260729Savg		case 'i':
1030260729Savg			invlprob = getnum(optarg, &endp);
1031260729Savg			if (!quiet)
1032260729Savg				fprintf(stdout,
1033260729Savg					"Chance of MS_INVALIDATE is 1 in %d\n",
1034260729Savg					invlprob);
1035260729Savg			if (invlprob <= 0)
1036260729Savg				usage();
1037260729Savg			break;
103888242Sjkh		case 'l':
103988242Sjkh			maxfilelen = getnum(optarg, &endp);
104088242Sjkh			if (maxfilelen <= 0)
104188242Sjkh				usage();
104288242Sjkh			break;
104388242Sjkh		case 'm':
104488242Sjkh			monitorstart = getnum(optarg, &endp);
104588242Sjkh			if (monitorstart < 0)
104688242Sjkh				usage();
104788242Sjkh			if (!endp || *endp++ != ':')
104888242Sjkh				usage();
104988242Sjkh			monitorend = getnum(endp, &endp);
105088242Sjkh			if (monitorend < 0)
105188242Sjkh				usage();
105288242Sjkh			if (monitorend == 0)
105388242Sjkh				monitorend = -1; /* aka infinity */
105488242Sjkh			debug = 1;
105588242Sjkh		case 'n':
105688242Sjkh			sizechecks = 0;
105788242Sjkh			break;
105888242Sjkh		case 'o':
105988242Sjkh			maxoplen = getnum(optarg, &endp);
106088242Sjkh			if (maxoplen <= 0)
106188242Sjkh				usage();
106288242Sjkh			break;
106388242Sjkh		case 'p':
106488242Sjkh			progressinterval = getnum(optarg, &endp);
106588242Sjkh			if (progressinterval < 0)
106688242Sjkh				usage();
106788242Sjkh			break;
106888242Sjkh		case 'q':
106988242Sjkh			quiet = 1;
107088242Sjkh			break;
107188242Sjkh		case 'r':
107288242Sjkh			readbdy = getnum(optarg, &endp);
107388242Sjkh			if (readbdy <= 0)
107488242Sjkh				usage();
107588242Sjkh			break;
107688242Sjkh		case 's':
107788242Sjkh			style = getnum(optarg, &endp);
107888242Sjkh			if (style < 0 || style > 1)
107988242Sjkh				usage();
108088242Sjkh			break;
108188242Sjkh		case 't':
108288242Sjkh			truncbdy = getnum(optarg, &endp);
108388242Sjkh			if (truncbdy <= 0)
108488242Sjkh				usage();
108588242Sjkh			break;
108688242Sjkh		case 'w':
108788242Sjkh			writebdy = getnum(optarg, &endp);
108888242Sjkh			if (writebdy <= 0)
108988242Sjkh				usage();
109088242Sjkh			break;
109188242Sjkh		case 'D':
109288242Sjkh			debugstart = getnum(optarg, &endp);
109388242Sjkh			if (debugstart < 1)
109488242Sjkh				usage();
109588242Sjkh			break;
109688242Sjkh		case 'L':
1097113949Sjkh			lite = 1;
109888242Sjkh			break;
109988242Sjkh		case 'N':
110088242Sjkh			numops = getnum(optarg, &endp);
110188242Sjkh			if (numops < 0)
110288242Sjkh				usage();
110388242Sjkh			break;
110488242Sjkh		case 'O':
110588242Sjkh			randomoplen = 0;
110688242Sjkh			break;
110788242Sjkh		case 'P':
110888242Sjkh			strncpy(goodfile, optarg, sizeof(goodfile));
110988242Sjkh			strcat(goodfile, "/");
111088242Sjkh			strncpy(logfile, optarg, sizeof(logfile));
111188242Sjkh			strcat(logfile, "/");
111288242Sjkh			break;
1113113949Sjkh		case 'R':
1114113949Sjkh			mapped_reads = 0;
1115113949Sjkh			break;
111688242Sjkh		case 'S':
1117113949Sjkh			seed = getnum(optarg, &endp);
111888242Sjkh			if (seed == 0)
111988242Sjkh				seed = time(0) % 10000;
112088242Sjkh			if (!quiet)
112188242Sjkh				fprintf(stdout, "Seed set to %d\n", seed);
112288242Sjkh			if (seed < 0)
112388242Sjkh				usage();
112488242Sjkh			break;
112588242Sjkh		case 'W':
1126113949Sjkh			mapped_writes = 0;
112788242Sjkh			if (!quiet)
112888242Sjkh				fprintf(stdout, "mapped writes DISABLED\n");
112988242Sjkh			break;
1130260726Savg		case 'U':
1131260726Savg			mapped_msync = 0;
1132260726Savg			if (!quiet)
1133260726Savg				fprintf(stdout, "mapped msync DISABLED\n");
1134260726Savg			break;
113588242Sjkh
113688242Sjkh		default:
113788242Sjkh			usage();
113888242Sjkh			/* NOTREACHED */
113988242Sjkh		}
114088242Sjkh	argc -= optind;
114188242Sjkh	argv += optind;
114288242Sjkh	if (argc != 1)
114388242Sjkh		usage();
114488242Sjkh	fname = argv[0];
114588242Sjkh
114688242Sjkh	signal(SIGHUP,	cleanup);
114788242Sjkh	signal(SIGINT,	cleanup);
114888242Sjkh	signal(SIGPIPE,	cleanup);
114988242Sjkh	signal(SIGALRM,	cleanup);
115088242Sjkh	signal(SIGTERM,	cleanup);
115188242Sjkh	signal(SIGXCPU,	cleanup);
115288242Sjkh	signal(SIGXFSZ,	cleanup);
115388242Sjkh	signal(SIGVTALRM,	cleanup);
115488242Sjkh	signal(SIGUSR1,	cleanup);
115588242Sjkh	signal(SIGUSR2,	cleanup);
115688242Sjkh
115788242Sjkh	initstate(seed, state, 256);
115888242Sjkh	setstate(state);
115988242Sjkh	fd = open(fname, O_RDWR|(lite ? 0 : O_CREAT|O_TRUNC), 0666);
116088242Sjkh	if (fd < 0) {
116188242Sjkh		prterr(fname);
116288242Sjkh		exit(91);
116388242Sjkh	}
116488242Sjkh	strncat(goodfile, fname, 256);
116588242Sjkh	strcat (goodfile, ".fsxgood");
116688242Sjkh	fsxgoodfd = open(goodfile, O_RDWR|O_CREAT|O_TRUNC, 0666);
116788242Sjkh	if (fsxgoodfd < 0) {
116888242Sjkh		prterr(goodfile);
116988242Sjkh		exit(92);
117088242Sjkh	}
117188242Sjkh	strncat(logfile, fname, 256);
117288242Sjkh	strcat (logfile, ".fsxlog");
117388242Sjkh	fsxlogf = fopen(logfile, "w");
117488242Sjkh	if (fsxlogf == NULL) {
117588242Sjkh		prterr(logfile);
117688242Sjkh		exit(93);
117788242Sjkh	}
117888242Sjkh	if (lite) {
117988242Sjkh		off_t ret;
118088242Sjkh		file_size = maxfilelen = lseek(fd, (off_t)0, SEEK_END);
118188242Sjkh		if (file_size == (off_t)-1) {
118288242Sjkh			prterr(fname);
118388242Sjkh			warn("main: lseek eof");
118488242Sjkh			exit(94);
118588242Sjkh		}
118688242Sjkh		ret = lseek(fd, (off_t)0, SEEK_SET);
118788242Sjkh		if (ret == (off_t)-1) {
118888242Sjkh			prterr(fname);
118988242Sjkh			warn("main: lseek 0");
119088242Sjkh			exit(95);
119188242Sjkh		}
119288242Sjkh	}
119388242Sjkh	original_buf = (char *) malloc(maxfilelen);
119488242Sjkh	for (i = 0; i < maxfilelen; i++)
119588242Sjkh		original_buf[i] = random() % 256;
119688242Sjkh	good_buf = (char *) malloc(maxfilelen);
119788242Sjkh	memset(good_buf, '\0', maxfilelen);
119888242Sjkh	temp_buf = (char *) malloc(maxoplen);
119988242Sjkh	memset(temp_buf, '\0', maxoplen);
120088242Sjkh	if (lite) {	/* zero entire existing file */
120188242Sjkh		ssize_t written;
120288242Sjkh
120388242Sjkh		written = write(fd, good_buf, (size_t)maxfilelen);
120488242Sjkh		if (written != maxfilelen) {
120588242Sjkh			if (written == -1) {
120688242Sjkh				prterr(fname);
120788242Sjkh				warn("main: error on write");
120888242Sjkh			} else
1209113949Sjkh				warn("main: short write, 0x%x bytes instead of 0x%x\n",
121088242Sjkh				     (unsigned)written, maxfilelen);
121188242Sjkh			exit(98);
121288242Sjkh		}
1213113949Sjkh	} else
121488242Sjkh		check_trunc_hack();
121588242Sjkh
121688242Sjkh	while (numops == -1 || numops--)
121788242Sjkh		test();
121888242Sjkh
121988242Sjkh	if (close(fd)) {
122088242Sjkh		prterr("close");
122188242Sjkh		report_failure(99);
122288242Sjkh	}
122388242Sjkh	prt("All operations completed A-OK!\n");
122488242Sjkh
122588242Sjkh	exit(0);
122688242Sjkh	return 0;
122788242Sjkh}
1228113949Sjkh
1229