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