d672925fab3aaea9a13a3949799216ef347c4c59
[xfstests-dev.git] / ltp / fsx.c
1 /*
2  *      Copyright (C) 1991, NeXT Computer, Inc.  All Rights Reserverd.
3  *
4  *      File:   fsx.c
5  *      Author: Avadis Tevanian, Jr.
6  *
7  *      File system exerciser. 
8  *
9  *      Rewritten 8/98 by Conrad Minshall.
10  *
11  *      Small changes to work under Linux -- davej.
12  *
13  *      Checks for mmap last-page zero fill.
14  */
15
16 #include "global.h"
17
18 #include <limits.h>
19 #include <time.h>
20 #include <strings.h>
21 #include <sys/file.h>
22 #include <sys/mman.h>
23 #include <stdbool.h>
24 #ifdef HAVE_ERR_H
25 #include <err.h>
26 #endif
27 #include <signal.h>
28 #include <stdio.h>
29 #include <stddef.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <stdarg.h>
33 #include <errno.h>
34 #ifdef AIO
35 #include <libaio.h>
36 #endif
37
38 #ifndef MAP_FILE
39 # define MAP_FILE 0
40 #endif
41
42 #define NUMPRINTCOLUMNS 32      /* # columns of data to print on each line */
43
44 /* Operation flags */
45
46 enum opflags { FL_NONE = 0, FL_SKIPPED = 1, FL_CLOSE_OPEN = 2, FL_KEEP_SIZE = 4 };
47
48 /*
49  *      A log entry is an operation and a bunch of arguments.
50  */
51
52 struct log_entry {
53         int     operation;
54         int     nr_args;
55         int     args[4];
56         enum opflags flags;
57 };
58
59 #define LOGSIZE 10000
60
61 struct log_entry        oplog[LOGSIZE]; /* the log */
62 int                     logptr = 0;     /* current position in log */
63 int                     logcount = 0;   /* total ops */
64
65 /*
66  * The operation matrix is complex due to conditional execution of different
67  * features. Hence when we come to deciding what operation to run, we need to
68  * be careful in how we select the different operations. The active operations
69  * are mapped to numbers as follows:
70  *
71  *                      lite    !lite   integrity
72  * READ:                0       0       0
73  * WRITE:               1       1       1
74  * MAPREAD:             2       2       2
75  * MAPWRITE:            3       3       3
76  * TRUNCATE:            -       4       4
77  * FALLOCATE:           -       5       5
78  * PUNCH HOLE:          -       6       6
79  * ZERO RANGE:          -       7       7
80  * COLLAPSE RANGE:      -       8       8
81  * FSYNC:               -       -       9
82  *
83  * When mapped read/writes are disabled, they are simply converted to normal
84  * reads and writes. When fallocate/fpunch calls are disabled, they are
85  * skipped.
86  *
87  * Because of the "lite" version, we also need to have different "maximum
88  * operation" defines to allow the ops to be selected correctly based on the
89  * mode being run.
90  */
91
92 enum {
93         /* common operations */
94         OP_READ = 0,
95         OP_WRITE,
96         OP_MAPREAD,
97         OP_MAPWRITE,
98         OP_MAX_LITE,
99
100         /* !lite operations */
101         OP_TRUNCATE = OP_MAX_LITE,
102         OP_FALLOCATE,
103         OP_PUNCH_HOLE,
104         OP_ZERO_RANGE,
105         OP_COLLAPSE_RANGE,
106         OP_INSERT_RANGE,
107         OP_CLONE_RANGE,
108         OP_MAX_FULL,
109
110         /* integrity operations */
111         OP_FSYNC = OP_MAX_FULL,
112         OP_MAX_INTEGRITY,
113 };
114
115 #undef PAGE_SIZE
116 #define PAGE_SIZE       getpagesize()
117 #undef PAGE_MASK
118 #define PAGE_MASK       (PAGE_SIZE - 1)
119
120 char    *original_buf;                  /* a pointer to the original data */
121 char    *good_buf;                      /* a pointer to the correct data */
122 char    *temp_buf;                      /* a pointer to the current data */
123 char    *fname;                         /* name of our test file */
124 char    *bname;                         /* basename of our test file */
125 char    *logdev;                        /* -i flag */
126 char    *logid;                         /* -j flag */
127 char    dname[1024];                    /* -P flag */
128 char    goodfile[PATH_MAX];
129 int     dirpath = 0;                    /* -P flag */
130 int     fd;                             /* fd for our test file */
131
132 blksize_t       block_size = 0;
133 off_t           file_size = 0;
134 off_t           biggest = 0;
135 unsigned long   testcalls = 0;          /* calls to function "test" */
136
137 unsigned long   simulatedopcount = 0;   /* -b flag */
138 int     closeprob = 0;                  /* -c flag */
139 int     debug = 0;                      /* -d flag */
140 unsigned long   debugstart = 0;         /* -D flag */
141 char    filldata = 0;                   /* -g flag */
142 int     flush = 0;                      /* -f flag */
143 int     do_fsync = 0;                   /* -y flag */
144 unsigned long   maxfilelen = 256 * 1024;        /* -l flag */
145 int     sizechecks = 1;                 /* -n flag disables them */
146 int     maxoplen = 64 * 1024;           /* -o flag */
147 int     quiet = 0;                      /* -q flag */
148 unsigned long progressinterval = 0;     /* -p flag */
149 int     readbdy = 1;                    /* -r flag */
150 int     style = 0;                      /* -s flag */
151 int     prealloc = 0;                   /* -x flag */
152 int     truncbdy = 1;                   /* -t flag */
153 int     writebdy = 1;                   /* -w flag */
154 long    monitorstart = -1;              /* -m flag */
155 long    monitorend = -1;                /* -m flag */
156 int     lite = 0;                       /* -L flag */
157 long    numops = -1;                    /* -N flag */
158 int     randomoplen = 1;                /* -O flag disables it */
159 int     seed = 1;                       /* -S flag */
160 int     mapped_writes = 1;              /* -W flag disables */
161 int     fallocate_calls = 1;            /* -F flag disables */
162 int     keep_size_calls = 1;            /* -K flag disables */
163 int     punch_hole_calls = 1;           /* -H flag disables */
164 int     zero_range_calls = 1;           /* -z flag disables */
165 int     collapse_range_calls = 1;       /* -C flag disables */
166 int     insert_range_calls = 1;         /* -I flag disables */
167 int     mapped_reads = 1;               /* -R flag disables it */
168 int     check_file = 0;                 /* -X flag enables */
169 int     clone_range_calls = 1;          /* -J flag disables */
170 int     integrity = 0;                  /* -i flag */
171 int     fsxgoodfd = 0;
172 int     o_direct;                       /* -Z */
173 int     aio = 0;
174 int     mark_nr = 0;
175
176 int page_size;
177 int page_mask;
178 int mmap_mask;
179 #ifdef AIO
180 int aio_rw(int rw, int fd, char *buf, unsigned len, unsigned offset);
181 #define READ 0
182 #define WRITE 1
183 #define fsxread(a,b,c,d)        aio_rw(READ, a,b,c,d)
184 #define fsxwrite(a,b,c,d)       aio_rw(WRITE, a,b,c,d)
185 #else
186 #define fsxread(a,b,c,d)        read(a,b,c)
187 #define fsxwrite(a,b,c,d)       write(a,b,c)
188 #endif
189
190 const char *replayops = NULL;
191 const char *recordops = NULL;
192 FILE *  fsxlogf = NULL;
193 FILE *  replayopsf = NULL;
194 char opsfile[PATH_MAX];
195 int badoff = -1;
196 int closeopen = 0;
197
198 static void *round_ptr_up(void *ptr, unsigned long align, unsigned long offset)
199 {
200         unsigned long ret = (unsigned long)ptr;
201
202         ret = ((ret + align - 1) & ~(align - 1));
203         ret += offset;
204         return (void *)ret;
205 }
206
207 void
208 vwarnc(int code, const char *fmt, va_list ap)
209 {
210         if (logid)
211                 fprintf(stderr, "%s: ", logid);
212         fprintf(stderr, "fsx: ");
213         if (fmt != NULL) {
214                 vfprintf(stderr, fmt, ap);
215                 fprintf(stderr, ": ");
216         }
217         fprintf(stderr, "%s\n", strerror(code));
218 }
219
220 void
221 warn(const char * fmt, ...)  {
222         va_list ap;
223         va_start(ap, fmt);
224         vwarnc(errno, fmt, ap);
225         va_end(ap);
226 }
227
228 void
229 prt(const char *fmt, ...)
230 {
231         va_list args;
232
233         if (logid)
234                 fprintf(stdout, "%s: ", logid);
235         va_start(args, fmt);
236         vfprintf(stdout, fmt, args);
237         va_end(args);
238         if (fsxlogf) {
239                 va_start(args, fmt);
240                 vfprintf(fsxlogf, fmt, args);
241                 va_end(args);
242         }
243 }
244
245 void
246 prterr(const char *prefix)
247 {
248         prt("%s%s%s\n", prefix, prefix ? ": " : "", strerror(errno));
249 }
250
251
252 static const char *op_names[] = {
253         [OP_READ] = "read",
254         [OP_WRITE] = "write",
255         [OP_MAPREAD] = "mapread",
256         [OP_MAPWRITE] = "mapwrite",
257         [OP_TRUNCATE] = "truncate",
258         [OP_FALLOCATE] = "fallocate",
259         [OP_PUNCH_HOLE] = "punch_hole",
260         [OP_ZERO_RANGE] = "zero_range",
261         [OP_COLLAPSE_RANGE] = "collapse_range",
262         [OP_INSERT_RANGE] = "insert_range",
263         [OP_CLONE_RANGE] = "clone_range",
264         [OP_FSYNC] = "fsync",
265 };
266
267 static const char *op_name(int operation)
268 {
269         if (operation >= 0 &&
270             operation < sizeof(op_names) / sizeof(op_names[0]))
271                 return op_names[operation];
272         return NULL;
273 }
274
275 static int op_code(const char *name)
276 {
277         int i;
278
279         for (i = 0; i < sizeof(op_names) / sizeof(op_names[0]); i++)
280                 if (op_names[i] && strcmp(name, op_names[i]) == 0)
281                         return i;
282         return -1;
283 }
284
285 void
286 log5(int operation, int arg0, int arg1, int arg2, enum opflags flags)
287 {
288         struct log_entry *le;
289
290         le = &oplog[logptr];
291         le->operation = operation;
292         if (closeopen)
293                 flags |= FL_CLOSE_OPEN;
294         le->args[0] = arg0;
295         le->args[1] = arg1;
296         le->args[2] = arg2;
297         le->args[3] = file_size;
298         le->nr_args = 4;
299         le->flags = flags;
300         logptr++;
301         logcount++;
302         if (logptr >= LOGSIZE)
303                 logptr = 0;
304 }
305
306 void
307 log4(int operation, int arg0, int arg1, enum opflags flags)
308 {
309         struct log_entry *le;
310
311         le = &oplog[logptr];
312         le->operation = operation;
313         if (closeopen)
314                 flags |= FL_CLOSE_OPEN;
315         le->args[0] = arg0;
316         le->args[1] = arg1;
317         le->args[2] = file_size;
318         le->nr_args = 3;
319         le->flags = flags;
320         logptr++;
321         logcount++;
322         if (logptr >= LOGSIZE)
323                 logptr = 0;
324 }
325
326 void
327 logdump(void)
328 {
329         FILE    *logopsf;
330         int     i, count, down;
331         struct log_entry        *lp;
332
333         prt("LOG DUMP (%d total operations):\n", logcount);
334
335         logopsf = fopen(opsfile, "w");
336         if (!logopsf)
337                 prterr(opsfile);
338
339         if (logcount < LOGSIZE) {
340                 i = 0;
341                 count = logcount;
342         } else {
343                 i = logptr;
344                 count = LOGSIZE;
345         }
346         for ( ; count > 0; count--) {
347                 bool overlap, overlap2;
348                 int opnum;
349
350                 opnum = i+1 + (logcount/LOGSIZE)*LOGSIZE;
351                 prt("%d(%3d mod 256): ", opnum, opnum%256);
352                 lp = &oplog[i];
353
354                 overlap = badoff >= lp->args[0] &&
355                           badoff < lp->args[0] + lp->args[1];
356
357                 if (lp->flags & FL_SKIPPED) {
358                         prt("SKIPPED (no operation)");
359                         goto skipped;
360                 }
361
362                 switch (lp->operation) {
363                 case OP_MAPREAD:
364                         prt("MAPREAD  0x%x thru 0x%x\t(0x%x bytes)",
365                             lp->args[0], lp->args[0] + lp->args[1] - 1,
366                             lp->args[1]);
367                         if (overlap)
368                                 prt("\t***RRRR***");
369                         break;
370                 case OP_MAPWRITE:
371                         prt("MAPWRITE 0x%x thru 0x%x\t(0x%x bytes)",
372                             lp->args[0], lp->args[0] + lp->args[1] - 1,
373                             lp->args[1]);
374                         if (overlap)
375                                 prt("\t******WWWW");
376                         break;
377                 case OP_READ:
378                         prt("READ     0x%x thru 0x%x\t(0x%x bytes)",
379                             lp->args[0], lp->args[0] + lp->args[1] - 1,
380                             lp->args[1]);
381                         if (overlap)
382                                 prt("\t***RRRR***");
383                         break;
384                 case OP_WRITE:
385                         prt("WRITE    0x%x thru 0x%x\t(0x%x bytes)",
386                             lp->args[0], lp->args[0] + lp->args[1] - 1,
387                             lp->args[1]);
388                         if (lp->args[0] > lp->args[2])
389                                 prt(" HOLE");
390                         else if (lp->args[0] + lp->args[1] > lp->args[2])
391                                 prt(" EXTEND");
392                         overlap = (badoff >= lp->args[0] ||
393                                    badoff >=lp->args[2]) &&
394                                   badoff < lp->args[0] + lp->args[1];
395                         if (overlap)
396                                 prt("\t***WWWW");
397                         break;
398                 case OP_TRUNCATE:
399                         down = lp->args[1] < lp->args[2];
400                         prt("TRUNCATE %s\tfrom 0x%x to 0x%x",
401                             down ? "DOWN" : "UP", lp->args[2], lp->args[1]);
402                         overlap = badoff >= lp->args[1 + !down] &&
403                                   badoff < lp->args[1 + !!down];
404                         if (overlap)
405                                 prt("\t******WWWW");
406                         break;
407                 case OP_FALLOCATE:
408                         /* 0: offset 1: length 2: where alloced */
409                         prt("FALLOC   0x%x thru 0x%x\t(0x%x bytes) ",
410                                 lp->args[0], lp->args[0] + lp->args[1],
411                                 lp->args[1]);
412                         if (lp->args[0] + lp->args[1] <= lp->args[2])
413                                 prt("INTERIOR");
414                         else if (lp->flags & FL_KEEP_SIZE)
415                                 prt("PAST_EOF");
416                         else
417                                 prt("EXTENDING");
418                         if (overlap)
419                                 prt("\t******FFFF");
420                         break;
421                 case OP_PUNCH_HOLE:
422                         prt("PUNCH    0x%x thru 0x%x\t(0x%x bytes)",
423                             lp->args[0], lp->args[0] + lp->args[1] - 1,
424                             lp->args[1]);
425                         if (overlap)
426                                 prt("\t******PPPP");
427                         break;
428                 case OP_ZERO_RANGE:
429                         prt("ZERO     0x%x thru 0x%x\t(0x%x bytes)",
430                             lp->args[0], lp->args[0] + lp->args[1] - 1,
431                             lp->args[1]);
432                         if (overlap)
433                                 prt("\t******ZZZZ");
434                         break;
435                 case OP_COLLAPSE_RANGE:
436                         prt("COLLAPSE 0x%x thru 0x%x\t(0x%x bytes)",
437                             lp->args[0], lp->args[0] + lp->args[1] - 1,
438                             lp->args[1]);
439                         if (overlap)
440                                 prt("\t******CCCC");
441                         break;
442                 case OP_INSERT_RANGE:
443                         prt("INSERT 0x%x thru 0x%x\t(0x%x bytes)",
444                             lp->args[0], lp->args[0] + lp->args[1] - 1,
445                             lp->args[1]);
446                         if (overlap)
447                                 prt("\t******IIII");
448                         break;
449                 case OP_CLONE_RANGE:
450                         prt("CLONE 0x%x thru 0x%x\t(0x%x bytes) to 0x%x thru 0x%x",
451                             lp->args[0], lp->args[0] + lp->args[1] - 1,
452                             lp->args[1],
453                             lp->args[2], lp->args[2] + lp->args[1] - 1);
454                         overlap2 = badoff >= lp->args[2] &&
455                                   badoff < lp->args[2] + lp->args[1];
456                         if (overlap && overlap2)
457                                 prt("\tJJJJ**JJJJ");
458                         else if (overlap)
459                                 prt("\tJJJJ******");
460                         else if (overlap2)
461                                 prt("\t******JJJJ");
462                         break;
463                 case OP_FSYNC:
464                         prt("FSYNC");
465                         break;
466                 default:
467                         prt("BOGUS LOG ENTRY (operation code = %d)!",
468                             lp->operation);
469                         continue;
470                 }
471
472             skipped:
473                 if (lp->flags & FL_CLOSE_OPEN)
474                         prt("\n\t\tCLOSE/OPEN");
475                 prt("\n");
476                 i++;
477                 if (i == LOGSIZE)
478                         i = 0;
479
480                 if (logopsf) {
481                         int j;
482
483                         if (lp->flags & FL_SKIPPED)
484                                 fprintf(logopsf, "skip ");
485                         fprintf(logopsf, "%s", op_name(lp->operation));
486                         for (j = 0; j < lp->nr_args; j++)
487                                 fprintf(logopsf, " 0x%x", lp->args[j]);
488                         if (lp->flags & FL_KEEP_SIZE)
489                                 fprintf(logopsf, " keep_size");
490                         if (lp->flags & FL_CLOSE_OPEN)
491                                 fprintf(logopsf, " close_open");
492                         if (overlap)
493                                 fprintf(logopsf, " *");
494                         fprintf(logopsf, "\n");
495                 }
496         }
497
498         if (logopsf) {
499                 if (fclose(logopsf) != 0)
500                         prterr(opsfile);
501                 else
502                         prt("Log of operations saved to \"%s\"; "
503                             "replay with --replay-ops\n",
504                             opsfile);
505         }
506 }
507
508
509 void
510 save_buffer(char *buffer, off_t bufferlength, int fd)
511 {
512         off_t ret;
513         ssize_t byteswritten;
514
515         if (fd <= 0 || bufferlength == 0)
516                 return;
517
518         if (bufferlength > SSIZE_MAX) {
519                 prt("fsx flaw: overflow in save_buffer\n");
520                 exit(67);
521         }
522         if (lite) {
523                 off_t size_by_seek = lseek(fd, (off_t)0, SEEK_END);
524                 if (size_by_seek == (off_t)-1)
525                         prterr("save_buffer: lseek eof");
526                 else if (bufferlength > size_by_seek) {
527                         warn("save_buffer: .fsxgood file too short... will save 0x%llx bytes instead of 0x%llx\n", (unsigned long long)size_by_seek,
528                              (unsigned long long)bufferlength);
529                         bufferlength = size_by_seek;
530                 }
531         }
532
533         ret = lseek(fd, (off_t)0, SEEK_SET);
534         if (ret == (off_t)-1)
535                 prterr("save_buffer: lseek 0");
536         
537         byteswritten = write(fd, buffer, (size_t)bufferlength);
538         if (byteswritten != bufferlength) {
539                 if (byteswritten == -1)
540                         prterr("save_buffer write");
541                 else
542                         warn("save_buffer: short write, 0x%x bytes instead of 0x%llx\n",
543                              (unsigned)byteswritten,
544                              (unsigned long long)bufferlength);
545         }
546 }
547
548
549 void
550 report_failure(int status)
551 {
552         logdump();
553         
554         if (fsxgoodfd) {
555                 if (good_buf) {
556                         save_buffer(good_buf, file_size, fsxgoodfd);
557                         prt("Correct content saved for comparison\n");
558                         prt("(maybe hexdump \"%s\" vs \"%s\")\n",
559                             fname, goodfile);
560                 }
561                 close(fsxgoodfd);
562         }
563         exit(status);
564 }
565
566
567 #define short_at(cp) ((unsigned short)((*((unsigned char *)(cp)) << 8) | \
568                                         *(((unsigned char *)(cp)) + 1)))
569
570 void
571 mark_log(void)
572 {
573         char command[256];
574         int ret;
575
576         snprintf(command, 256, "dmsetup message %s 0 mark %s.mark%d", logdev,
577                  bname, mark_nr);
578         ret = system(command);
579         if (ret) {
580                 prterr("dmsetup mark failed");
581                 exit(211);
582         }
583 }
584
585 void
586 dump_fsync_buffer(void)
587 {
588         char fname_buffer[PATH_MAX];
589         int good_fd;
590
591         if (!good_buf)
592                 return;
593
594         snprintf(fname_buffer, sizeof(fname_buffer), "%s%s.mark%d", dname,
595                  bname, mark_nr);
596         good_fd = open(fname_buffer, O_WRONLY|O_CREAT|O_TRUNC, 0666);
597         if (good_fd < 0) {
598                 prterr(fname_buffer);
599                 exit(212);
600         }
601
602         save_buffer(good_buf, file_size, good_fd);
603         close(good_fd);
604         prt("Dumped fsync buffer to %s\n", fname_buffer + dirpath);
605 }
606
607 void
608 check_buffers(char *buf, unsigned offset, unsigned size)
609 {
610         unsigned char c, t;
611         unsigned i = 0;
612         unsigned n = 0;
613         unsigned op = 0;
614         unsigned bad = 0;
615
616         if (memcmp(good_buf + offset, buf, size) != 0) {
617                 prt("READ BAD DATA: offset = 0x%x, size = 0x%x, fname = %s\n",
618                     offset, size, fname);
619                 prt("OFFSET\tGOOD\tBAD\tRANGE\n");
620                 while (size > 0) {
621                         c = good_buf[offset];
622                         t = buf[i];
623                         if (c != t) {
624                                 if (n < 16) {
625                                         bad = short_at(&buf[i]);
626                                         prt("0x%05x\t0x%04x\t0x%04x", offset,
627                                             short_at(&good_buf[offset]), bad);
628                                         op = buf[offset & 1 ? i+1 : i];
629                                         prt("\t0x%05x\n", n);
630                                         if (op)
631                                                 prt("operation# (mod 256) for "
632                                                   "the bad data may be %u\n",
633                                                 ((unsigned)op & 0xff));
634                                         else
635                                                 prt("operation# (mod 256) for "
636                                                   "the bad data unknown, check"
637                                                   " HOLE and EXTEND ops\n");
638                                 }
639                                 n++;
640                                 badoff = offset;
641                         }
642                         offset++;
643                         i++;
644                         size--;
645                 }
646                 report_failure(110);
647         }
648 }
649
650
651 void
652 check_size(void)
653 {
654         struct stat     statbuf;
655         off_t   size_by_seek;
656
657         if (fstat(fd, &statbuf)) {
658                 prterr("check_size: fstat");
659                 statbuf.st_size = -1;
660         }
661         size_by_seek = lseek(fd, (off_t)0, SEEK_END);
662         if (file_size != statbuf.st_size || file_size != size_by_seek) {
663                 prt("Size error: expected 0x%llx stat 0x%llx seek 0x%llx\n",
664                     (unsigned long long)file_size,
665                     (unsigned long long)statbuf.st_size,
666                     (unsigned long long)size_by_seek);
667                 report_failure(120);
668         }
669 }
670
671
672 void
673 check_trunc_hack(void)
674 {
675         struct stat statbuf;
676         off_t offset = file_size + (off_t)100000;
677
678         if (ftruncate(fd, file_size))
679                 goto ftruncate_err;
680         if (ftruncate(fd, offset))
681                 goto ftruncate_err;
682         fstat(fd, &statbuf);
683         if (statbuf.st_size != offset) {
684                 prt("no extend on truncate! not posix!\n");
685                 exit(130);
686         }
687         if (ftruncate(fd, file_size)) {
688 ftruncate_err:
689                 prterr("check_trunc_hack: ftruncate");
690                 exit(131);
691         }
692 }
693
694 void
695 doflush(unsigned offset, unsigned size)
696 {
697         unsigned pg_offset;
698         unsigned map_size;
699         char    *p;
700
701         if (o_direct == O_DIRECT)
702                 return;
703
704         pg_offset = offset & mmap_mask;
705         map_size  = pg_offset + size;
706
707         if ((p = (char *)mmap(0, map_size, PROT_READ | PROT_WRITE,
708                               MAP_FILE | MAP_SHARED, fd,
709                               (off_t)(offset - pg_offset))) == (char *)-1) {
710                 prterr("doflush: mmap");
711                 report_failure(202);
712         }
713         if (msync(p, map_size, MS_INVALIDATE) != 0) {
714                 prterr("doflush: msync");
715                 report_failure(203);
716         }
717         if (munmap(p, map_size) != 0) {
718                 prterr("doflush: munmap");
719                 report_failure(204);
720         }
721 }
722
723 void
724 doread(unsigned offset, unsigned size)
725 {
726         off_t ret;
727         unsigned iret;
728
729         offset -= offset % readbdy;
730         if (o_direct)
731                 size -= size % readbdy;
732         if (size == 0) {
733                 if (!quiet && testcalls > simulatedopcount && !o_direct)
734                         prt("skipping zero size read\n");
735                 log4(OP_READ, offset, size, FL_SKIPPED);
736                 return;
737         }
738         if (size + offset > file_size) {
739                 if (!quiet && testcalls > simulatedopcount)
740                         prt("skipping seek/read past end of file\n");
741                 log4(OP_READ, offset, size, FL_SKIPPED);
742                 return;
743         }
744
745         log4(OP_READ, offset, size, FL_NONE);
746
747         if (testcalls <= simulatedopcount)
748                 return;
749
750         if (!quiet &&
751                 ((progressinterval && testcalls % progressinterval == 0)  ||
752                 (debug &&
753                        (monitorstart == -1 ||
754                         (offset + size > monitorstart &&
755                         (monitorend == -1 || offset <= monitorend))))))
756                 prt("%lu read\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
757                     offset, offset + size - 1, size);
758         ret = lseek(fd, (off_t)offset, SEEK_SET);
759         if (ret == (off_t)-1) {
760                 prterr("doread: lseek");
761                 report_failure(140);
762         }
763         iret = fsxread(fd, temp_buf, size, offset);
764         if (iret != size) {
765                 if (iret == -1)
766                         prterr("doread: read");
767                 else
768                         prt("short read: 0x%x bytes instead of 0x%x\n",
769                             iret, size);
770                 report_failure(141);
771         }
772         check_buffers(temp_buf, offset, size);
773 }
774
775 void
776 check_eofpage(char *s, unsigned offset, char *p, int size)
777 {
778         unsigned long last_page, should_be_zero;
779
780         if (offset + size <= (file_size & ~page_mask))
781                 return;
782         /*
783          * we landed in the last page of the file
784          * test to make sure the VM system provided 0's 
785          * beyond the true end of the file mapping
786          * (as required by mmap def in 1996 posix 1003.1)
787          */
788         last_page = ((unsigned long)p + (offset & page_mask) + size) & ~page_mask;
789
790         for (should_be_zero = last_page + (file_size & page_mask);
791              should_be_zero < last_page + page_size;
792              should_be_zero++)
793                 if (*(char *)should_be_zero) {
794                         prt("Mapped %s: non-zero data past EOF (0x%llx) page offset 0x%x is 0x%04x\n",
795                             s, file_size - 1, should_be_zero & page_mask,
796                             short_at(should_be_zero));
797                         report_failure(205);
798                 }
799 }
800
801 void
802 check_contents(void)
803 {
804         static char *check_buf;
805         unsigned offset = 0;
806         unsigned size = file_size;
807         unsigned map_offset;
808         unsigned map_size;
809         char *p;
810         off_t ret;
811         unsigned iret;
812
813         if (!check_buf) {
814                 check_buf = (char *) malloc(maxfilelen + writebdy);
815                 assert(check_buf != NULL);
816                 check_buf = round_ptr_up(check_buf, writebdy, 0);
817                 memset(check_buf, '\0', maxfilelen);
818         }
819
820         if (o_direct)
821                 size -= size % readbdy;
822         if (size == 0)
823                 return;
824
825         ret = lseek(fd, (off_t)offset, SEEK_SET);
826         if (ret == (off_t)-1) {
827                 prterr("doread: lseek");
828                 report_failure(140);
829         }
830
831         iret = fsxread(fd, check_buf, size, offset);
832         if (iret != size) {
833                 if (iret == -1)
834                         prterr("check_contents: read");
835                 else
836                         prt("short check read: 0x%x bytes instead of 0x%x\n",
837                             iret, size);
838                 report_failure(141);
839         }
840         check_buffers(check_buf, offset, size);
841
842         /* Map eof page, check it */
843         map_offset = size - (size & PAGE_MASK);
844         if (map_offset == size)
845                 map_offset -= PAGE_SIZE;
846         map_size  = size - map_offset;
847
848         p = mmap(0, map_size, PROT_READ, MAP_SHARED, fd, map_offset);
849         if (p == MAP_FAILED) {
850                 prterr("check_contents: mmap");
851                 report_failure(190);
852         }
853         check_eofpage("check_contents", map_offset, p, map_size);
854
855         if (munmap(p, map_size) != 0) {
856                 prterr("check_contents: munmap");
857                 report_failure(191);
858         }
859 }
860
861 void
862 domapread(unsigned offset, unsigned size)
863 {
864         unsigned pg_offset;
865         unsigned map_size;
866         char    *p;
867
868         offset -= offset % readbdy;
869         if (size == 0) {
870                 if (!quiet && testcalls > simulatedopcount)
871                         prt("skipping zero size read\n");
872                 log4(OP_MAPREAD, offset, size, FL_SKIPPED);
873                 return;
874         }
875         if (size + offset > file_size) {
876                 if (!quiet && testcalls > simulatedopcount)
877                         prt("skipping seek/read past end of file\n");
878                 log4(OP_MAPREAD, offset, size, FL_SKIPPED);
879                 return;
880         }
881
882         log4(OP_MAPREAD, offset, size, FL_NONE);
883
884         if (testcalls <= simulatedopcount)
885                 return;
886
887         if (!quiet &&
888                 ((progressinterval && testcalls % progressinterval == 0) ||
889                        (debug &&
890                        (monitorstart == -1 ||
891                         (offset + size > monitorstart &&
892                         (monitorend == -1 || offset <= monitorend))))))
893                 prt("%lu mapread\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
894                     offset, offset + size - 1, size);
895
896         pg_offset = offset & PAGE_MASK;
897         map_size  = pg_offset + size;
898
899         if ((p = (char *)mmap(0, map_size, PROT_READ, MAP_SHARED, fd,
900                               (off_t)(offset - pg_offset))) == (char *)-1) {
901                 prterr("domapread: mmap");
902                 report_failure(190);
903         }
904         memcpy(temp_buf, p + pg_offset, size);
905
906         check_eofpage("Read", offset, p, size);
907
908         if (munmap(p, map_size) != 0) {
909                 prterr("domapread: munmap");
910                 report_failure(191);
911         }
912
913         check_buffers(temp_buf, offset, size);
914 }
915
916
917 void
918 gendata(char *original_buf, char *good_buf, unsigned offset, unsigned size)
919 {
920         while (size--) {
921                 if (filldata) {
922                         good_buf[offset] = filldata;
923                 } else {
924                         good_buf[offset] = testcalls % 256;
925                         if (offset % 2)
926                                 good_buf[offset] += original_buf[offset];
927                 }
928                 offset++;
929         }
930 }
931
932
933 void
934 dowrite(unsigned offset, unsigned size)
935 {
936         off_t ret;
937         unsigned iret;
938
939         offset -= offset % writebdy;
940         if (o_direct)
941                 size -= size % writebdy;
942         if (size == 0) {
943                 if (!quiet && testcalls > simulatedopcount && !o_direct)
944                         prt("skipping zero size write\n");
945                 log4(OP_WRITE, offset, size, FL_SKIPPED);
946                 return;
947         }
948
949         log4(OP_WRITE, offset, size, FL_NONE);
950
951         gendata(original_buf, good_buf, offset, size);
952         if (file_size < offset + size) {
953                 if (file_size < offset)
954                         memset(good_buf + file_size, '\0', offset - file_size);
955                 file_size = offset + size;
956                 if (lite) {
957                         warn("Lite file size bug in fsx!");
958                         report_failure(149);
959                 }
960         }
961
962         if (testcalls <= simulatedopcount)
963                 return;
964
965         if (!quiet &&
966                 ((progressinterval && testcalls % progressinterval == 0) ||
967                        (debug &&
968                        (monitorstart == -1 ||
969                         (offset + size > monitorstart &&
970                         (monitorend == -1 || offset <= monitorend))))))
971                 prt("%lu write\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
972                     offset, offset + size - 1, size);
973         ret = lseek(fd, (off_t)offset, SEEK_SET);
974         if (ret == (off_t)-1) {
975                 prterr("dowrite: lseek");
976                 report_failure(150);
977         }
978         iret = fsxwrite(fd, good_buf + offset, size, offset);
979         if (iret != size) {
980                 if (iret == -1)
981                         prterr("dowrite: write");
982                 else
983                         prt("short write: 0x%x bytes instead of 0x%x\n",
984                             iret, size);
985                 report_failure(151);
986         }
987         if (do_fsync) {
988                 if (fsync(fd)) {
989                         prt("fsync() failed: %s\n", strerror(errno));
990                         report_failure(152);
991                 }
992         }
993         if (flush) {
994                 doflush(offset, size);
995         }
996 }
997
998
999 void
1000 domapwrite(unsigned offset, unsigned size)
1001 {
1002         unsigned pg_offset;
1003         unsigned map_size;
1004         off_t    cur_filesize;
1005         char    *p;
1006
1007         offset -= offset % writebdy;
1008         if (size == 0) {
1009                 if (!quiet && testcalls > simulatedopcount)
1010                         prt("skipping zero size write\n");
1011                 log4(OP_MAPWRITE, offset, size, FL_SKIPPED);
1012                 return;
1013         }
1014         cur_filesize = file_size;
1015
1016         log4(OP_MAPWRITE, offset, size, FL_NONE);
1017
1018         gendata(original_buf, good_buf, offset, size);
1019         if (file_size < offset + size) {
1020                 if (file_size < offset)
1021                         memset(good_buf + file_size, '\0', offset - file_size);
1022                 file_size = offset + size;
1023                 if (lite) {
1024                         warn("Lite file size bug in fsx!");
1025                         report_failure(200);
1026                 }
1027         }
1028
1029         if (testcalls <= simulatedopcount)
1030                 return;
1031
1032         if (!quiet &&
1033                 ((progressinterval && testcalls % progressinterval == 0) ||
1034                        (debug &&
1035                        (monitorstart == -1 ||
1036                         (offset + size > monitorstart &&
1037                         (monitorend == -1 || offset <= monitorend))))))
1038                 prt("%lu mapwrite\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
1039                     offset, offset + size - 1, size);
1040
1041         if (file_size > cur_filesize) {
1042                 if (ftruncate(fd, file_size) == -1) {
1043                         prterr("domapwrite: ftruncate");
1044                         exit(201);
1045                 }
1046         }
1047         pg_offset = offset & PAGE_MASK;
1048         map_size  = pg_offset + size;
1049
1050         if ((p = (char *)mmap(0, map_size, PROT_READ | PROT_WRITE,
1051                               MAP_FILE | MAP_SHARED, fd,
1052                               (off_t)(offset - pg_offset))) == (char *)-1) {
1053                 prterr("domapwrite: mmap");
1054                 report_failure(202);
1055         }
1056         memcpy(p + pg_offset, good_buf + offset, size);
1057         if (msync(p, map_size, MS_SYNC) != 0) {
1058                 prterr("domapwrite: msync");
1059                 report_failure(203);
1060         }
1061
1062         check_eofpage("Write", offset, p, size);
1063
1064         if (munmap(p, map_size) != 0) {
1065                 prterr("domapwrite: munmap");
1066                 report_failure(204);
1067         }
1068 }
1069
1070
1071 void
1072 dotruncate(unsigned size)
1073 {
1074         int oldsize = file_size;
1075
1076         size -= size % truncbdy;
1077         if (size > biggest) {
1078                 biggest = size;
1079                 if (!quiet && testcalls > simulatedopcount)
1080                         prt("truncating to largest ever: 0x%x\n", size);
1081         }
1082
1083         log4(OP_TRUNCATE, 0, size, FL_NONE);
1084
1085         if (size > file_size)
1086                 memset(good_buf + file_size, '\0', size - file_size);
1087         file_size = size;
1088
1089         if (testcalls <= simulatedopcount)
1090                 return;
1091         
1092         if ((progressinterval && testcalls % progressinterval == 0) ||
1093             (debug && (monitorstart == -1 || monitorend == -1 ||
1094                       size <= monitorend)))
1095                 prt("%lu trunc\tfrom 0x%x to 0x%x\n", testcalls, oldsize, size);
1096         if (ftruncate(fd, (off_t)size) == -1) {
1097                 prt("ftruncate1: %x\n", size);
1098                 prterr("dotruncate: ftruncate");
1099                 report_failure(160);
1100         }
1101 }
1102
1103 #ifdef FALLOC_FL_PUNCH_HOLE
1104 void
1105 do_punch_hole(unsigned offset, unsigned length)
1106 {
1107         unsigned end_offset;
1108         int max_offset = 0;
1109         int max_len = 0;
1110         int mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE;
1111
1112         if (length == 0) {
1113                 if (!quiet && testcalls > simulatedopcount)
1114                         prt("skipping zero length punch hole\n");
1115                 log4(OP_PUNCH_HOLE, offset, length, FL_SKIPPED);
1116                 return;
1117         }
1118
1119         if (file_size <= (loff_t)offset) {
1120                 if (!quiet && testcalls > simulatedopcount)
1121                         prt("skipping hole punch off the end of the file\n");
1122                 log4(OP_PUNCH_HOLE, offset, length, FL_SKIPPED);
1123                 return;
1124         }
1125
1126         end_offset = offset + length;
1127
1128         log4(OP_PUNCH_HOLE, offset, length, FL_NONE);
1129
1130         if (testcalls <= simulatedopcount)
1131                 return;
1132
1133         if ((progressinterval && testcalls % progressinterval == 0) ||
1134             (debug && (monitorstart == -1 || monitorend == -1 ||
1135                       end_offset <= monitorend))) {
1136                 prt("%lu punch\tfrom 0x%x to 0x%x, (0x%x bytes)\n", testcalls,
1137                         offset, offset+length, length);
1138         }
1139         if (fallocate(fd, mode, (loff_t)offset, (loff_t)length) == -1) {
1140                 prt("punch hole: 0x%x to 0x%x\n", offset, offset + length);
1141                 prterr("do_punch_hole: fallocate");
1142                 report_failure(161);
1143         }
1144
1145
1146         max_offset = offset < file_size ? offset : file_size;
1147         max_len = max_offset + length <= file_size ? length :
1148                         file_size - max_offset;
1149         memset(good_buf + max_offset, '\0', max_len);
1150 }
1151
1152 #else
1153 void
1154 do_punch_hole(unsigned offset, unsigned length)
1155 {
1156         return;
1157 }
1158 #endif
1159
1160 #ifdef FALLOC_FL_ZERO_RANGE
1161 void
1162 do_zero_range(unsigned offset, unsigned length, int keep_size)
1163 {
1164         unsigned end_offset;
1165         int mode = FALLOC_FL_ZERO_RANGE;
1166
1167         if (length == 0) {
1168                 if (!quiet && testcalls > simulatedopcount)
1169                         prt("skipping zero length zero range\n");
1170                 log4(OP_ZERO_RANGE, offset, length, FL_SKIPPED |
1171                      (keep_size ? FL_KEEP_SIZE : FL_NONE));
1172                 return;
1173         }
1174
1175         end_offset = keep_size ? 0 : offset + length;
1176
1177         if (end_offset > biggest) {
1178                 biggest = end_offset;
1179                 if (!quiet && testcalls > simulatedopcount)
1180                         prt("zero_range to largest ever: 0x%x\n", end_offset);
1181         }
1182
1183         /*
1184          * last arg matches fallocate string array index in logdump:
1185          *      0: allocate past EOF
1186          *      1: extending prealloc
1187          *      2: interior prealloc
1188          */
1189         log4(OP_ZERO_RANGE, offset, length,
1190              keep_size ? FL_KEEP_SIZE : FL_NONE);
1191
1192         if (testcalls <= simulatedopcount)
1193                 return;
1194
1195         if ((progressinterval && testcalls % progressinterval == 0) ||
1196             (debug && (monitorstart == -1 || monitorend == -1 ||
1197                       end_offset <= monitorend))) {
1198                 prt("%lu zero\tfrom 0x%x to 0x%x, (0x%x bytes)\n", testcalls,
1199                         offset, offset+length, length);
1200         }
1201         if (fallocate(fd, mode, (loff_t)offset, (loff_t)length) == -1) {
1202                 prt("zero range: 0x%x to 0x%x\n", offset, offset + length);
1203                 prterr("do_zero_range: fallocate");
1204                 report_failure(161);
1205         }
1206
1207         memset(good_buf + offset, '\0', length);
1208 }
1209
1210 #else
1211 void
1212 do_zero_range(unsigned offset, unsigned length, int keep_size)
1213 {
1214         return;
1215 }
1216 #endif
1217
1218 #ifdef FALLOC_FL_COLLAPSE_RANGE
1219 void
1220 do_collapse_range(unsigned offset, unsigned length)
1221 {
1222         unsigned end_offset;
1223         int mode = FALLOC_FL_COLLAPSE_RANGE;
1224
1225         if (length == 0) {
1226                 if (!quiet && testcalls > simulatedopcount)
1227                         prt("skipping zero length collapse range\n");
1228                 log4(OP_COLLAPSE_RANGE, offset, length, FL_SKIPPED);
1229                 return;
1230         }
1231
1232         end_offset = offset + length;
1233         if ((loff_t)end_offset >= file_size) {
1234                 if (!quiet && testcalls > simulatedopcount)
1235                         prt("skipping collapse range behind EOF\n");
1236                 log4(OP_COLLAPSE_RANGE, offset, length, FL_SKIPPED);
1237                 return;
1238         }
1239
1240         log4(OP_COLLAPSE_RANGE, offset, length, FL_NONE);
1241
1242         if (testcalls <= simulatedopcount)
1243                 return;
1244
1245         if ((progressinterval && testcalls % progressinterval == 0) ||
1246             (debug && (monitorstart == -1 || monitorend == -1 ||
1247                       end_offset <= monitorend))) {
1248                 prt("%lu collapse\tfrom 0x%x to 0x%x, (0x%x bytes)\n", testcalls,
1249                         offset, offset+length, length);
1250         }
1251         if (fallocate(fd, mode, (loff_t)offset, (loff_t)length) == -1) {
1252                 prt("collapse range: 0x%x to 0x%x\n", offset, offset + length);
1253                 prterr("do_collapse_range: fallocate");
1254                 report_failure(161);
1255         }
1256
1257         memmove(good_buf + offset, good_buf + end_offset,
1258                 file_size - end_offset);
1259         file_size -= length;
1260 }
1261
1262 #else
1263 void
1264 do_collapse_range(unsigned offset, unsigned length)
1265 {
1266         return;
1267 }
1268 #endif
1269
1270 #ifdef FALLOC_FL_INSERT_RANGE
1271 void
1272 do_insert_range(unsigned offset, unsigned length)
1273 {
1274         unsigned end_offset;
1275         int mode = FALLOC_FL_INSERT_RANGE;
1276
1277         if (length == 0) {
1278                 if (!quiet && testcalls > simulatedopcount)
1279                         prt("skipping zero length insert range\n");
1280                 log4(OP_INSERT_RANGE, offset, length, FL_SKIPPED);
1281                 return;
1282         }
1283
1284         if ((loff_t)offset >= file_size) {
1285                 if (!quiet && testcalls > simulatedopcount)
1286                         prt("skipping insert range behind EOF\n");
1287                 log4(OP_INSERT_RANGE, offset, length, FL_SKIPPED);
1288                 return;
1289         }
1290
1291         log4(OP_INSERT_RANGE, offset, length, FL_NONE);
1292
1293         if (testcalls <= simulatedopcount)
1294                 return;
1295
1296         end_offset = offset + length;
1297         if ((progressinterval && testcalls % progressinterval == 0) ||
1298             (debug && (monitorstart == -1 || monitorend == -1 ||
1299                       end_offset <= monitorend))) {
1300                 prt("%lu insert\tfrom 0x%x to 0x%x, (0x%x bytes)\n", testcalls,
1301                         offset, offset+length, length);
1302         }
1303         if (fallocate(fd, mode, (loff_t)offset, (loff_t)length) == -1) {
1304                 prt("insert range: 0x%x to 0x%x\n", offset, offset + length);
1305                 prterr("do_insert_range: fallocate");
1306                 report_failure(161);
1307         }
1308
1309         memmove(good_buf + end_offset, good_buf + offset,
1310                 file_size - offset);
1311         memset(good_buf + offset, '\0', length);
1312         file_size += length;
1313 }
1314
1315 #else
1316 void
1317 do_insert_range(unsigned offset, unsigned length)
1318 {
1319         return;
1320 }
1321 #endif
1322
1323 #ifdef FICLONERANGE
1324 int
1325 test_clone_range(void)
1326 {
1327         struct file_clone_range fcr = {
1328                 .src_fd = fd,
1329         };
1330
1331         if (ioctl(fd, FICLONERANGE, &fcr) &&
1332             (errno = EOPNOTSUPP || errno == ENOTTY)) {
1333                 if (!quiet)
1334                         fprintf(stderr,
1335                                 "main: filesystem does not support "
1336                                 "clone range, disabling!\n");
1337                 return 0;
1338         }
1339
1340         return 1;
1341 }
1342
1343 void
1344 do_clone_range(unsigned offset, unsigned length, unsigned dest)
1345 {
1346         struct file_clone_range fcr = {
1347                 .src_fd = fd,
1348                 .src_offset = offset,
1349                 .src_length = length,
1350                 .dest_offset = dest,
1351         };
1352
1353         if (length == 0) {
1354                 if (!quiet && testcalls > simulatedopcount)
1355                         prt("skipping zero length clone range\n");
1356                 log5(OP_CLONE_RANGE, offset, length, dest, FL_SKIPPED);
1357                 return;
1358         }
1359
1360         if ((loff_t)offset >= file_size) {
1361                 if (!quiet && testcalls > simulatedopcount)
1362                         prt("skipping clone range behind EOF\n");
1363                 log5(OP_CLONE_RANGE, offset, length, dest, FL_SKIPPED);
1364                 return;
1365         }
1366
1367         if (dest + length > biggest) {
1368                 biggest = dest + length;
1369                 if (!quiet && testcalls > simulatedopcount)
1370                         prt("cloning to largest ever: 0x%x\n", dest + length);
1371         }
1372
1373         log5(OP_CLONE_RANGE, offset, length, dest, FL_NONE);
1374
1375         if (testcalls <= simulatedopcount)
1376                 return;
1377
1378         if ((progressinterval && testcalls % progressinterval == 0) ||
1379             (debug && (monitorstart == -1 || monitorend == -1 ||
1380                        dest <= monitorstart || dest + length <= monitorend))) {
1381                 prt("%lu clone\tfrom 0x%x to 0x%x, (0x%x bytes) at 0x%x\n",
1382                         testcalls, offset, offset+length, length, dest);
1383         }
1384
1385         if (ioctl(fd, FICLONERANGE, &fcr) == -1) {
1386                 prt("clone range: 0x%x to 0x%x at 0x%x\n", offset,
1387                                 offset + length, dest);
1388                 prterr("do_clone_range: FICLONERANGE");
1389                 report_failure(161);
1390         }
1391
1392         memcpy(good_buf + dest, good_buf + offset, length);
1393         if (dest > file_size)
1394                 memset(good_buf + file_size, '\0', dest - file_size);
1395         if (dest + length > file_size)
1396                 file_size = dest + length;
1397 }
1398
1399 #else
1400 int
1401 test_clone_range(void)
1402 {
1403         return 0;
1404 }
1405
1406 void
1407 do_clone_range(unsigned offset, unsigned length, unsigned dest)
1408 {
1409         return;
1410 }
1411 #endif
1412
1413 #ifdef HAVE_LINUX_FALLOC_H
1414 /* fallocate is basically a no-op unless extending, then a lot like a truncate */
1415 void
1416 do_preallocate(unsigned offset, unsigned length, int keep_size)
1417 {
1418         unsigned end_offset;
1419
1420         if (length == 0) {
1421                 if (!quiet && testcalls > simulatedopcount)
1422                         prt("skipping zero length fallocate\n");
1423                 log4(OP_FALLOCATE, offset, length, FL_SKIPPED |
1424                      (keep_size ? FL_KEEP_SIZE : FL_NONE));
1425                 return;
1426         }
1427
1428         end_offset = keep_size ? 0 : offset + length;
1429
1430         if (end_offset > biggest) {
1431                 biggest = end_offset;
1432                 if (!quiet && testcalls > simulatedopcount)
1433                         prt("fallocating to largest ever: 0x%x\n", end_offset);
1434         }
1435
1436         /*
1437          * last arg matches fallocate string array index in logdump:
1438          *      0: allocate past EOF
1439          *      1: extending prealloc
1440          *      2: interior prealloc
1441          */
1442         log4(OP_FALLOCATE, offset, length,
1443              keep_size ? FL_KEEP_SIZE : FL_NONE);
1444
1445         if (end_offset > file_size) {
1446                 memset(good_buf + file_size, '\0', end_offset - file_size);
1447                 file_size = end_offset;
1448         }
1449
1450         if (testcalls <= simulatedopcount)
1451                 return;
1452         
1453         if ((progressinterval && testcalls % progressinterval == 0) ||
1454             (debug && (monitorstart == -1 || monitorend == -1 ||
1455                       end_offset <= monitorend)))
1456                 prt("%lu falloc\tfrom 0x%x to 0x%x (0x%x bytes)\n", testcalls,
1457                                 offset, offset + length, length);
1458         if (fallocate(fd, keep_size ? FALLOC_FL_KEEP_SIZE : 0, (loff_t)offset, (loff_t)length) == -1) {
1459                 prt("fallocate: 0x%x to 0x%x\n", offset, offset + length);
1460                 prterr("do_preallocate: fallocate");
1461                 report_failure(161);
1462         }
1463 }
1464 #else
1465 void
1466 do_preallocate(unsigned offset, unsigned length, int keep_size)
1467 {
1468         return;
1469 }
1470 #endif
1471
1472 void
1473 writefileimage()
1474 {
1475         ssize_t iret;
1476
1477         if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
1478                 prterr("writefileimage: lseek");
1479                 report_failure(171);
1480         }
1481         iret = write(fd, good_buf, file_size);
1482         if ((off_t)iret != file_size) {
1483                 if (iret == -1)
1484                         prterr("writefileimage: write");
1485                 else
1486                         prt("short write: 0x%x bytes instead of 0x%llx\n",
1487                             iret, (unsigned long long)file_size);
1488                 report_failure(172);
1489         }
1490         if (lite ? 0 : ftruncate(fd, file_size) == -1) {
1491                 prt("ftruncate2: %llx\n", (unsigned long long)file_size);
1492                 prterr("writefileimage: ftruncate");
1493                 report_failure(173);
1494         }
1495 }
1496
1497
1498 void
1499 docloseopen(void)
1500
1501         if (testcalls <= simulatedopcount)
1502                 return;
1503
1504         if (debug)
1505                 prt("%lu close/open\n", testcalls);
1506         if (close(fd)) {
1507                 prterr("docloseopen: close");
1508                 report_failure(180);
1509         }
1510         fd = open(fname, O_RDWR|o_direct, 0);
1511         if (fd < 0) {
1512                 prterr("docloseopen: open");
1513                 report_failure(181);
1514         }
1515 }
1516
1517 void
1518 dofsync(void)
1519 {
1520         int ret;
1521
1522         if (testcalls <= simulatedopcount)
1523                 return;
1524         if (debug)
1525                 prt("%lu fsync\n", testcalls);
1526         log4(OP_FSYNC, 0, 0, 0);
1527         ret = fsync(fd);
1528         if (ret < 0) {
1529                 prterr("dofsync");
1530                 report_failure(210);
1531         }
1532         mark_log();
1533         dump_fsync_buffer();
1534         mark_nr++;
1535 }
1536
1537 #define TRIM_OFF(off, size)                     \
1538 do {                                            \
1539         if (size)                               \
1540                 (off) %= (size);                \
1541         else                                    \
1542                 (off) = 0;                      \
1543 } while (0)
1544
1545 #define TRIM_LEN(off, len, size)                \
1546 do {                                            \
1547         if ((off) + (len) > (size))             \
1548                 (len) = (size) - (off);         \
1549 } while (0)
1550
1551 #define TRIM_OFF_LEN(off, len, size)            \
1552 do {                                            \
1553         TRIM_OFF(off, size);                    \
1554         TRIM_LEN(off, len, size);               \
1555 } while (0)
1556
1557 void
1558 cleanup(int sig)
1559 {
1560         if (sig)
1561                 prt("signal %d\n", sig);
1562         prt("testcalls = %lu\n", testcalls);
1563         exit(sig);
1564 }
1565
1566 static int
1567 op_args_count(int operation)
1568 {
1569         switch (operation) {
1570         case OP_CLONE_RANGE:
1571                 return 4;
1572         default:
1573                 return 3;
1574         }
1575 }
1576
1577 static int
1578 read_op(struct log_entry *log_entry)
1579 {
1580         char line[256];
1581
1582         memset(log_entry, 0, sizeof(*log_entry));
1583         log_entry->operation = -1;
1584
1585         while (log_entry->operation == -1) {
1586                 char *str;
1587                 int i;
1588
1589                 do {
1590                         if (!fgets(line, sizeof(line), replayopsf)) {
1591                                 if (feof(replayopsf)) {
1592                                         replayopsf = NULL;
1593                                         return 0;
1594                                 }
1595                                 goto fail;
1596                         }
1597                         str = strtok(line, " \t\n");
1598                 } while (!str || str[0] == '#');
1599
1600                 if (strcmp(str, "skip") == 0) {
1601                         log_entry->flags |= FL_SKIPPED;
1602                         str = strtok(NULL, " \t\n");
1603                         if (!str)
1604                                 goto fail;
1605                 }
1606                 log_entry->operation = op_code(str);
1607                 if (log_entry->operation == -1)
1608                         goto fail;
1609                 log_entry->nr_args = op_args_count(log_entry->operation);
1610                 for (i = 0; i < log_entry->nr_args; i++) {
1611                         char *end;
1612
1613                         str = strtok(NULL, " \t\n");
1614                         if (!str)
1615                                 goto fail;
1616                         log_entry->args[i] = strtoul(str, &end, 0);
1617                         if (*end)
1618                                 goto fail;
1619                 }
1620                 while ((str = strtok(NULL, " \t\n"))) {
1621                         if (strcmp(str, "keep_size") == 0)
1622                                 log_entry->flags |= FL_KEEP_SIZE;
1623                         else if (strcmp(str, "close_open") == 0)
1624                                 log_entry->flags |= FL_CLOSE_OPEN;
1625                         else if (strcmp(str, "*") == 0)
1626                                 ;  /* overlap marker; ignore */
1627                         else
1628                                 goto fail;
1629                 }
1630         }
1631         return 1;
1632
1633 fail:
1634         fprintf(stderr, "%s: parse error\n", replayops);
1635         fclose(replayopsf);
1636         replayopsf = NULL;
1637         cleanup(100);  /* doesn't return */
1638         return 0;
1639 }
1640
1641
1642 int
1643 test(void)
1644 {
1645         unsigned long   offset, offset2;
1646         unsigned long   size;
1647         unsigned long   rv;
1648         unsigned long   op;
1649         int             keep_size = 0;
1650
1651         if (simulatedopcount > 0 && testcalls == simulatedopcount)
1652                 writefileimage();
1653
1654         testcalls++;
1655
1656         if (debugstart > 0 && testcalls >= debugstart)
1657                 debug = 1;
1658
1659         if (!quiet && testcalls < simulatedopcount && testcalls % 100000 == 0)
1660                 prt("%lu...\n", testcalls);
1661
1662         if (replayopsf) {
1663                 struct log_entry log_entry;
1664
1665                 while (read_op(&log_entry)) {
1666                         if (log_entry.flags & FL_SKIPPED) {
1667                                 log4(log_entry.operation,
1668                                      log_entry.args[0], log_entry.args[1],
1669                                      log_entry.flags);
1670                                 continue;
1671                         }
1672
1673                         op = log_entry.operation;
1674                         offset = log_entry.args[0];
1675                         size = log_entry.args[1];
1676                         offset2 = log_entry.args[2];
1677                         closeopen = !!(log_entry.flags & FL_CLOSE_OPEN);
1678                         keep_size = !!(log_entry.flags & FL_KEEP_SIZE);
1679                         goto have_op;
1680                 }
1681                 return 0;
1682         }
1683
1684         rv = random();
1685         if (closeprob)
1686                 closeopen = (rv >> 3) < (1 << 28) / closeprob;
1687
1688         offset = random();
1689         offset2 = 0;
1690         size = maxoplen;
1691         if (randomoplen)
1692                 size = random() % (maxoplen + 1);
1693
1694         /* calculate appropriate op to run */
1695         if (lite)
1696                 op = rv % OP_MAX_LITE;
1697         else if (!integrity)
1698                 op = rv % OP_MAX_FULL;
1699         else
1700                 op = rv % OP_MAX_INTEGRITY;
1701
1702         switch(op) {
1703         case OP_TRUNCATE:
1704                 if (!style)
1705                         size = random() % maxfilelen;
1706                 break;
1707         case OP_FALLOCATE:
1708                 if (fallocate_calls && size && keep_size_calls)
1709                         keep_size = random() % 2;
1710                 break;
1711         case OP_ZERO_RANGE:
1712                 if (zero_range_calls && size && keep_size_calls)
1713                         keep_size = random() % 2;
1714                 break;
1715         case OP_CLONE_RANGE:
1716                 TRIM_OFF_LEN(offset, size, file_size);
1717                 offset = offset & ~(block_size - 1);
1718                 size = size & ~(block_size - 1);
1719                 do {
1720                         offset2 = random();
1721                         TRIM_OFF(offset2, maxfilelen);
1722                         offset2 = offset2 & ~(block_size - 1);
1723                 } while (llabs(offset2 - offset) < size ||
1724                          offset2 + size > maxfilelen);
1725                 break;
1726         }
1727
1728 have_op:
1729
1730         switch (op) {
1731         case OP_MAPREAD:
1732                 if (!mapped_reads)
1733                         op = OP_READ;
1734                 break;
1735         case OP_MAPWRITE:
1736                 if (!mapped_writes)
1737                         op = OP_WRITE;
1738                 break;
1739         case OP_FALLOCATE:
1740                 if (!fallocate_calls) {
1741                         log4(OP_FALLOCATE, offset, size, FL_SKIPPED);
1742                         goto out;
1743                 }
1744                 break;
1745         case OP_PUNCH_HOLE:
1746                 if (!punch_hole_calls) {
1747                         log4(OP_PUNCH_HOLE, offset, size, FL_SKIPPED);
1748                         goto out;
1749                 }
1750                 break;
1751         case OP_ZERO_RANGE:
1752                 if (!zero_range_calls) {
1753                         log4(OP_ZERO_RANGE, offset, size, FL_SKIPPED);
1754                         goto out;
1755                 }
1756                 break;
1757         case OP_COLLAPSE_RANGE:
1758                 if (!collapse_range_calls) {
1759                         log4(OP_COLLAPSE_RANGE, offset, size, FL_SKIPPED);
1760                         goto out;
1761                 }
1762                 break;
1763         case OP_INSERT_RANGE:
1764                 if (!insert_range_calls) {
1765                         log4(OP_INSERT_RANGE, offset, size, FL_SKIPPED);
1766                         goto out;
1767                 }
1768                 break;
1769         case OP_CLONE_RANGE:
1770                 if (!clone_range_calls) {
1771                         log5(op, offset, size, offset2, FL_SKIPPED);
1772                         goto out;
1773                 }
1774                 break;
1775         }
1776
1777         switch (op) {
1778         case OP_READ:
1779                 TRIM_OFF_LEN(offset, size, file_size);
1780                 doread(offset, size);
1781                 break;
1782
1783         case OP_WRITE:
1784                 TRIM_OFF_LEN(offset, size, maxfilelen);
1785                 dowrite(offset, size);
1786                 break;
1787
1788         case OP_MAPREAD:
1789                 TRIM_OFF_LEN(offset, size, file_size);
1790                 domapread(offset, size);
1791                 break;
1792
1793         case OP_MAPWRITE:
1794                 TRIM_OFF_LEN(offset, size, maxfilelen);
1795                 domapwrite(offset, size);
1796                 break;
1797
1798         case OP_TRUNCATE:
1799                 dotruncate(size);
1800                 break;
1801
1802         case OP_FALLOCATE:
1803                 TRIM_OFF_LEN(offset, size, maxfilelen);
1804                 do_preallocate(offset, size, keep_size);
1805                 break;
1806
1807         case OP_PUNCH_HOLE:
1808                 TRIM_OFF_LEN(offset, size, file_size);
1809                 do_punch_hole(offset, size);
1810                 break;
1811         case OP_ZERO_RANGE:
1812                 TRIM_OFF_LEN(offset, size, file_size);
1813                 do_zero_range(offset, size, keep_size);
1814                 break;
1815         case OP_COLLAPSE_RANGE:
1816                 TRIM_OFF_LEN(offset, size, file_size - 1);
1817                 offset = offset & ~(block_size - 1);
1818                 size = size & ~(block_size - 1);
1819                 if (size == 0) {
1820                         log4(OP_COLLAPSE_RANGE, offset, size, FL_SKIPPED);
1821                         goto out;
1822                 }
1823                 do_collapse_range(offset, size);
1824                 break;
1825         case OP_INSERT_RANGE:
1826                 TRIM_OFF(offset, file_size);
1827                 TRIM_LEN(file_size, size, maxfilelen);
1828                 offset = offset & ~(block_size - 1);
1829                 size = size & ~(block_size - 1);
1830                 if (size == 0) {
1831                         log4(OP_INSERT_RANGE, offset, size, FL_SKIPPED);
1832                         goto out;
1833                 }
1834                 if (file_size + size > maxfilelen) {
1835                         log4(OP_INSERT_RANGE, offset, size, FL_SKIPPED);
1836                         goto out;
1837                 }
1838
1839                 do_insert_range(offset, size);
1840                 break;
1841         case OP_CLONE_RANGE:
1842                 if (size == 0) {
1843                         log5(OP_CLONE_RANGE, offset, size, offset2, FL_SKIPPED);
1844                         goto out;
1845                 }
1846                 if (offset2 + size > maxfilelen) {
1847                         log5(OP_CLONE_RANGE, offset, size, offset2, FL_SKIPPED);
1848                         goto out;
1849                 }
1850
1851                 do_clone_range(offset, size, offset2);
1852                 break;
1853         case OP_FSYNC:
1854                 dofsync();
1855                 break;
1856         default:
1857                 prterr("test: unknown operation");
1858                 report_failure(42);
1859                 break;
1860         }
1861
1862         if (check_file && testcalls > simulatedopcount)
1863                 check_contents();
1864
1865 out:
1866         if (sizechecks && testcalls > simulatedopcount)
1867                 check_size();
1868         if (closeopen)
1869                 docloseopen();
1870         return 1;
1871 }
1872
1873
1874 void
1875 usage(void)
1876 {
1877         fprintf(stdout, "usage: %s",
1878                 "fsx [-dknqxAFJLOWZ] [-b opnum] [-c Prob] [-g filldata] [-i logdev] [-j logid] [-l flen] [-m start:end] [-o oplen] [-p progressinterval] [-r readbdy] [-s style] [-t truncbdy] [-w writebdy] [-D startingop] [-N numops] [-P dirpath] [-S seed] fname\n\
1879         -b opnum: beginning operation number (default 1)\n\
1880         -c P: 1 in P chance of file close+open at each op (default infinity)\n\
1881         -d: debug output for all operations\n\
1882         -f flush and invalidate cache after I/O\n\
1883         -g X: write character X instead of random generated data\n\
1884         -i logdev: do integrity testing, logdev is the dm log writes device\n\
1885         -j logid: prefix debug log messsages with this id\n\
1886         -k: do not truncate existing file and use its size as upper bound on file size\n\
1887         -l flen: the upper bound on file size (default 262144)\n\
1888         -m startop:endop: monitor (print debug output) specified byte range (default 0:infinity)\n\
1889         -n: no verifications of file size\n\
1890         -o oplen: the upper bound on operation size (default 65536)\n\
1891         -p progressinterval: debug output at specified operation interval\n\
1892         -q: quieter operation\n\
1893         -r readbdy: 4096 would make reads page aligned (default 1)\n\
1894         -s style: 1 gives smaller truncates (default 0)\n\
1895         -t truncbdy: 4096 would make truncates page aligned (default 1)\n\
1896         -w writebdy: 4096 would make writes page aligned (default 1)\n\
1897         -x: preallocate file space before starting, XFS only (default 0)\n\
1898         -y synchronize changes to a file\n"
1899
1900 #ifdef AIO
1901 "       -A: Use the AIO system calls\n"
1902 #endif
1903 "       -D startingop: debug output starting at specified operation\n"
1904 #ifdef HAVE_LINUX_FALLOC_H
1905 "       -F: Do not use fallocate (preallocation) calls\n"
1906 #endif
1907 #ifdef FALLOC_FL_PUNCH_HOLE
1908 "       -H: Do not use punch hole calls\n"
1909 #endif
1910 #ifdef FALLOC_FL_ZERO_RANGE
1911 "       -z: Do not use zero range calls\n"
1912 #endif
1913 #ifdef FALLOC_FL_COLLAPSE_RANGE
1914 "       -C: Do not use collapse range calls\n"
1915 #endif
1916 #ifdef FALLOC_FL_INSERT_RANGE
1917 "       -I: Do not use insert range calls\n"
1918 #endif
1919 #ifdef FICLONERANGE
1920 "       -J: Do not use clone range calls\n"
1921 #endif
1922 "       -L: fsxLite - no file creations & no file size changes\n\
1923         -N numops: total # operations to do (default infinity)\n\
1924         -O: use oplen (see -o flag) for every op (default random)\n\
1925         -P: save .fsxlog .fsxops and .fsxgood files in dirpath (default ./)\n\
1926         -S seed: for random # generator (default 1) 0 gets timestamp\n\
1927         -W: mapped write operations DISabled\n\
1928         -X: Read file and compare to good buffer after every operation.\n\
1929         -R: read() system calls only (mapped reads disabled)\n\
1930         -Z: O_DIRECT (use -R, -W, -r and -w too)\n\
1931         --replay-ops opsfile: replay ops from recorded .fsxops file\n\
1932         --record-ops[=opsfile]: dump ops file also on success. optionally specify ops file name\n\
1933         fname: this filename is REQUIRED (no default)\n");
1934         exit(90);
1935 }
1936
1937
1938 int
1939 getnum(char *s, char **e)
1940 {
1941         int ret;
1942
1943         *e = (char *) 0;
1944         ret = strtol(s, e, 0);
1945         if (*e)
1946                 switch (**e) {
1947                 case 'b':
1948                 case 'B':
1949                         ret *= 512;
1950                         *e = *e + 1;
1951                         break;
1952                 case 'k':
1953                 case 'K':
1954                         ret *= 1024;
1955                         *e = *e + 1;
1956                         break;
1957                 case 'm':
1958                 case 'M':
1959                         ret *= 1024*1024;
1960                         *e = *e + 1;
1961                         break;
1962                 case 'w':
1963                 case 'W':
1964                         ret *= 4;
1965                         *e = *e + 1;
1966                         break;
1967                 }
1968         return (ret);
1969 }
1970
1971 #ifdef AIO
1972
1973 #define QSZ     1024
1974 io_context_t    io_ctx;
1975 struct iocb     iocb;
1976
1977 int aio_setup()
1978 {
1979         int ret;
1980         ret = io_queue_init(QSZ, &io_ctx);
1981         if (ret != 0) {
1982                 fprintf(stderr, "aio_setup: io_queue_init failed: %s\n",
1983                         strerror(ret));
1984                 return(-1);
1985         }
1986         return(0);
1987 }
1988
1989 int
1990 __aio_rw(int rw, int fd, char *buf, unsigned len, unsigned offset)
1991 {
1992         struct io_event event;
1993         static struct timespec ts;
1994         struct iocb *iocbs[] = { &iocb };
1995         int ret;
1996         long res;
1997
1998         if (rw == READ) {
1999                 io_prep_pread(&iocb, fd, buf, len, offset);
2000         } else {
2001                 io_prep_pwrite(&iocb, fd, buf, len, offset);
2002         }
2003
2004         ts.tv_sec = 30;
2005         ts.tv_nsec = 0;
2006         ret = io_submit(io_ctx, 1, iocbs);
2007         if (ret != 1) {
2008                 fprintf(stderr, "errcode=%d\n", ret);
2009                 fprintf(stderr, "aio_rw: io_submit failed: %s\n",
2010                                 strerror(ret));
2011                 goto out_error;
2012         }
2013
2014         ret = io_getevents(io_ctx, 1, 1, &event, &ts);
2015         if (ret != 1) {
2016                 if (ret == 0)
2017                         fprintf(stderr, "aio_rw: no events available\n");
2018                 else {
2019                         fprintf(stderr, "errcode=%d\n", -ret);
2020                         fprintf(stderr, "aio_rw: io_getevents failed: %s\n",
2021                                         strerror(-ret));
2022                 }
2023                 goto out_error;
2024         }
2025         if (len != event.res) {
2026                 /*
2027                  * The b0rked libaio defines event.res as unsigned.
2028                  * However the kernel strucuture has it signed,
2029                  * and it's used to pass negated error value.
2030                  * Till the library is fixed use the temp var.
2031                  */
2032                 res = (long)event.res;
2033                 if (res >= 0)
2034                         fprintf(stderr, "bad io length: %lu instead of %u\n",
2035                                         res, len);
2036                 else {
2037                         fprintf(stderr, "errcode=%ld\n", -res);
2038                         fprintf(stderr, "aio_rw: async io failed: %s\n",
2039                                         strerror(-res));
2040                         ret = res;
2041                         goto out_error;
2042                 }
2043
2044         }
2045         return event.res;
2046
2047 out_error:
2048         /*
2049          * The caller expects error return in traditional libc
2050          * convention, i.e. -1 and the errno set to error.
2051          */
2052         errno = -ret;
2053         return -1;
2054 }
2055
2056 int aio_rw(int rw, int fd, char *buf, unsigned len, unsigned offset)
2057 {
2058         int ret;
2059
2060         if (aio) {
2061                 ret = __aio_rw(rw, fd, buf, len, offset);
2062         } else {
2063                 if (rw == READ)
2064                         ret = read(fd, buf, len);
2065                 else
2066                         ret = write(fd, buf, len);
2067         }
2068         return ret;
2069 }
2070
2071 #endif
2072
2073 #define test_fallocate(mode) __test_fallocate(mode, #mode)
2074
2075 int
2076 __test_fallocate(int mode, const char *mode_str)
2077 {
2078 #ifdef HAVE_LINUX_FALLOC_H
2079         int ret = 0;
2080         if (!lite) {
2081                 if (fallocate(fd, mode, file_size, 1) && errno == EOPNOTSUPP) {
2082                         if(!quiet)
2083                                 fprintf(stderr,
2084                                         "main: filesystem does not support "
2085                                         "fallocate mode %s, disabling!\n",
2086                                         mode_str);
2087                 } else {
2088                         ret = 1;
2089                         if (ftruncate(fd, file_size)) {
2090                                 warn("main: ftruncate");
2091                                 exit(132);
2092                         }
2093                 }
2094         }
2095         return ret;
2096 #endif
2097 }
2098
2099 static struct option longopts[] = {
2100         {"replay-ops", required_argument, 0, 256},
2101         {"record-ops", optional_argument, 0, 255},
2102         { }
2103 };
2104
2105 int
2106 main(int argc, char **argv)
2107 {
2108         int     i, style, ch;
2109         char    *endp, *tmp;
2110         char logfile[PATH_MAX];
2111         struct stat statbuf;
2112         int o_flags = O_RDWR|O_CREAT|O_TRUNC;
2113
2114         logfile[0] = 0;
2115         dname[0] = 0;
2116
2117         page_size = getpagesize();
2118         page_mask = page_size - 1;
2119         mmap_mask = page_mask;
2120         
2121
2122         setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */
2123
2124         while ((ch = getopt_long(argc, argv,
2125                                  "b:c:dfg:i:j:kl:m:no:p:qr:s:t:w:xyAD:FJKHzCILN:OP:RS:WXZ",
2126                                  longopts, NULL)) != EOF)
2127                 switch (ch) {
2128                 case 'b':
2129                         simulatedopcount = getnum(optarg, &endp);
2130                         if (!quiet)
2131                                 prt("Will begin at operation %ld\n", simulatedopcount);
2132                         if (simulatedopcount == 0)
2133                                 usage();
2134                         simulatedopcount -= 1;
2135                         break;
2136                 case 'c':
2137                         closeprob = getnum(optarg, &endp);
2138                         if (!quiet)
2139                                 prt("Chance of close/open is 1 in %d\n", closeprob);
2140                         if (closeprob <= 0)
2141                                 usage();
2142                         break;
2143                 case 'd':
2144                         debug = 1;
2145                         break;
2146                 case 'f':
2147                         flush = 1;
2148                         break;
2149                 case 'g':
2150                         filldata = *optarg;
2151                         break;
2152                 case 'i':
2153                         integrity = 1;
2154                         logdev = strdup(optarg);
2155                         if (!logdev) {
2156                                 prterr("strdup");
2157                                 exit(101);
2158                         }
2159                         break;
2160                 case 'j':
2161                         logid = strdup(optarg);
2162                         if (!logid) {
2163                                 prterr("strdup");
2164                                 exit(101);
2165                         }
2166                         break;
2167                 case 'k':
2168                         o_flags &= ~O_TRUNC;
2169                         break;
2170                 case 'l':
2171                         maxfilelen = getnum(optarg, &endp);
2172                         if (maxfilelen <= 0)
2173                                 usage();
2174                         break;
2175                 case 'm':
2176                         monitorstart = getnum(optarg, &endp);
2177                         if (monitorstart < 0)
2178                                 usage();
2179                         if (!endp || *endp++ != ':')
2180                                 usage();
2181                         monitorend = getnum(endp, &endp);
2182                         if (monitorend < 0)
2183                                 usage();
2184                         if (monitorend == 0)
2185                                 monitorend = -1; /* aka infinity */
2186                         debug = 1;
2187                 case 'n':
2188                         sizechecks = 0;
2189                         break;
2190                 case 'o':
2191                         maxoplen = getnum(optarg, &endp);
2192                         if (maxoplen <= 0)
2193                                 usage();
2194                         break;
2195                 case 'p':
2196                         progressinterval = getnum(optarg, &endp);
2197                         if (progressinterval == 0)
2198                                 usage();
2199                         break;
2200                 case 'q':
2201                         quiet = 1;
2202                         break;
2203                 case 'r':
2204                         readbdy = getnum(optarg, &endp);
2205                         if (readbdy <= 0)
2206                                 usage();
2207                         break;
2208                 case 's':
2209                         style = getnum(optarg, &endp);
2210                         if (style < 0 || style > 1)
2211                                 usage();
2212                         break;
2213                 case 't':
2214                         truncbdy = getnum(optarg, &endp);
2215                         if (truncbdy <= 0)
2216                                 usage();
2217                         break;
2218                 case 'w':
2219                         writebdy = getnum(optarg, &endp);
2220                         if (writebdy <= 0)
2221                                 usage();
2222                         break;
2223                 case 'x':
2224                         prealloc = 1;
2225                         break;
2226                 case 'y':
2227                         do_fsync = 1;
2228                         break;
2229                 case 'A':
2230                         aio = 1;
2231                         break;
2232                 case 'D':
2233                         debugstart = getnum(optarg, &endp);
2234                         if (debugstart < 1)
2235                                 usage();
2236                         break;
2237                 case 'F':
2238                         fallocate_calls = 0;
2239                         break;
2240                 case 'K':
2241                         keep_size_calls = 0;
2242                         break;
2243                 case 'H':
2244                         punch_hole_calls = 0;
2245                         break;
2246                 case 'z':
2247                         zero_range_calls = 0;
2248                         break;
2249                 case 'C':
2250                         collapse_range_calls = 0;
2251                         break;
2252                 case 'I':
2253                         insert_range_calls = 0;
2254                         break;
2255                 case 'J':
2256                         clone_range_calls = 0;
2257                         break;
2258                 case 'L':
2259                         lite = 1;
2260                         o_flags &= ~(O_CREAT|O_TRUNC);
2261                         break;
2262                 case 'N':
2263                         numops = getnum(optarg, &endp);
2264                         if (numops < 0)
2265                                 usage();
2266                         break;
2267                 case 'O':
2268                         randomoplen = 0;
2269                         break;
2270                 case 'P':
2271                         strncpy(dname, optarg, sizeof(dname));
2272                         strcat(dname, "/");
2273                         dirpath = strlen(dname);
2274                         break;
2275                 case 'R':
2276                         mapped_reads = 0;
2277                         break;
2278                 case 'S':
2279                         seed = getnum(optarg, &endp);
2280                         if (seed == 0) {
2281                                 seed = time(0) % 10000;
2282                                 seed += (int)getpid();
2283                         }
2284                         if (seed < 0)
2285                                 usage();
2286                         break;
2287                 case 'W':
2288                         mapped_writes = 0;
2289                         if (!quiet)
2290                                 prt("mapped writes DISABLED\n");
2291                         break;
2292                 case 'X':
2293                         check_file = 1;
2294                         break;
2295                 case 'Z':
2296                         o_direct = O_DIRECT;
2297                         o_flags |= O_DIRECT;
2298                         break;
2299                 case 255:  /* --record-ops */
2300                         if (optarg)
2301                                 strncpy(opsfile, optarg, sizeof(opsfile));
2302                         recordops = opsfile;
2303                         break;
2304                 case 256:  /* --replay-ops */
2305                         replayops = optarg;
2306                         break;
2307                 default:
2308                         usage();
2309                         /* NOTREACHED */
2310                 }
2311         argc -= optind;
2312         argv += optind;
2313         if (argc != 1)
2314                 usage();
2315
2316         if (integrity && !dirpath) {
2317                 fprintf(stderr, "option -i <logdev> requires -P <dirpath>\n");
2318                 usage();
2319         }
2320
2321         fname = argv[0];
2322         tmp = strdup(fname);
2323         if (!tmp) {
2324                 prterr("strdup");
2325                 exit(101);
2326         }
2327         bname = basename(tmp);
2328
2329         signal(SIGHUP,  cleanup);
2330         signal(SIGINT,  cleanup);
2331         signal(SIGPIPE, cleanup);
2332         signal(SIGALRM, cleanup);
2333         signal(SIGTERM, cleanup);
2334         signal(SIGXCPU, cleanup);
2335         signal(SIGXFSZ, cleanup);
2336         signal(SIGVTALRM,       cleanup);
2337         signal(SIGUSR1, cleanup);
2338         signal(SIGUSR2, cleanup);
2339
2340         if (!quiet && seed)
2341                 prt("Seed set to %d\n", seed);
2342         srandom(seed);
2343         fd = open(fname, o_flags, 0666);
2344         if (fd < 0) {
2345                 prterr(fname);
2346                 exit(91);
2347         }
2348         if (fstat(fd, &statbuf)) {
2349                 prterr("check_size: fstat");
2350                 exit(91);
2351         }
2352         block_size = statbuf.st_blksize;
2353 #ifdef XFS
2354         if (prealloc) {
2355                 xfs_flock64_t   resv = { 0 };
2356 #ifdef HAVE_XFS_PLATFORM_DEFS_H
2357                 if (!platform_test_xfs_fd(fd)) {
2358                         prterr(fname);
2359                         fprintf(stderr, "main: cannot prealloc, non XFS\n");
2360                         exit(96);
2361                 }
2362 #endif
2363                 resv.l_len = maxfilelen;
2364                 if ((xfsctl(fname, fd, XFS_IOC_RESVSP, &resv)) < 0) {
2365                         prterr(fname);
2366                         exit(97);
2367                 }
2368         }
2369 #endif
2370
2371         if (dirpath) {
2372                 snprintf(goodfile, sizeof(goodfile), "%s%s.fsxgood", dname, bname);
2373                 snprintf(logfile, sizeof(logfile), "%s%s.fsxlog", dname, bname);
2374                 if (!*opsfile)
2375                         snprintf(opsfile, sizeof(opsfile), "%s%s.fsxops", dname, bname);
2376         } else {
2377                 snprintf(goodfile, sizeof(goodfile), "%s.fsxgood", fname);
2378                 snprintf(logfile, sizeof(logfile), "%s.fsxlog", fname);
2379                 if (!*opsfile)
2380                         snprintf(opsfile, sizeof(opsfile), "%s.fsxops", fname);
2381         }
2382         fsxgoodfd = open(goodfile, O_RDWR|O_CREAT|O_TRUNC, 0666);
2383         if (fsxgoodfd < 0) {
2384                 prterr(goodfile);
2385                 exit(92);
2386         }
2387         fsxlogf = fopen(logfile, "w");
2388         if (fsxlogf == NULL) {
2389                 prterr(logfile);
2390                 exit(93);
2391         }
2392         unlink(opsfile);
2393
2394         if (replayops) {
2395                 replayopsf = fopen(replayops, "r");
2396                 if (!replayopsf) {
2397                         prterr(replayops);
2398                         exit(93);
2399                 }
2400         }
2401
2402 #ifdef AIO
2403         if (aio) 
2404                 aio_setup();
2405 #endif
2406
2407         if (!(o_flags & O_TRUNC)) {
2408                 off_t ret;
2409                 file_size = maxfilelen = biggest = lseek(fd, (off_t)0, SEEK_END);
2410                 if (file_size == (off_t)-1) {
2411                         prterr(fname);
2412                         warn("main: lseek eof");
2413                         exit(94);
2414                 }
2415                 ret = lseek(fd, (off_t)0, SEEK_SET);
2416                 if (ret == (off_t)-1) {
2417                         prterr(fname);
2418                         warn("main: lseek 0");
2419                         exit(95);
2420                 }
2421         }
2422         original_buf = (char *) malloc(maxfilelen);
2423         for (i = 0; i < maxfilelen; i++)
2424                 original_buf[i] = random() % 256;
2425         good_buf = (char *) malloc(maxfilelen + writebdy);
2426         good_buf = round_ptr_up(good_buf, writebdy, 0);
2427         memset(good_buf, '\0', maxfilelen);
2428         temp_buf = (char *) malloc(maxoplen + readbdy);
2429         temp_buf = round_ptr_up(temp_buf, readbdy, 0);
2430         memset(temp_buf, '\0', maxoplen);
2431         if (lite) {     /* zero entire existing file */
2432                 ssize_t written;
2433
2434                 written = write(fd, good_buf, (size_t)maxfilelen);
2435                 if (written != maxfilelen) {
2436                         if (written == -1) {
2437                                 prterr(fname);
2438                                 warn("main: error on write");
2439                         } else
2440                                 warn("main: short write, 0x%x bytes instead "
2441                                         "of 0x%lx\n",
2442                                         (unsigned)written,
2443                                         maxfilelen);
2444                         exit(98);
2445                 }
2446         } else {
2447                 ssize_t ret, len = file_size;
2448                 off_t off = 0;
2449
2450                 while (len > 0) {
2451                         ret = read(fd, good_buf + off, len);
2452                         if (ret == -1) {
2453                                 prterr(fname);
2454                                 warn("main: error on read");
2455                                 exit(98);
2456                         }
2457                         len -= ret;
2458                         off += ret;
2459                 }
2460
2461                 check_trunc_hack();
2462         }
2463
2464         if (fallocate_calls)
2465                 fallocate_calls = test_fallocate(0);
2466         if (keep_size_calls)
2467                 keep_size_calls = test_fallocate(FALLOC_FL_KEEP_SIZE);
2468         if (punch_hole_calls)
2469                 punch_hole_calls = test_fallocate(FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE);
2470         if (zero_range_calls)
2471                 zero_range_calls = test_fallocate(FALLOC_FL_ZERO_RANGE);
2472         if (collapse_range_calls)
2473                 collapse_range_calls = test_fallocate(FALLOC_FL_COLLAPSE_RANGE);
2474         if (insert_range_calls)
2475                 insert_range_calls = test_fallocate(FALLOC_FL_INSERT_RANGE);
2476         if (clone_range_calls)
2477                 clone_range_calls = test_clone_range();
2478
2479         while (numops == -1 || numops--)
2480                 if (!test())
2481                         break;
2482
2483         free(tmp);
2484         if (close(fd)) {
2485                 prterr("close");
2486                 report_failure(99);
2487         }
2488         prt("All %lu operations completed A-OK!\n", testcalls);
2489         if (recordops)
2490                 logdump();
2491
2492         exit(0);
2493         return 0;
2494 }