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