Whamcloud - gitweb
LU-13602 pcc: add LCM_FL_PCC_RDONLY layout flag
[fs/lustre-release.git] / lustre / tests / fsx.c
1 /*
2  * Copyright (C) 1991, NeXT Computer, Inc.  All Rights Reserverd.
3  * Copyright (c) 1998-2001 Apple Computer, Inc. All rights reserved.
4  *
5  * Copyright (c) 2012, Intel Corporation.
6  *
7  * @APPLE_LICENSE_HEADER_START@
8  *
9  * The contents of this file constitute Original Code as defined in and
10  * are subject to the Apple Public Source License Version 1.1 (the
11  * "License").  You may not use this file except in compliance with the
12  * License.  Please obtain a copy of the License at
13  * http://www.apple.com/publicsource and read it before using this file.
14  *
15  * This Original Code and all software distributed under the License are
16  * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
20  * License for the specific language governing rights and limitations
21  * under the License.
22  *
23  * @APPLE_LICENSE_HEADER_END@
24  *
25  *      File:   fsx.c
26  *      Author: Avadis Tevanian, Jr.
27  *
28  *      File system exerciser.
29  *
30  *      Rewrite and enhancements 1998-2001 Conrad Minshall -- conrad@mac.com
31  *
32  *      Various features from Joe Sokol, Pat Dirks, and Clark Warner.
33  *
34  *      Small changes to work under Linux -- davej.
35  *
36  *      Sundry porting patches from Guy Harris 12/2001
37  * $FreeBSD: src/tools/regression/fsx/fsx.c,v 1.1 2001/12/20 04:15:57 jkh Exp $
38  *
39  *      Checks for mmap last-page zero fill.
40  *
41  *      Add multi-file testing feature -- Zach Brown <zab@clusterfs.com>
42  *
43  *      Add random preallocation calls - Eric Sandeen <sandeen@redhat.com>
44  *
45  * $FreeBSD: src/tools/regression/fsx/fsx.c,v 1.2 2003/04/23 23:42:23 jkh Exp $
46  * $DragonFly: src/test/stress/fsx/fsx.c,v 1.2 2005/05/02 19:31:56 dillon Exp $
47  */
48 #ifndef _GNU_SOURCE
49 #define _GNU_SOURCE
50 #endif
51
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #if defined(_UWIN) || defined(__linux__)
55 # include <sys/param.h>
56 # include <limits.h>
57 # include <time.h>
58 # include <strings.h>
59 #endif
60 #include <sys/time.h>
61 #include <fcntl.h>
62 #include <sys/mman.h>
63 #ifndef MAP_FILE
64 # define MAP_FILE 0
65 #endif
66 #include <limits.h>
67 #include <signal.h>
68 #include <stdio.h>
69 #include <stddef.h>
70 #include <stdlib.h>
71 #include <string.h>
72 #include <unistd.h>
73 #include <stdarg.h>
74 #include <errno.h>
75 #include <libcfs/util/string.h>
76 #include <setjmp.h>
77
78 /*
79  * Each test run will work with one or more separate file descriptors for the
80  * same file.  This allows testing cache coherency across multiple mountpoints
81  * of the same network filesystem on a single client.
82  */
83 struct test_file {
84         char *path;
85         int fd;
86         int o_direct;
87 } *test_files = NULL, *tf;
88
89 int num_test_files;
90
91 enum fd_iteration_policy {
92         FD_SINGLE,
93         FD_ROTATE,
94         FD_RANDOM,
95 };
96
97 int fd_policy = FD_RANDOM;
98 int fd_last;
99
100 /*
101  *      A log entry is an operation and a bunch of arguments.
102  */
103
104 struct log_entry {
105         int operation;
106         int args[3];
107         struct timeval tv;
108         const struct test_file *tf;
109 };
110
111 #define LOGSIZE 100000
112
113 struct log_entry oplog[LOGSIZE]; /* the log */
114 int logptr; /* current position in log */
115 int logcount; /* total ops */
116 int jmpbuf_good;
117 jmp_buf jmpbuf;
118
119 /*
120  * Define operations
121  */
122
123 /* common operations */
124 #define OP_READ         0
125 #define OP_WRITE        1
126 #define OP_MAPREAD      2
127 #define OP_MAPWRITE     3
128 #define OP_MAX_LITE     4
129
130 /* !lite operations */
131 #define OP_TRUNCATE             4
132 #define OP_FALLOCATE            5
133 #define OP_PUNCH_HOLE           6
134 #define OP_ZERO_RANGE           7
135 #define OP_CLOSEOPEN            8
136 #define OP_MAX_FULL             9
137
138 #define OP_SKIPPED 101
139 /* _GNU_SOURCE defines O_DIRECT as 14th bit which is 0x4000(16384) */
140 #define OP_DIRECT  16384
141
142 #ifndef FALLOC_FL_PUNCH_HOLE
143 #define FALLOC_FL_PUNCH_HOLE 0x02 /* de-allocates range */
144 #endif
145
146 #ifndef FALLOC_FL_KEEP_SIZE
147 #define FALLOC_FL_KEEP_SIZE 0x01 /* default is extend size */
148 #endif
149
150 #ifndef FALLOC_FL_ZERO_RANGE
151 #define FALLOC_FL_ZERO_RANGE 0x10 /* convert range to zeros */
152 #endif
153
154
155 char *original_buf; /* a pointer to the original data */
156 char *good_buf; /* a pointer to the correct data */
157 char *temp_buf; /* a pointer to the current data */
158 char *fname; /* name of our test file */
159 char logfile[PATH_MAX]; /* name of our log file */
160 char goodfile[PATH_MAX]; /* name of our test file */
161
162 struct timeval tv; /* time current operation started */
163 off_t file_size;
164 off_t biggest;
165 char state[256];
166 unsigned long testcalls; /* calls to function "test" */
167
168 long simulatedopcount;                  /* -b flag */
169 int closeprob;                          /* -c flag */
170 int debug ;                             /* -d flag */
171 long debugstart;                        /* -D flag */
172 int flush;                              /* -f flag */
173 int do_fsync;                           /* -y flag */
174 long maxfilelen = 256 * 1024;           /* -l flag */
175 int sizechecks = 1;                     /* -n flag disables them */
176 int maxoplen = 64 * 1024;               /* -o flag */
177 int quiet;                              /* -q flag */
178 long progressinterval;                  /* -p flag */
179 int readbdy = 1;                        /* -r flag */
180 int style;                              /* -s flag */
181 int truncbdy = 1;                       /* -t flag */
182 int writebdy = 1;                       /* -w flag */
183 long monitorstart = -1;                 /* -m flag */
184 long monitorend = -1;                   /* -m flag */
185 int lite;                               /* -L flag */
186 long numops = -1;                       /* -N flag */
187 int randomoplen = 1;                    /* -O flag disables it */
188 int seed = 1;                           /* -S flag */
189 int mapped_writes = 1;                  /* -W flag disables */
190 int fallocate_calls = 1;                /* -F flag disables */
191 int punch_hole_calls = 1;               /* -H flag disables */
192 int zero_range_calls = 1;               /* -z flag disables */
193 int mapped_reads = 1;                   /* -R flag disables it */
194 int fsxgoodfd;
195 int o_direct;                           /* -Z */
196 int fl_keep_size;
197
198 int page_size;
199 int page_mask;
200
201 FILE *fsxlogf;
202 int badoff = -1;
203
204 void
205 vwarnc(code, fmt, ap)
206         int code;
207         const char *fmt;
208         va_list ap;
209 {
210         fprintf(stderr, "fsx: ");
211         if (fmt) {
212                 vfprintf(stderr, fmt, ap);
213                 fprintf(stderr, ": ");
214         }
215         fprintf(stderr, "%s\n", strerror(code));
216 }
217
218 void
219 __attribute__((format(__printf__, 1, 2)))
220 warn(const char *fmt, ...)
221 {
222         va_list ap;
223
224         va_start(ap, fmt);
225         vwarnc(errno, fmt, ap);
226         va_end(ap);
227 }
228
229 void
230 __attribute__((format(__printf__, 1, 2)))
231 prt(char *fmt, ...)
232 {
233         va_list args;
234
235         va_start(args, fmt);
236         vfprintf(stdout, fmt, args);
237         va_end(args);
238
239         if (fsxlogf) {
240                 va_start(args, fmt);
241                 vfprintf(fsxlogf, fmt, args);
242                 va_end(args);
243         }
244 }
245
246 /*
247  * prterr() is now a macro. It internally calls ptrerr_func()
248  * which transparently handles passing of function name.
249  * This version also keeps checkpatch happy.
250  */
251 void
252 ptrerr_func(const char *func, const char *prefix)
253 {
254         prt("%s: %s%s%s\n", func, prefix, prefix ? ": " : "", strerror(errno));
255 }
256 #define prterr(prefix) ptrerr_func(__func__, prefix)
257
258 void
259 log4(int operation, int arg0, int arg1, int arg2)
260 {
261         struct log_entry *le;
262
263         le = &oplog[logptr];
264         le->operation = operation;
265         le->args[0] = arg0;
266         le->args[1] = arg1;
267         le->args[2] = arg2;
268         gettimeofday(&tv, NULL);
269         le->tv = tv;
270         le->tf = tf;
271         logptr++;
272         logcount++;
273         if (logptr >= LOGSIZE)
274                 logptr = 0;
275 }
276
277 const char *
278 fill_tf_buf(const struct test_file *tf)
279 {
280         static int max_tf_len;
281         static char tf_buf[32];
282
283         if (fd_policy == FD_SINGLE)
284                 return "";
285
286         if (max_tf_len == 0)
287                 max_tf_len = scnprintf(tf_buf, sizeof(tf_buf) - 1,
288                                       "%u", num_test_files - 1);
289
290         snprintf(tf_buf, sizeof(tf_buf), "[%0*lu]", max_tf_len,
291                 (unsigned long)(tf - test_files));
292
293         return tf_buf;
294 }
295
296 void
297 logdump(void)
298 {
299         int i, count, down;
300         struct log_entry *lp;
301         char *falloc_type[3] = {"PAST_EOF", "EXTENDING", "INTERIOR"};
302
303         prt("LOG DUMP (%d total operations):\n", logcount);
304         if (logcount < LOGSIZE) {
305                 i = 0;
306                 count = logcount;
307         } else {
308                 i = logptr;
309                 count = LOGSIZE;
310         }
311         for ( ; count > 0; count--) {
312                 int opnum;
313
314                 opnum = i + 1 + (logcount / LOGSIZE) * LOGSIZE;
315                 lp = &oplog[i];
316                 prt("%d%s: %lu.%06u ", opnum, fill_tf_buf(lp->tf),
317                     lp->tv.tv_sec, (int)lp->tv.tv_usec);
318
319                 switch (lp->operation) {
320                 case OP_MAPREAD:
321                         prt("MAPREAD  0x%05x thru 0x%05x (0x%05x bytes)",
322                             lp->args[0], lp->args[0] + lp->args[1] - 1,
323                             lp->args[1]);
324                         if (badoff >= lp->args[0] && badoff <
325                                                      lp->args[0] + lp->args[1])
326                                 prt("\t***RRRR***");
327                         break;
328                 case OP_MAPWRITE:
329                         prt("MAPWRITE 0x%05x thru 0x%05x (0x%05x bytes)",
330                             lp->args[0], lp->args[0] + lp->args[1] - 1,
331                             lp->args[1]);
332                         if (badoff >= lp->args[0] && badoff <
333                                                      lp->args[0] + lp->args[1])
334                                 prt("\t******WWWW");
335                         break;
336                 case OP_READ:
337                 case OP_READ + OP_DIRECT:
338                         prt("READ%s  0x%05x thru 0x%05x (0x%05x bytes)",
339                             lp->operation & OP_DIRECT ? "_OD" : "   ",
340                             lp->args[0], lp->args[0] + lp->args[1] - 1,
341                             lp->args[1]);
342                         if (badoff >= lp->args[0] &&
343                             badoff < lp->args[0] + lp->args[1])
344                                 prt("\t***RRRR***");
345                         break;
346                 case OP_WRITE:
347                 case OP_WRITE + OP_DIRECT:
348                         prt("WRITE%s 0x%05x thru 0x%05x (0x%05x bytes)",
349                             lp->operation & OP_DIRECT ? "_OD" : "   ",
350                             lp->args[0], lp->args[0] + lp->args[1] - 1,
351                             lp->args[1]);
352                         if (lp->args[0] > lp->args[2])
353                                 prt(" HOLE");
354                         else if (lp->args[0] + lp->args[1] > lp->args[2])
355                                 prt(" EXTEND");
356                         if ((badoff >= lp->args[0] || badoff >= lp->args[2]) &&
357                             badoff < lp->args[0] + lp->args[1])
358                                 prt("\t***WWWW");
359                         break;
360                 case OP_TRUNCATE:
361                         down = lp->args[0] < lp->args[1];
362                         prt("TRUNCATE %s\tfrom 0x%05x to 0x%05x",
363                             down ? "DOWN" : "UP", lp->args[1], lp->args[0]);
364                         if (badoff >= lp->args[!down] &&
365                             badoff < lp->args[!!down])
366                                 prt("\t******WWWW");
367                         break;
368                 case OP_FALLOCATE:
369                         /* 0: offset 1: length 2: where alloced */
370                         prt("FALLOC  \tfrom 0x%05x to 0x%05x\t(0x%05x bytes)%s",
371                             lp->args[0], lp->args[0] + lp->args[1],
372                             lp->args[1], falloc_type[lp->args[2]]);
373                         if (badoff >= lp->args[0] &&
374                             badoff < lp->args[0] + lp->args[1])
375                                 prt("\t******FFFF");
376                         break;
377                 case OP_PUNCH_HOLE:
378                         prt("PUNCH    0x%05x thru 0x%05x\t(0x%05x bytes)",
379                             lp->args[0], lp->args[0] + lp->args[1] - 1,
380                             lp->args[1]);
381                         if (badoff >= lp->args[0] && badoff <
382                                                      lp->args[0] + lp->args[1])
383                                 prt("\t******PPPP");
384                         break;
385                 case OP_ZERO_RANGE:
386                         prt("ZERO     0x%05x thru 0x%05x\t(0x%05x bytes)",
387                             lp->args[0], lp->args[0] + lp->args[1] - 1,
388                             lp->args[1]);
389                         if (badoff >= lp->args[0] && badoff <
390                                                      lp->args[0] + lp->args[1])
391                                 prt("\t******ZZZZ");
392                         break;
393                 case OP_CLOSEOPEN:
394                 case OP_CLOSEOPEN + OP_DIRECT:
395                         prt("CLOSE/OPEN%s",
396                             lp->operation & OP_DIRECT ? "_OD" : "   ");
397                         break;
398                 case OP_SKIPPED:
399                         prt("SKIPPED (no operation)");
400                         break;
401                 default:
402                         prt("BOGUS LOG ENTRY (operation code = %d)!",
403                             lp->operation);
404                 }
405                 prt("\n");
406                 i++;
407                 if (i == LOGSIZE)
408                         i = 0;
409         }
410 }
411
412 void
413 save_buffer(char *buffer, off_t bufferlength, int fd)
414 {
415         off_t ret;
416         ssize_t byteswritten;
417
418         if (fd <= 0 || bufferlength == 0)
419                 return;
420
421         if (bufferlength > INT_MAX) {
422                 prt("fsx flaw: overflow in %s\n", __func__);
423                 exit(67);
424         }
425         if (lite) {
426                 off_t size_by_seek = lseek(fd, (off_t)0, SEEK_END);
427
428                 if (size_by_seek == (off_t)-1) {
429                         prterr("lseek eof");
430                 } else if (bufferlength > size_by_seek) {
431                         warn("%s: .fsxgood file too short... will save 0x%llx bytes instead of 0x%llx\n",
432                              __func__, (unsigned long long)size_by_seek,
433                              (unsigned long long)bufferlength);
434                         bufferlength = size_by_seek;
435                 }
436         }
437
438         ret = lseek(fd, (off_t)0, SEEK_SET);
439         if (ret == (off_t)-1)
440                 prterr("lseek 0");
441
442         byteswritten = write(fd, buffer, (size_t)bufferlength);
443         if (byteswritten != bufferlength) {
444                 if (byteswritten == -1)
445                         prterr("write");
446                 else
447                         warn("%s: short write, 0x%x bytes instead of 0x%llx\n",
448                              __func__, (unsigned int)byteswritten,
449                              (unsigned long long)bufferlength);
450         }
451 }
452
453 void
454 report_failure(int status)
455 {
456         logdump();
457
458         if (fsxgoodfd) {
459                 if (good_buf) {
460                         save_buffer(good_buf, file_size, fsxgoodfd);
461                         prt("Correct content saved for comparison\n");
462                         prt("(maybe hexdump \"%s\" vs \"%s\")\n",
463                             fname, goodfile);
464                 }
465                 close(fsxgoodfd);
466         }
467         exit(status);
468 }
469
470 #define short_at(cp) ((unsigned short)((*((unsigned char *)(cp)) << 8) | \
471                       *(((unsigned char *)(cp)) + 1)))
472
473 void
474 check_buffers(unsigned int offset, unsigned int size)
475 {
476         unsigned char c, t;
477         unsigned int i = 0;
478         unsigned int n = 0;
479         unsigned int op = 0;
480         unsigned int bad = 0;
481
482         if (memcmp(good_buf + offset, temp_buf, size) != 0) {
483                 prt("READ BAD DATA: offset = 0x%x, size = 0x%x\n",
484                     offset, size);
485                 prt("OFFSET\tGOOD\tBAD\tRANGE\n");
486                 while (size > 0) {
487                         c = good_buf[offset];
488                         t = temp_buf[i];
489                         if (c != t) {
490                                 if (n == 0) {
491                                         bad = short_at(&temp_buf[i]);
492                                         prt("%#07x\t%#06x\t%#06x", offset,
493                                             short_at(&good_buf[offset]), bad);
494                                         op = temp_buf[offset & 1 ? i + 1 : i];
495                                 }
496                                 n++;
497                                 badoff = offset;
498                         }
499                         offset++;
500                         i++;
501                         size--;
502                 }
503                 if (n) {
504                         prt("\t%#7x\n", n);
505                         if (bad)
506                                 prt("operation# (mod 256) for the bad data may be %u\n",
507                                     ((unsigned int)op & 0xff));
508                         else
509                                 prt("operation# (mod 256) for the bad data unknown, check HOLE and EXTEND ops\n");
510                 } else {
511                         prt("????????????????\n");
512                 }
513                 report_failure(110);
514         }
515 }
516
517 struct test_file *
518 get_tf(void)
519 {
520         unsigned int index = 0;
521
522         switch (fd_policy) {
523         case FD_ROTATE:
524                 index = fd_last++;
525                 break;
526         case FD_RANDOM:
527                 index = random();
528                 break;
529         case FD_SINGLE:
530                 index = 0;
531                 break;
532         default:
533                 prt("unknown policy");
534                 exit(1);
535                 break;
536         }
537         return &test_files[index % num_test_files];
538 }
539
540 void
541 assign_fd_policy(char *policy)
542 {
543         if (!strcmp(policy, "random")) {
544                 fd_policy = FD_RANDOM;
545         } else if (!strcmp(policy, "rotate")) {
546                 fd_policy = FD_ROTATE;
547         } else {
548                 prt("unknown -I policy: '%s'\n", policy);
549                 exit(1);
550         }
551 }
552
553 int
554 get_fd(void)
555 {
556         struct test_file *tf = get_tf();
557
558         return tf->fd;
559 }
560
561 static const char *my_basename(const char *path)
562 {
563         char *c = strrchr(path, '/');
564
565         return c ? c++ : path;
566 }
567
568 void
569 open_test_files(char **argv, int argc)
570 {
571         struct test_file *tf;
572         int i;
573
574         num_test_files = argc;
575         if (num_test_files == 1)
576                 fd_policy = FD_SINGLE;
577
578         test_files = calloc(num_test_files, sizeof(*test_files));
579         if (!test_files) {
580                 prterr("reallocating space for test files");
581                 exit(1);
582         }
583
584         for (i = 0, tf = test_files; i < num_test_files; i++, tf++) {
585                 tf->path = argv[i];
586 #ifdef O_DIRECT
587                 tf->o_direct = (random() % (o_direct + 1)) ? OP_DIRECT : 0;
588 #endif
589                 tf->fd = open(tf->path,
590                               O_RDWR | (lite ? 0 : O_CREAT | O_TRUNC) |
591                               tf->o_direct, 0666);
592                 if (tf->fd < 0) {
593                         prterr(tf->path);
594                         exit(91);
595                 }
596         }
597
598         if (quiet || fd_policy == FD_SINGLE)
599                 return;
600
601         for (i = 0, tf = test_files; i < num_test_files; i++, tf++)
602                 prt("fd %d: %s\n", i, tf->path);
603 }
604
605 void
606 close_test_files(void)
607 {
608         int i;
609         struct test_file *tf;
610
611         for (i = 0, tf = test_files; i < num_test_files; i++, tf++) {
612                 if (close(tf->fd)) {
613                         prterr("close");
614                         report_failure(99);
615                 }
616         }
617 }
618
619 void
620 check_size(void)
621 {
622         struct stat statbuf;
623         off_t size_by_seek;
624         int fd = get_fd();
625
626         if (fstat(fd, &statbuf)) {
627                 prterr("fstat");
628                 statbuf.st_size = -1;
629         }
630         size_by_seek = lseek(fd, (off_t)0, SEEK_END);
631         if (file_size != statbuf.st_size || file_size != size_by_seek) {
632                 prt("Size error: expected 0x%llx stat 0x%llx seek 0x%llx\n",
633                     (unsigned long long)file_size,
634                     (unsigned long long)statbuf.st_size,
635                     (unsigned long long)size_by_seek);
636                 report_failure(120);
637         }
638 }
639
640 void
641 check_trunc_hack(void)
642 {
643         struct stat statbuf;
644         int fd = get_fd();
645
646         /* should not ignore ftruncate(2)'s return value */
647         if (ftruncate(fd, (off_t)0) < 0) {
648                 prterr("trunc_hack: ftruncate(0)");
649                 exit(1);
650         }
651         if (ftruncate(fd, (off_t)100000) < 0) {
652                 prterr("trunc_hack: ftruncate(100000)");
653                 exit(1);
654         }
655         if (fstat(fd, &statbuf)) {
656                 prterr("trunc_hack: fstat");
657                 statbuf.st_size = -1;
658         }
659         if (statbuf.st_size != (off_t)100000) {
660                 prt("no extend on truncate! not posix!\n");
661                 exit(130);
662         }
663         if (ftruncate(fd, 0) < 0) {
664                 prterr("trunc_hack: ftruncate(0) (2nd call)");
665                 exit(1);
666         }
667 }
668
669 void
670 output_line(struct test_file *tf, int op, unsigned int offset,
671             unsigned int size)
672 {
673         char *ops[] = {
674                 [OP_READ] = "read",
675                 [OP_WRITE] = "write",
676                 [OP_TRUNCATE] = "trunc from",
677                 [OP_MAPREAD] = "mapread",
678                 [OP_MAPWRITE] = "mapwrite",
679                 [OP_READ + OP_DIRECT] = "read_OD",
680                 [OP_WRITE + OP_DIRECT] = "write_OD",
681                 [OP_FALLOCATE] = "fallocate",
682                 [OP_PUNCH_HOLE] = "punch from",
683         };
684
685         /* W. */
686         if (!(!quiet &&
687             ((progressinterval && testcalls % progressinterval == 0) ||
688             (debug && (monitorstart == -1 ||
689             (offset + size > monitorstart &&
690             (monitorend == -1 || offset <= monitorend)))))))
691                 return;
692
693         prt("%06lu%s %lu.%06u %-10s %#08x %s %#08x\t(0x0%x bytes)\n",
694             testcalls, fill_tf_buf(tf), tv.tv_sec, (int)tv.tv_usec,
695             ops[op], offset, op == OP_TRUNCATE || op == OP_PUNCH_HOLE ?
696             " to " : "thru", offset + size - 1,
697              (int)size < 0 ? -(int)size : size);
698 }
699
700 void output_debug(unsigned int offset, unsigned int size, const char *what)
701 {
702         struct timeval t;
703
704         if (!quiet && (debug > 1 && (monitorstart == -1 ||
705             (offset + size >= monitorstart &&
706              (monitorend == -1 || offset <= monitorend))))) {
707                 gettimeofday(&t, NULL);
708                 prt("       %lu.%06u %s\n", t.tv_sec, (int)t.tv_usec, what);
709         }
710 }
711
712 void
713 doflush(unsigned int offset, unsigned int size)
714 {
715         unsigned int pg_offset;
716         unsigned int map_size;
717         char *p;
718         struct test_file *tf = get_tf();
719         int fd = tf->fd;
720
721         if (tf->o_direct)
722                 return;
723
724         pg_offset = offset & page_mask;
725         map_size  = pg_offset + size;
726
727         p = (char *)mmap(0, map_size, PROT_READ | PROT_WRITE,
728                          MAP_FILE | MAP_SHARED, fd,
729                          (off_t)(offset - pg_offset));
730         if (p == (char *)-1) {
731                 prterr("mmap");
732                 report_failure(202);
733         }
734         if (msync(p, map_size, MS_INVALIDATE) != 0) {
735                 prterr("msync");
736                 report_failure(203);
737         }
738         if (munmap(p, map_size) != 0) {
739                 prterr("munmap");
740                 report_failure(204);
741         }
742         output_debug(offset, size, "flush done");
743 }
744
745 void
746 doread(unsigned int offset, unsigned int size)
747 {
748         off_t ret;
749         unsigned int iret;
750         struct test_file *tf = get_tf();
751         int fd = tf->fd;
752
753         offset -= offset % readbdy;
754         if (tf->o_direct)
755                 size -= size % readbdy;
756
757         if (size == 0) {
758                 if (!quiet && testcalls > simulatedopcount && !tf->o_direct)
759                         prt("skipping zero size read\n");
760                 log4(OP_SKIPPED, OP_READ, offset, size);
761                 return;
762         }
763         if (size + offset > file_size) {
764                 if (!quiet && testcalls > simulatedopcount)
765                         prt("skipping seek/read past end of file\n");
766                 log4(OP_SKIPPED, OP_READ, offset, size);
767                 return;
768         }
769
770         log4(OP_READ + tf->o_direct, offset, size, 0);
771
772         if (testcalls <= simulatedopcount)
773                 return;
774
775         output_line(tf, OP_READ + tf->o_direct, offset, size);
776
777         ret = lseek(fd, (off_t)offset, SEEK_SET);
778         if (ret == (off_t)-1) {
779                 prterr("lseek");
780                 report_failure(140);
781         }
782         iret = read(fd, temp_buf, size);
783         output_debug(offset, size, "read done");
784         if (iret != size) {
785                 if (iret == -1)
786                         prterr("read");
787                 else
788                         prt("short read: 0x%x bytes instead of 0x%x\n",
789                             iret, size);
790                 report_failure(141);
791         }
792         check_buffers(offset, size);
793 }
794
795 void
796 check_eofpage(char *s, unsigned int offset, char *p, int size)
797 {
798         long last_page, should_be_zero;
799
800         if (offset + size <= (file_size & ~page_mask))
801                 return;
802         /*
803          * we landed in the last page of the file
804          * test to make sure the VM system provided 0's
805          * beyond the true end of the file mapping
806          * (as required by mmap def in 1996 posix 1003.1)
807          */
808         last_page = ((long)p + (offset & page_mask) + size) & ~page_mask;
809
810         for (should_be_zero = last_page + (file_size & page_mask);
811              should_be_zero < last_page + page_size;
812              should_be_zero++)
813                 if (*(char *)should_be_zero) {
814                         prt("Mapped %s: non-zero data past EOF (0x%llx) page offset 0x%lx is 0x%04x\n",
815                             s, (long long)file_size - 1,
816                             should_be_zero & page_mask,
817                             short_at(should_be_zero));
818                         report_failure(205);
819                 }
820 }
821
822 void
823 domapread(unsigned int offset, unsigned int size)
824 {
825         unsigned int pg_offset;
826         unsigned int map_size;
827         char *p;
828         int fd;
829
830         offset -= offset % readbdy;
831         tf = get_tf();
832         fd = tf->fd;
833         if (size == 0) {
834                 if (!quiet && testcalls > simulatedopcount)
835                         prt("skipping zero size read\n");
836                 log4(OP_SKIPPED, OP_MAPREAD, offset, size);
837                 return;
838         }
839         if (size + offset > file_size) {
840                 if (!quiet && testcalls > simulatedopcount)
841                         prt("skipping seek/read past end of file\n");
842                 log4(OP_SKIPPED, OP_MAPREAD, offset, size);
843                 return;
844         }
845
846         log4(OP_MAPREAD, offset, size, 0);
847
848         if (testcalls <= simulatedopcount)
849                 return;
850
851         output_line(tf, OP_MAPREAD, offset, size);
852
853         pg_offset = offset & page_mask;
854         map_size  = pg_offset + size;
855
856         p = mmap(0, map_size, PROT_READ, MAP_FILE | MAP_SHARED, fd,
857                  (off_t)(offset - pg_offset));
858         if (p == MAP_FAILED) {
859                 prterr("mmap");
860                 report_failure(190);
861         }
862         output_debug(offset, size, "mmap done");
863         if (setjmp(jmpbuf) == 0) {
864                 jmpbuf_good = 1;
865                 memcpy(temp_buf, p + pg_offset, size);
866                 check_eofpage("Read", offset, p, size);
867                 jmpbuf_good = 0;
868         } else {
869                 report_failure(1901);
870         }
871         output_debug(offset, size, "memcpy done");
872         if (munmap(p, map_size) != 0) {
873                 prterr("munmap");
874                 report_failure(191);
875         }
876         output_debug(offset, size, "munmap done");
877
878         check_buffers(offset, size);
879 }
880
881 void
882 gendata(char *original_buf, char *good_buf, unsigned int offset,
883         unsigned int size)
884 {
885         while (size--) {
886                 good_buf[offset] = testcalls % 256;
887                 if (offset % 2)
888                         good_buf[offset] += original_buf[offset];
889                 offset++;
890         }
891 }
892
893 void
894 dowrite(unsigned int offset, unsigned int size)
895 {
896         off_t ret;
897         unsigned int iret;
898         int fd;
899
900         tf = get_tf();
901         fd = tf->fd;
902         offset -= offset % writebdy;
903         if (tf->o_direct)
904                 size -= size % writebdy;
905         if (size == 0) {
906                 if (!quiet && testcalls > simulatedopcount && !tf->o_direct)
907                         prt("skipping zero size write\n");
908                 log4(OP_SKIPPED, OP_WRITE, offset, size);
909                 return;
910         }
911
912         log4(OP_WRITE + tf->o_direct, offset, size, file_size);
913
914         gendata(original_buf, good_buf, offset, size);
915         if (file_size < offset + size) {
916                 if (file_size < offset)
917                         memset(good_buf + file_size, '\0', offset - file_size);
918                 file_size = offset + size;
919                 if (lite) {
920                         warn("Lite file size bug in fsx!");
921                         report_failure(149);
922                 }
923         }
924
925         if (testcalls <= simulatedopcount)
926                 return;
927
928         output_line(tf, OP_WRITE + tf->o_direct, offset, size);
929
930         ret = lseek(fd, (off_t)offset, SEEK_SET);
931         if (ret == (off_t)-1) {
932                 prterr("lseek");
933                 report_failure(150);
934         }
935         iret = write(fd, good_buf + offset, size);
936         output_debug(offset, size, "write done");
937         if (iret != size) {
938                 if (iret == -1)
939                         prterr("write");
940                 else
941                         prt("short write: 0x%x bytes instead of 0x%x\n",
942                             iret, size);
943                 report_failure(151);
944         }
945         if (do_fsync) {
946                 if (fsync(fd)) {
947                         prt("fsync() failed: %s\n", strerror(errno));
948                         report_failure(152);
949                 }
950                 output_debug(offset, size, "fsync done");
951         }
952         if (flush) {
953                 doflush(offset, size);
954                 output_debug(offset, size, "flush done");
955         }
956 }
957
958 void
959 domapwrite(unsigned int offset, unsigned int size)
960 {
961         unsigned int pg_offset;
962         unsigned int map_size;
963         off_t cur_filesize;
964         char *p;
965         int fd;
966
967         tf = get_tf();
968         fd = tf->fd;
969         offset -= offset % writebdy;
970         if (size == 0) {
971                 if (!quiet && testcalls > simulatedopcount)
972                         prt("skipping zero size write\n");
973                 log4(OP_SKIPPED, OP_MAPWRITE, offset, size);
974                 return;
975         }
976         cur_filesize = file_size;
977
978         log4(OP_MAPWRITE, offset, size, 0);
979
980         gendata(original_buf, good_buf, offset, size);
981         if (file_size < offset + size) {
982                 if (file_size < offset)
983                         memset(good_buf + file_size, '\0', offset - file_size);
984                 file_size = offset + size;
985                 if (lite) {
986                         warn("Lite file size bug in fsx!");
987                         report_failure(200);
988                 }
989         }
990
991         if (testcalls <= simulatedopcount)
992                 return;
993
994         output_line(tf, OP_MAPWRITE, offset, size);
995
996         if (file_size > cur_filesize) {
997                 if (ftruncate(fd, file_size) == -1) {
998                         prterr("ftruncate");
999                         exit(201);
1000                 }
1001                 output_debug(offset, size, "truncate done");
1002         }
1003         pg_offset = offset & page_mask;
1004         map_size  = pg_offset + size;
1005
1006         p = mmap(0, map_size, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED,
1007                  fd, (off_t)(offset - pg_offset));
1008         if (p == MAP_FAILED) {
1009                 prterr("mmap");
1010                 report_failure(202);
1011         }
1012         output_debug(offset, map_size, "mmap done");
1013         if (setjmp(jmpbuf) == 0) {
1014                 jmpbuf_good = 1;
1015                 memcpy(p + pg_offset, good_buf + offset, size);
1016                 if (msync(p, map_size, MS_SYNC) != 0) {
1017                         prterr("msync");
1018                         report_failure(203);
1019                 }
1020                 check_eofpage("Write", offset, p, size);
1021                 jmpbuf_good = 0;
1022         } else {
1023                 report_failure(2021);
1024         }
1025         output_debug(offset, map_size, "msync done");
1026         if (munmap(p, map_size) != 0) {
1027                 prterr("munmap");
1028                 report_failure(204);
1029         }
1030         output_debug(offset, map_size, "munmap done");
1031 }
1032
1033 void
1034 dotruncate(unsigned int size)
1035 {
1036         int oldsize = file_size;
1037         int fd;
1038
1039         tf = get_tf();
1040         fd = tf->fd;
1041         size -= size % truncbdy;
1042         if (size > biggest) {
1043                 biggest = size;
1044                 if (!quiet && testcalls > simulatedopcount)
1045                         prt("truncating to largest ever: 0x%x\n", size);
1046         }
1047
1048         log4(OP_TRUNCATE, size, (unsigned int)file_size, 0);
1049
1050         if (size > file_size)
1051                 memset(good_buf + file_size, '\0', size - file_size);
1052         file_size = size;
1053
1054         if (testcalls <= simulatedopcount)
1055                 return;
1056
1057         output_line(tf, OP_TRUNCATE, oldsize, size - oldsize);
1058
1059         if (ftruncate(fd, (off_t)size) == -1) {
1060                 prt("ftruncate: 0x%x\n", size);
1061                 prterr("ftruncate");
1062                 report_failure(160);
1063         }
1064         output_debug(size, 0, "truncate done");
1065 }
1066
1067 void
1068 do_punch_hole(unsigned int offset, unsigned int length)
1069 {
1070         int max_offset = 0;
1071         int max_len = 0;
1072         int mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE;
1073         int fd;
1074
1075         tf = get_tf();
1076         fd = tf->fd;
1077         if (length == 0) {
1078                 if (!quiet && testcalls > simulatedopcount) {
1079                         prt("skipping zero length punch hole\n");
1080                         log4(OP_SKIPPED, OP_PUNCH_HOLE, offset, length);
1081                 }
1082                 return;
1083         }
1084
1085         if (file_size <= (loff_t)offset) {
1086                 if (!quiet && testcalls > simulatedopcount) {
1087                         prt("skipping hole punch off the end of the file\n");
1088                         log4(OP_SKIPPED, OP_PUNCH_HOLE, offset, length);
1089                 }
1090                 return;
1091         }
1092
1093         log4(OP_PUNCH_HOLE, offset, length, 0);
1094
1095         if (testcalls <= simulatedopcount)
1096                 return;
1097
1098         output_line(tf, OP_PUNCH_HOLE, offset, length);
1099         if (fallocate(fd, mode, (loff_t)offset, (loff_t)length) == -1) {
1100                 prt("punch hole: %x to %x\n", offset, length);
1101                 prterr("fallocate");
1102                 report_failure(161);
1103         }
1104         output_debug(offset, length, "zero_range done");
1105
1106         max_offset = offset < file_size ? offset : file_size;
1107         max_len = max_offset + length <= file_size ? length :
1108                         file_size - max_offset;
1109         memset(good_buf + max_offset, '\0', max_len);
1110 }
1111
1112 void
1113 do_zero_range(unsigned int offset, unsigned int length)
1114 {
1115         unsigned int end_offset;
1116         int mode = FALLOC_FL_ZERO_RANGE;
1117         int keep_size;
1118         int fd;
1119
1120         tf = get_tf();
1121         fd = tf->fd;
1122         if (length == 0) {
1123                 if (!quiet && testcalls > simulatedopcount) {
1124                         prt("skipping zero length zero range\n");
1125                         log4(OP_SKIPPED, OP_ZERO_RANGE, offset, length);
1126                 }
1127                 return;
1128         }
1129
1130         keep_size = random() % 2;
1131
1132         end_offset = keep_size ? 0 : offset + length;
1133
1134         if (end_offset > biggest) {
1135                 biggest = end_offset;
1136                 if (!quiet && testcalls > simulatedopcount)
1137                         prt("zero_range to largest ever: 0x%x\n", end_offset);
1138         }
1139
1140         /*
1141          * last arg matches fallocate string array index in logdump:
1142          * 0: allocate past EOF
1143          * 1: extending prealloc
1144          * 2: interior prealloc
1145          */
1146         log4(OP_ZERO_RANGE, offset, length,
1147              (end_offset > file_size) ? (keep_size ? 0 : 1) : 2);
1148
1149         if (testcalls <= simulatedopcount)
1150                 return;
1151
1152         output_line(tf, OP_TRUNCATE, offset, length);
1153
1154         if (fallocate(fd, mode, (loff_t)offset, (loff_t)length) == -1) {
1155                 prt("pzero range: %x to %x\n", offset, length);
1156                 prterr("fallocate");
1157                 report_failure(161);
1158         }
1159         output_debug(offset, length, "zero_range done");
1160
1161         memset(good_buf + offset, '\0', length);
1162 }
1163
1164 /*
1165  * fallocate is basically a no-op unless extending,
1166  * then a lot like a truncate
1167  */
1168 void
1169 do_preallocate(unsigned int offset, unsigned int length)
1170 {
1171         off_t end_offset;
1172         int keep_size;
1173         int fd;
1174         struct stat statbufs;
1175
1176         tf = get_tf();
1177         fd = tf->fd;
1178         if (length == 0) {
1179                 if (!quiet && testcalls > simulatedopcount)
1180                         prt("skipping zero length fallocate\n");
1181                 log4(OP_SKIPPED, OP_FALLOCATE, offset, length);
1182                 return;
1183         }
1184
1185         keep_size = fl_keep_size && (random() % 2);
1186
1187         end_offset = offset + length;
1188         if (end_offset > biggest) {
1189                 biggest = end_offset;
1190                 if (!quiet && testcalls > simulatedopcount)
1191                         prt("fallocating to largest ever: 0x%jx\n", end_offset);
1192         }
1193
1194         /*
1195          * last arg matches fallocate string array index in logdump:
1196          * 0: allocate past EOF
1197          * 1: extending prealloc
1198          * 2: interior prealloc
1199          */
1200         log4(OP_FALLOCATE, offset, length, (end_offset > file_size) ?
1201              (keep_size ? 0 : 1) : 2);
1202
1203         if (end_offset > file_size && !keep_size) {
1204                 memset(good_buf + file_size, '\0', end_offset - file_size);
1205                 file_size = end_offset;
1206         }
1207
1208         if (testcalls <= simulatedopcount)
1209                 return;
1210
1211         fstat(fd, &statbufs);
1212         if (fallocate(fd, keep_size ? FALLOC_FL_KEEP_SIZE : 0, (loff_t)offset,
1213                       (loff_t)length) == -1) {
1214                 prt("fallocate: %x to %x\n", offset, length);
1215                 prterr("fallocate");
1216                 report_failure(161);
1217         }
1218         output_line(tf, OP_FALLOCATE, offset, length);
1219         output_debug(offset, length, "fallocate done");
1220 }
1221
1222 void
1223 writefileimage()
1224 {
1225         ssize_t iret;
1226         int fd = get_fd();
1227
1228         if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
1229                 prterr("lseek");
1230                 report_failure(171);
1231         }
1232         iret = write(fd, good_buf, file_size);
1233         if ((off_t)iret != file_size) {
1234                 if (iret == -1)
1235                         prterr("write");
1236                 else
1237                         prt("short write: 0x%lx bytes instead of 0x%llx\n",
1238                             (unsigned long)iret, (unsigned long long)file_size);
1239                 report_failure(172);
1240         }
1241         if (lite ? 0 : ftruncate(fd, file_size) == -1) {
1242                 prt("ftruncate2: %llx\n", (unsigned long long)file_size);
1243                 prterr("ftruncate");
1244                 report_failure(173);
1245         }
1246 }
1247
1248 void
1249 docloseopen(void)
1250 {
1251         int direct = 0;
1252         const char *tf_num = "";
1253
1254         if (testcalls <= simulatedopcount)
1255                 return;
1256
1257         tf = get_tf();
1258 #ifdef O_DIRECT
1259         direct = (random() % (o_direct + 1)) ? OP_DIRECT : 0;
1260 #endif
1261         log4(OP_CLOSEOPEN + direct, file_size, (unsigned int)file_size, 0);
1262
1263         if (fd_policy != FD_SINGLE)
1264                 tf_num = fill_tf_buf(tf);
1265
1266         if (debug)
1267                 prt("%06lu %lu.%06u %sclose/open%s\n", testcalls, tv.tv_sec,
1268                     (int)tv.tv_usec, tf_num, direct ? "(O_DIRECT)" : "");
1269         if (close(tf->fd))
1270                 report_failure(180);
1271
1272         output_debug(monitorstart, 0, "close done");
1273         tf->o_direct = direct;
1274         tf->fd = open(tf->path, O_RDWR | tf->o_direct, 0);
1275         if (tf->fd < 0) {
1276                 prterr(tf->o_direct ? "open(O_DIRECT)" : "open");
1277                 report_failure(181);
1278         }
1279         output_debug(monitorstart, 0,
1280                      tf->o_direct ? "open(O_DIRECT) done" : "open done");
1281 }
1282
1283 #define TRIM_OFF_LEN(off, len, size)    \
1284 do {                                    \
1285         if (size)                       \
1286                 (off) %= (size);        \
1287         else                            \
1288                 (off) = 0;              \
1289         if ((off) + (len) > (size))     \
1290                 (len) = (size) - (off); \
1291 } while (0)
1292
1293 void
1294 test(void)
1295 {
1296         unsigned long offset;
1297         unsigned long size = maxoplen;
1298         unsigned long rv = random();
1299         unsigned long op;
1300         int closeopen = 0;
1301
1302         if (simulatedopcount > 0 && testcalls == simulatedopcount)
1303                 writefileimage();
1304
1305         testcalls++;
1306
1307         if (closeprob)
1308                 closeopen = (rv >> 3) < (1 << 28) / closeprob;
1309
1310         if (debugstart > 0 && testcalls >= debugstart)
1311                 debug = 1;
1312
1313         if (!quiet && testcalls < simulatedopcount && testcalls % 100000 == 0)
1314                 prt("%lu...\n", testcalls);
1315
1316         offset = random();
1317         if (randomoplen)
1318                 size = random() % (maxoplen + 1);
1319
1320         /* calculate appropriate op to run */
1321         if (lite)
1322                 op = rv % OP_MAX_LITE;
1323         else
1324                 op = rv % OP_MAX_FULL;
1325
1326         switch (op) {
1327         case OP_MAPREAD:
1328                 if (!mapped_reads)
1329                         op = OP_READ;
1330                 break;
1331         case OP_MAPWRITE:
1332                 if (!mapped_writes)
1333                         op = OP_WRITE;
1334                 break;
1335         case OP_FALLOCATE:
1336                 if (!fallocate_calls) {
1337                         log4(OP_SKIPPED, OP_FALLOCATE, offset, size);
1338                         goto out;
1339                 }
1340                 break;
1341         case OP_PUNCH_HOLE:
1342                 if (!punch_hole_calls) {
1343                         log4(OP_SKIPPED, OP_PUNCH_HOLE, offset, size);
1344                         goto out;
1345                 }
1346                 break;
1347         case OP_ZERO_RANGE:
1348                 if (!zero_range_calls) {
1349                         log4(OP_SKIPPED, OP_ZERO_RANGE, offset, size);
1350                         goto out;
1351                 }
1352                 break;
1353         }
1354
1355         switch (op) {
1356         case OP_READ:
1357                 TRIM_OFF_LEN(offset, size, file_size);
1358                 doread(offset, size);
1359                 break;
1360         case OP_WRITE:
1361                 TRIM_OFF_LEN(offset, size, maxfilelen);
1362                 dowrite(offset, size);
1363                 break;
1364         case OP_MAPREAD:
1365                 TRIM_OFF_LEN(offset, size, file_size);
1366                 domapread(offset, size);
1367                 break;
1368         case OP_MAPWRITE:
1369                 TRIM_OFF_LEN(offset, size, maxfilelen);
1370                 domapwrite(offset, size);
1371                 break;
1372         case OP_TRUNCATE:
1373                 if (!style)
1374                         size = random() % maxfilelen;
1375                 dotruncate(size);
1376                 break;
1377         case OP_FALLOCATE:
1378                 TRIM_OFF_LEN(offset, size, maxfilelen);
1379                 do_preallocate(offset, size);
1380                 break;
1381         case OP_PUNCH_HOLE:
1382                 TRIM_OFF_LEN(offset, size, file_size);
1383                 do_punch_hole(offset, size);
1384                 break;
1385         case OP_ZERO_RANGE:
1386                 TRIM_OFF_LEN(offset, size, file_size);
1387                 do_zero_range(offset, size);
1388                 break;
1389         case OP_CLOSEOPEN:
1390                 if (closeopen)
1391                         docloseopen();
1392                 break;
1393         default:
1394                 prterr("unknown operation %d: Operation not supported");
1395                 report_failure(42);
1396                 break;
1397         }
1398
1399 out:
1400         if (sizechecks && testcalls > simulatedopcount)
1401                 check_size();
1402 }
1403
1404 void
1405 segv(int sig)
1406 {
1407         if (jmpbuf_good) {
1408                 jmpbuf_good = 0;
1409                 longjmp(jmpbuf, 1);
1410         }
1411         report_failure(9999);
1412 }
1413
1414 void
1415 cleanup(sig)
1416         int     sig;
1417 {
1418         if (sig)
1419                 prt("signal %d\n", sig);
1420         prt("testcalls = %lu\n", testcalls);
1421         exit(sig);
1422 }
1423
1424 void
1425 usage(void)
1426 {
1427         fprintf(stdout,
1428                 "usage: fsx [-dfnqFLOW] [-b opnum] [-c Prob] [-l flen] [-m start:end] [-o oplen] [-p progressinterval] [-r readbdy] [-s style] [-t truncbdy] [-w writebdy] [-D startingop] [ -I random|rotate ] [-N numops] [-P dirpath] [-S seed] [-Z [prob]] fname [additional paths to fname..]\n"
1429 "       -b opnum: beginning operation number (default 1)\n"
1430 "       -c P: 1 in P chance of file close+open at each op (default infinity)\n"
1431 "       -d: debug output for all operations [-d -d = more debugging]\n"
1432 "       -f flush and invalidate cache after I/O\n"
1433 /* OSX: -d duration: number of hours for the tool to run\n\ */
1434 /* OSX: -e: tests using an extended attribute rather than a file\n\ */
1435 /* OSX: -f forkname: test the named fork of fname\n\ */
1436 /* OSX: -g logpath: path for .fsxlog file\n\ */
1437 /* OSX: -h: write 0s instead of creating holes (i.e. sparse file)\n\ */
1438 /* OSX: -i: interactive mode, hit return before performing each operation\n\ */
1439 "       -l flen: the upper bound on file size (default 262144)\n"
1440 "       -m startop:endop: monitor (print debug output) specified byte range\n"
1441 "          (default 0:infinity)\n"
1442 "       -n: no verifications of file size\n"
1443 "       -o oplen: the upper bound on operation size (default 65536)\n"
1444 "       -p progressinterval: debug output at specified operation interval\n"
1445 "       -q: quieter operation\n"
1446 "       -r readbdy: %1$u would make reads page aligned (default 1)\n"
1447 "       -s style: 1 gives smaller truncates (default 0)\n"
1448 "       -t truncbdy: %1$u would make truncates page aligned (default 1)\n"
1449 "       -w writebdy: %1$u would make writes page aligned (default 1)\n"
1450 /* XFS: -x: preallocate file space before starting, XFS only (default 0)\n\ */
1451 "       -y synchronize changes to a file\n"
1452 /* OSX: -v: debug output for all operations\n\ */
1453 /* XFS: -A: Use the AIO system calls\n" */
1454 /* OSX: -C mix cached and un-cached read/write ops\n\ */
1455 "       -D startingop: debug output starting at specified operation\n"
1456 "       -F: Do not use fallocate (preallocation) calls\n"
1457 /* OSX: -G logsize: #entries in oplog (default 1024)\n\ */
1458 #ifdef FALLOC_FL_PUNCH_HOLE
1459 "       -H: Do not use punch hole calls\n"
1460 #endif
1461 #ifdef FALLOC_FL_ZERO_RANGE
1462 "       -z: Do not use zero range calls\n"
1463 #endif
1464 /* XFS: -C: Do not use collapse range calls\n\ */
1465 "       -I [rotate|random]: When multiple paths to the file are given,\n"
1466 "           each operation uses a different path.  Iterate through them in\n"
1467 "           order with 'rotate' or chose them at 'random'.  (default random)\n"
1468 "       -L: fsxLite - no file creations & no file size changes\n"
1469 /* OSX: -I: start interactive mode since operation opnum\n\ */
1470 /* OSX: -M: slow motion mode, wait 1 second before each op\n\ */
1471 "       -N numops: total # operations to do (default infinity)\n"
1472 "       -O: use oplen (see -o flag) for every op (default random)\n"
1473 "       -P: save .fsxlog and .fsxgood files in dirpath (default ./)\n"
1474 "       -R: read() system calls only (mapped reads disabled)\n"
1475 "       -S seed: for random # generator (default 1) 0 gets timestamp\n"
1476 /* OSX: -T datasize: atomic data element write size [1,2,4] (default 4)\n\ */
1477 "       -W: mapped write operations DISabled\n"
1478 #ifdef O_DIRECT
1479 "       -Z[P]: O_DIRECT file IO [1 in P chance for each open] (default off)\n"
1480 #endif
1481 "       fname: this filename is REQUIRED (no default)\n",
1482         page_size);
1483         exit(90);
1484 }
1485
1486 int
1487 getnum(char *s, char **e)
1488 {
1489         int ret = -1;
1490
1491         *e = (char *)0;
1492         ret = strtol(s, e, 0);
1493         if (*e)
1494                 switch (**e) {
1495                 case 'b':
1496                 case 'B':
1497                         ret *= 512;
1498                         *e = *e + 1;
1499                         break;
1500                 case 'k':
1501                 case 'K':
1502                         ret *= 1024;
1503                         *e = *e + 1;
1504                         break;
1505                 case 'm':
1506                 case 'M':
1507                         ret *= 1024 * 1024;
1508                         *e = *e + 1;
1509                         break;
1510                 case 'w':
1511                 case 'W':
1512                         ret *= 4;
1513                         *e = *e + 1;
1514                         break;
1515                 }
1516         return (ret);
1517 }
1518
1519 int
1520 test_fallocate(int mode)
1521 {
1522         int ret = 0;
1523         int fd = get_fd();
1524
1525         if (!lite) {
1526                 /* Must go more than a page away so let's go 4M to be sure */
1527                 if (fallocate(fd, mode, 0, 4096*1024) && errno == EOPNOTSUPP) {
1528                         if (!quiet)
1529                                 warn("%s: filesystem does not support fallocate mode 0x%x, disabling!",
1530                                      __func__, mode);
1531                 } else {
1532                         ret = 1;
1533                 }
1534
1535                 /* Always call ftruncate since file size might be adjusted
1536                  * by fallocate even on error
1537                  */
1538                 if (ftruncate(fd, 0) == -1)
1539                         warn("ftruncate to 0 size failed");
1540         }
1541         return ret;
1542 }
1543
1544 int
1545 main(int argc, char **argv)
1546 {
1547         int i, style, ch;
1548         char *endp;
1549         int dirpath = 0;
1550
1551         goodfile[0] = 0;
1552         logfile[0] = 0;
1553
1554         page_size = getpagesize();
1555         page_mask = page_size - 1;
1556
1557         setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */
1558
1559         while ((ch = getopt(argc, argv,
1560                             "b:c:dfl:m:no:p:qr:s:t:w:xyzD:FHI:LN:OP:RS:WZ::"))
1561                != EOF)
1562                 switch (ch) {
1563                 case 'b':
1564                         simulatedopcount = getnum(optarg, &endp);
1565                         if (!quiet)
1566                                 fprintf(stdout, "Will begin at operation %ld\n",
1567                                         simulatedopcount);
1568                         if (simulatedopcount == 0)
1569                                 usage();
1570                         simulatedopcount -= 1;
1571                         break;
1572                 case 'c':
1573                         closeprob = getnum(optarg, &endp);
1574                         if (!quiet)
1575                                 fprintf(stdout,
1576                                         "Chance of close/open is 1 in %d\n",
1577                                         closeprob);
1578                         if (closeprob <= 0)
1579                                 usage();
1580                         break;
1581                 case 'd':
1582                         debug++;
1583                         break;
1584                 case 'f':
1585                         flush = 1;
1586                         break;
1587                 case 'l':
1588                         maxfilelen = getnum(optarg, &endp);
1589                         if (maxfilelen <= 0)
1590                                 usage();
1591                         break;
1592                 case 'm':
1593                         monitorstart = getnum(optarg, &endp);
1594                         if (monitorstart < 0)
1595                                 usage();
1596                         if (!endp || *endp++ != ':')
1597                                 usage();
1598                         monitorend = getnum(endp, &endp);
1599                         if (monitorend < 0)
1600                                 usage();
1601                         if (monitorend == 0)
1602                                 monitorend = -1; /* aka infinity */
1603                         debug = 1;
1604                 case 'n':
1605                         sizechecks = 0;
1606                         break;
1607                 case 'o':
1608                         maxoplen = getnum(optarg, &endp);
1609                         if (maxoplen <= 0)
1610                                 usage();
1611                         break;
1612                 case 'p':
1613                         progressinterval = getnum(optarg, &endp);
1614                         if (progressinterval <= 0)
1615                                 usage();
1616                         break;
1617                 case 'q':
1618                         quiet = 1;
1619                         break;
1620                 case 'r':
1621                         readbdy = getnum(optarg, &endp);
1622                         if (readbdy <= 0)
1623                                 usage();
1624                         break;
1625                 case 's':
1626                         style = getnum(optarg, &endp);
1627                         if (style < 0 || style > 1)
1628                                 usage();
1629                         break;
1630                 case 't':
1631                         truncbdy = getnum(optarg, &endp);
1632                         if (truncbdy <= 0)
1633                                 usage();
1634                         break;
1635                 case 'w':
1636                         writebdy = getnum(optarg, &endp);
1637                         if (writebdy <= 0)
1638                                 usage();
1639                         break;
1640                 case 'y':
1641                         do_fsync = 1;
1642                         break;
1643                 case 'D':
1644                         debugstart = getnum(optarg, &endp);
1645                         if (debugstart < 1)
1646                                 usage();
1647                         break;
1648                 case 'F':
1649                         fallocate_calls = 0;
1650                         break;
1651                 case 'H':
1652                         punch_hole_calls = 0;
1653                         break;
1654                 case 'z':
1655                         zero_range_calls = 0;
1656                         break;
1657                 case 'I':
1658                         assign_fd_policy(optarg);
1659                         break;
1660                 case 'L':
1661                         lite = 1;
1662                         break;
1663                 case 'N':
1664                         numops = getnum(optarg, &endp);
1665                         if (numops < 0)
1666                                 usage();
1667                         break;
1668                 case 'O':
1669                         randomoplen = 0;
1670                         break;
1671                 case 'P':
1672                         strncpy(goodfile, optarg, sizeof(goodfile) - 1);
1673                         strncat(goodfile, "/", PATH_MAX - strlen(goodfile) - 1);
1674                         strncpy(logfile, optarg, sizeof(logfile) - 1);
1675                         strncat(logfile, "/", PATH_MAX - strlen(logfile) - 1);
1676                         dirpath = 1;
1677                         break;
1678                 case 'R':
1679                         mapped_reads = 0;
1680                         break;
1681                 case 'S':
1682                         seed = getnum(optarg, &endp);
1683                         if (seed == 0)
1684                                 seed = time(0) % 10000;
1685                         if (!quiet)
1686                                 fprintf(stdout, "Seed set to %d\n", seed);
1687                         if (seed < 0)
1688                                 usage();
1689                         break;
1690                 case 'W':
1691                         mapped_writes = 0;
1692                         if (!quiet)
1693                                 fprintf(stdout, "mapped writes DISABLED\n");
1694                         break;
1695                 case 'Z':
1696 #ifdef O_DIRECT
1697                         if (optarg)
1698                                 o_direct = getnum(optarg, &endp);
1699                         if (!optarg || o_direct == 0)
1700                                 o_direct = 1;
1701 #endif
1702                         break;
1703                 default:
1704                         usage();
1705                         /* NOTREACHED */
1706                 }
1707         argc -= optind;
1708         argv += optind;
1709         if (argc < 1)
1710                 usage();
1711         fname = argv[0];
1712
1713         signal(SIGHUP, cleanup);
1714         signal(SIGINT, cleanup);
1715         signal(SIGPIPE, cleanup);
1716         signal(SIGALRM, cleanup);
1717         signal(SIGTERM, cleanup);
1718         signal(SIGXCPU, cleanup);
1719         signal(SIGXFSZ, cleanup);
1720         signal(SIGVTALRM, cleanup);
1721         signal(SIGUSR1, cleanup);
1722         signal(SIGUSR2, cleanup);
1723         signal(SIGBUS, segv);
1724         signal(SIGSEGV, segv);
1725
1726         initstate(seed, state, 256);
1727         setstate(state);
1728
1729         open_test_files(argv, argc);
1730
1731         strncat(goodfile, dirpath ? my_basename(fname) : fname, 256);
1732         strncat(goodfile, ".fsxgood", PATH_MAX - strlen(goodfile) - 1);
1733         fsxgoodfd = open(goodfile, O_RDWR | O_CREAT | O_TRUNC, 0666);
1734         if (fsxgoodfd < 0) {
1735                 prterr(goodfile);
1736                 exit(92);
1737         }
1738         strncat(logfile, dirpath ? my_basename(fname) : fname, 256);
1739         strncat(logfile, ".fsxlog", PATH_MAX - strlen(logfile) - 1);
1740         fsxlogf = fopen(logfile, "w");
1741         if (!fsxlogf) {
1742                 prterr(logfile);
1743                 exit(93);
1744         }
1745         if (lite) {
1746                 off_t ret;
1747                 int fd = get_fd();
1748
1749                 maxfilelen = lseek(fd, (off_t)0, SEEK_END);
1750                 file_size = maxfilelen;
1751                 if (file_size == (off_t)-1) {
1752                         prterr(fname);
1753                         warn("%s: lseek eof", __func__);
1754                         exit(94);
1755                 }
1756                 ret = lseek(fd, (off_t)0, SEEK_SET);
1757                 if (ret == (off_t)-1) {
1758                         prterr(fname);
1759                         warn("%s: lseek 0", __func__);
1760                         exit(95);
1761                 }
1762         }
1763         original_buf = (char *)malloc(maxfilelen);
1764         if (!original_buf)
1765                 exit(96);
1766         for (i = 0; i < maxfilelen; i++)
1767                 original_buf[i] = random() % 256;
1768         if (o_direct) {
1769                 int ret;
1770
1771                 ret = posix_memalign((void **)&good_buf, writebdy, maxfilelen);
1772                 if (ret) {
1773                         prt("%s: posix_memalign failed: %s\n", __func__,
1774                             strerror(ret));
1775                         exit(96);
1776                 }
1777
1778                 ret = posix_memalign((void **)&temp_buf, readbdy, maxoplen);
1779                 if (ret) {
1780                         prt("%s: posix_memalign failed: %s\n", __func__,
1781                             strerror(ret));
1782                         exit(97);
1783                 }
1784         } else {
1785                 good_buf = malloc(maxfilelen);
1786                 if (!good_buf) {
1787                         prt("malloc failed.\n");
1788                         exit(98);
1789                 }
1790
1791                 temp_buf = malloc(maxoplen);
1792                 if (!temp_buf) {
1793                         prt("malloc failed.\n");
1794                         exit(99);
1795                 }
1796         }
1797         memset(good_buf, 0, maxfilelen);
1798         memset(temp_buf, 0, maxoplen);
1799
1800         if (lite) {     /* zero entire existing file */
1801                 ssize_t written;
1802                 int fd = get_fd();
1803
1804                 written = write(fd, good_buf, (size_t)maxfilelen);
1805                 if (written != maxfilelen) {
1806                         if (written == -1) {
1807                                 prterr(fname);
1808                                 warn("%s: error on write", __func__);
1809                         } else {
1810                                 warn("%s: short write, 0x%x bytes instead of 0x%lx\n",
1811                                      __func__, (unsigned int)written,
1812                                      maxfilelen);
1813                         }
1814                         exit(98);
1815                 }
1816         } else {
1817                 check_trunc_hack();
1818         }
1819
1820         if (fallocate_calls)
1821                 fallocate_calls = test_fallocate(0);
1822
1823         if (punch_hole_calls)
1824                 punch_hole_calls = test_fallocate(FALLOC_FL_PUNCH_HOLE |
1825                                                   FALLOC_FL_KEEP_SIZE);
1826
1827         if (zero_range_calls)
1828                 zero_range_calls = test_fallocate(FALLOC_FL_ZERO_RANGE);
1829
1830         fl_keep_size = test_fallocate(FALLOC_FL_KEEP_SIZE);
1831
1832         while (numops == -1 || numops--)
1833                 test();
1834
1835         close_test_files();
1836         prt("All operations completed A-OK!\n");
1837
1838         free(original_buf);
1839         free(good_buf);
1840         free(temp_buf);
1841
1842         return 0;
1843 }