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