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