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