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