limit OP_PUNCH_HOLE operations to the file size
[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 #ifdef HAVE_ERR_H
24 #include <err.h>
25 #endif
26 #include <signal.h>
27 #include <stdio.h>
28 #include <stddef.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <stdarg.h>
32 #include <errno.h>
33 #ifdef AIO
34 #include <libaio.h>
35 #endif
36 #ifdef FALLOCATE
37 #include <linux/falloc.h>
38 #endif
39
40 #ifndef MAP_FILE
41 # define MAP_FILE 0
42 #endif
43
44 #define NUMPRINTCOLUMNS 32      /* # columns of data to print on each line */
45
46 /*
47  *      A log entry is an operation and a bunch of arguments.
48  */
49
50 struct log_entry {
51         int     operation;
52         int     args[3];
53 };
54
55 #define LOGSIZE 1000
56
57 struct log_entry        oplog[LOGSIZE]; /* the log */
58 int                     logptr = 0;     /* current position in log */
59 int                     logcount = 0;   /* total ops */
60
61 /*
62  * The operation matrix is complex due to conditional execution of different
63  * features. Hence when we come to deciding what operation to run, we need to
64  * be careful in how we select the different operations. The active operations
65  * are mapped to numbers as follows:
66  *
67  *              lite    !lite
68  * READ:        0       0
69  * WRITE:       1       1
70  * MAPREAD:     2       2
71  * MAPWRITE:    3       3
72  * TRUNCATE:    -       4
73  * FALLOCATE:   -       5
74  * PUNCH HOLE:  -       6
75  *
76  * When mapped read/writes are disabled, they are simply converted to normal
77  * reads and writes. When fallocate/fpunch calls are disabled, they are
78  * converted to OP_SKIPPED. Hence OP_SKIPPED needs to have a number higher than
79  * the operation selction matrix, as does the OP_CLOSEOPEN which is an
80  * operation modifier rather than an operation in itself.
81  *
82  * Because of the "lite" version, we also need to have different "maximum
83  * operation" defines to allow the ops to be selected correctly based on the
84  * mode being run.
85  */
86
87 /* common operations */
88 #define OP_READ         0
89 #define OP_WRITE        1
90 #define OP_MAPREAD      2
91 #define OP_MAPWRITE     3
92 #define OP_MAX_LITE     4
93
94 /* !lite operations */
95 #define OP_TRUNCATE     4
96 #define OP_FALLOCATE    5
97 #define OP_PUNCH_HOLE   6
98 #define OP_MAX_FULL     7
99
100 /* operation modifiers */
101 #define OP_CLOSEOPEN    100
102 #define OP_SKIPPED      101
103
104 #undef PAGE_SIZE
105 #define PAGE_SIZE       getpagesize()
106 #undef PAGE_MASK
107 #define PAGE_MASK       (PAGE_SIZE - 1)
108
109 char    *original_buf;                  /* a pointer to the original data */
110 char    *good_buf;                      /* a pointer to the correct data */
111 char    *temp_buf;                      /* a pointer to the current data */
112 char    *fname;                         /* name of our test file */
113 int     fd;                             /* fd for our test file */
114
115 off_t           file_size = 0;
116 off_t           biggest = 0;
117 char            state[256];
118 unsigned long   testcalls = 0;          /* calls to function "test" */
119
120 unsigned long   simulatedopcount = 0;   /* -b flag */
121 int     closeprob = 0;                  /* -c flag */
122 int     debug = 0;                      /* -d flag */
123 unsigned long   debugstart = 0;         /* -D flag */
124 int     flush = 0;                      /* -f flag */
125 int     do_fsync = 0;                   /* -y flag */
126 unsigned long   maxfilelen = 256 * 1024;        /* -l flag */
127 int     sizechecks = 1;                 /* -n flag disables them */
128 int     maxoplen = 64 * 1024;           /* -o flag */
129 int     quiet = 0;                      /* -q flag */
130 unsigned long progressinterval = 0;     /* -p flag */
131 int     readbdy = 1;                    /* -r flag */
132 int     style = 0;                      /* -s flag */
133 int     prealloc = 0;                   /* -x flag */
134 int     truncbdy = 1;                   /* -t flag */
135 int     writebdy = 1;                   /* -w flag */
136 long    monitorstart = -1;              /* -m flag */
137 long    monitorend = -1;                /* -m flag */
138 int     lite = 0;                       /* -L flag */
139 long    numops = -1;                    /* -N flag */
140 int     randomoplen = 1;                /* -O flag disables it */
141 int     seed = 1;                       /* -S flag */
142 int     mapped_writes = 1;              /* -W flag disables */
143 int     fallocate_calls = 1;            /* -F flag disables */
144 int     punch_hole_calls = 1;           /* -H flag disables */
145 int     mapped_reads = 1;               /* -R flag disables it */
146 int     fsxgoodfd = 0;
147 int     o_direct;                       /* -Z */
148 int     aio = 0;
149
150 int page_size;
151 int page_mask;
152 int mmap_mask;
153 #ifdef AIO
154 int aio_rw(int rw, int fd, char *buf, unsigned len, unsigned offset);
155 #define READ 0
156 #define WRITE 1
157 #define fsxread(a,b,c,d)        aio_rw(READ, a,b,c,d)
158 #define fsxwrite(a,b,c,d)       aio_rw(WRITE, a,b,c,d)
159 #else
160 #define fsxread(a,b,c,d)        read(a,b,c)
161 #define fsxwrite(a,b,c,d)       write(a,b,c)
162 #endif
163
164 FILE *  fsxlogf = NULL;
165 int badoff = -1;
166 int closeopen = 0;
167
168 static void *round_ptr_up(void *ptr, unsigned long align, unsigned long offset)
169 {
170         unsigned long ret = (unsigned long)ptr;
171
172         ret = ((ret + align - 1) & ~(align - 1));
173         ret += offset;
174         return (void *)ret;
175 }
176
177 void
178 vwarnc(int code, const char *fmt, va_list ap) {
179   fprintf(stderr, "fsx: ");
180   if (fmt != NULL) {
181         vfprintf(stderr, fmt, ap);
182         fprintf(stderr, ": ");
183   }
184   fprintf(stderr, "%s\n", strerror(code));
185 }
186
187 void
188 warn(const char * fmt, ...)  {
189         va_list ap;
190         va_start(ap, fmt);
191         vwarnc(errno, fmt, ap);
192         va_end(ap);
193 }
194
195 #define BUF_SIZE 1024
196
197 void
198 prt(char *fmt, ...)
199 {
200         va_list args;
201         char buffer[BUF_SIZE];
202
203         va_start(args, fmt);
204         vsnprintf(buffer, BUF_SIZE, fmt, args);
205         va_end(args);
206         fprintf(stdout, buffer);
207         if (fsxlogf)
208                 fprintf(fsxlogf, buffer);
209 }
210
211 void
212 prterr(char *prefix)
213 {
214         prt("%s%s%s\n", prefix, prefix ? ": " : "", strerror(errno));
215 }
216
217
218 void
219 log4(int operation, int arg0, int arg1, int arg2)
220 {
221         struct log_entry *le;
222
223         le = &oplog[logptr];
224         le->operation = operation;
225         if (closeopen)
226                 le->operation = ~ le->operation;
227         le->args[0] = arg0;
228         le->args[1] = arg1;
229         le->args[2] = arg2;
230         logptr++;
231         logcount++;
232         if (logptr >= LOGSIZE)
233                 logptr = 0;
234 }
235
236
237 void
238 logdump(void)
239 {
240         int     i, count, down;
241         struct log_entry        *lp;
242         char *falloc_type[3] = {"PAST_EOF", "EXTENDING", "INTERIOR"};
243
244         prt("LOG DUMP (%d total operations):\n", logcount);
245         if (logcount < LOGSIZE) {
246                 i = 0;
247                 count = logcount;
248         } else {
249                 i = logptr;
250                 count = LOGSIZE;
251         }
252         for ( ; count > 0; count--) {
253                 int opnum;
254
255                 opnum = i+1 + (logcount/LOGSIZE)*LOGSIZE;
256                 prt("%d(%3d mod 256): ", opnum, opnum%256);
257                 lp = &oplog[i];
258                 if ((closeopen = lp->operation < 0))
259                         lp->operation = ~ lp->operation;
260                         
261                 switch (lp->operation) {
262                 case OP_MAPREAD:
263                         prt("MAPREAD  0x%x thru 0x%x\t(0x%x bytes)",
264                             lp->args[0], lp->args[0] + lp->args[1] - 1,
265                             lp->args[1]);
266                         if (badoff >= lp->args[0] && badoff <
267                                                      lp->args[0] + lp->args[1])
268                                 prt("\t***RRRR***");
269                         break;
270                 case OP_MAPWRITE:
271                         prt("MAPWRITE 0x%x thru 0x%x\t(0x%x bytes)",
272                             lp->args[0], lp->args[0] + lp->args[1] - 1,
273                             lp->args[1]);
274                         if (badoff >= lp->args[0] && badoff <
275                                                      lp->args[0] + lp->args[1])
276                                 prt("\t******WWWW");
277                         break;
278                 case OP_READ:
279                         prt("READ     0x%x thru 0x%x\t(0x%x bytes)",
280                             lp->args[0], lp->args[0] + lp->args[1] - 1,
281                             lp->args[1]);
282                         if (badoff >= lp->args[0] &&
283                             badoff < lp->args[0] + lp->args[1])
284                                 prt("\t***RRRR***");
285                         break;
286                 case OP_WRITE:
287                         prt("WRITE    0x%x thru 0x%x\t(0x%x bytes)",
288                             lp->args[0], lp->args[0] + lp->args[1] - 1,
289                             lp->args[1]);
290                         if (lp->args[0] > lp->args[2])
291                                 prt(" HOLE");
292                         else if (lp->args[0] + lp->args[1] > lp->args[2])
293                                 prt(" EXTEND");
294                         if ((badoff >= lp->args[0] || badoff >=lp->args[2]) &&
295                             badoff < lp->args[0] + lp->args[1])
296                                 prt("\t***WWWW");
297                         break;
298                 case OP_TRUNCATE:
299                         down = lp->args[0] < lp->args[1];
300                         prt("TRUNCATE %s\tfrom 0x%x to 0x%x",
301                             down ? "DOWN" : "UP", lp->args[1], lp->args[0]);
302                         if (badoff >= lp->args[!down] &&
303                             badoff < lp->args[!!down])
304                                 prt("\t******WWWW");
305                         break;
306                 case OP_FALLOCATE:
307                         /* 0: offset 1: length 2: where alloced */
308                         prt("FALLOC   0x%x thru 0x%x\t(0x%x bytes) %s",
309                                 lp->args[0], lp->args[0] + lp->args[1],
310                                 lp->args[1], falloc_type[lp->args[2]]);
311                         if (badoff >= lp->args[0] &&
312                             badoff < lp->args[0] + lp->args[1])
313                                 prt("\t******FFFF");
314                         break;
315                 case OP_PUNCH_HOLE:
316                         prt("PUNCH    0x%x thru 0x%x\t(0x%x bytes)",
317                             lp->args[0], lp->args[0] + lp->args[1] - 1,
318                             lp->args[1]);
319                         if (badoff >= lp->args[0] && badoff <
320                                                      lp->args[0] + lp->args[1])
321                                 prt("\t******PPPP");
322                         break;
323                 case OP_SKIPPED:
324                         prt("SKIPPED (no operation)");
325                         break;
326                 default:
327                         prt("BOGUS LOG ENTRY (operation code = %d)!",
328                             lp->operation);
329                 }
330                 if (closeopen)
331                         prt("\n\t\tCLOSE/OPEN");
332                 prt("\n");
333                 i++;
334                 if (i == LOGSIZE)
335                         i = 0;
336         }
337 }
338
339
340 void
341 save_buffer(char *buffer, off_t bufferlength, int fd)
342 {
343         off_t ret;
344         ssize_t byteswritten;
345
346         if (fd <= 0 || bufferlength == 0)
347                 return;
348
349         if (bufferlength > SSIZE_MAX) {
350                 prt("fsx flaw: overflow in save_buffer\n");
351                 exit(67);
352         }
353         if (lite) {
354                 off_t size_by_seek = lseek(fd, (off_t)0, SEEK_END);
355                 if (size_by_seek == (off_t)-1)
356                         prterr("save_buffer: lseek eof");
357                 else if (bufferlength > size_by_seek) {
358                         warn("save_buffer: .fsxgood file too short... will save 0x%llx bytes instead of 0x%llx\n", (unsigned long long)size_by_seek,
359                              (unsigned long long)bufferlength);
360                         bufferlength = size_by_seek;
361                 }
362         }
363
364         ret = lseek(fd, (off_t)0, SEEK_SET);
365         if (ret == (off_t)-1)
366                 prterr("save_buffer: lseek 0");
367         
368         byteswritten = write(fd, buffer, (size_t)bufferlength);
369         if (byteswritten != bufferlength) {
370                 if (byteswritten == -1)
371                         prterr("save_buffer write");
372                 else
373                         warn("save_buffer: short write, 0x%x bytes instead of 0x%llx\n",
374                              (unsigned)byteswritten,
375                              (unsigned long long)bufferlength);
376         }
377 }
378
379
380 void
381 report_failure(int status)
382 {
383         logdump();
384         
385         if (fsxgoodfd) {
386                 if (good_buf) {
387                         save_buffer(good_buf, file_size, fsxgoodfd);
388                         prt("Correct content saved for comparison\n");
389                         prt("(maybe hexdump \"%s\" vs \"%s.fsxgood\")\n",
390                             fname, fname);
391                 }
392                 close(fsxgoodfd);
393         }
394         exit(status);
395 }
396
397
398 #define short_at(cp) ((unsigned short)((*((unsigned char *)(cp)) << 8) | \
399                                         *(((unsigned char *)(cp)) + 1)))
400
401 void
402 check_buffers(unsigned offset, unsigned size)
403 {
404         unsigned char c, t;
405         unsigned i = 0;
406         unsigned n = 0;
407         unsigned op = 0;
408         unsigned bad = 0;
409
410         if (memcmp(good_buf + offset, temp_buf, size) != 0) {
411                 prt("READ BAD DATA: offset = 0x%x, size = 0x%x, fname = %s\n",
412                     offset, size, fname);
413                 prt("OFFSET\tGOOD\tBAD\tRANGE\n");
414                 while (size > 0) {
415                         c = good_buf[offset];
416                         t = temp_buf[i];
417                         if (c != t) {
418                                 if (n < 16) {
419                                         bad = short_at(&temp_buf[i]);
420                                         prt("0x%5x\t0x%04x\t0x%04x", offset,
421                                             short_at(&good_buf[offset]), bad);
422                                         op = temp_buf[offset & 1 ? i+1 : i];
423                                         prt("\t0x%5x\n", n);
424                                         if (op)
425                                                 prt("operation# (mod 256) for "
426                                                   "the bad data may be %u\n",
427                                                 ((unsigned)op & 0xff));
428                                         else
429                                                 prt("operation# (mod 256) for "
430                                                   "the bad data unknown, check"
431                                                   " HOLE and EXTEND ops\n");
432                                 }
433                                 n++;
434                                 badoff = offset;
435                         }
436                         offset++;
437                         i++;
438                         size--;
439                 }
440                 report_failure(110);
441         }
442 }
443
444
445 void
446 check_size(void)
447 {
448         struct stat     statbuf;
449         off_t   size_by_seek;
450
451         if (fstat(fd, &statbuf)) {
452                 prterr("check_size: fstat");
453                 statbuf.st_size = -1;
454         }
455         size_by_seek = lseek(fd, (off_t)0, SEEK_END);
456         if (file_size != statbuf.st_size || file_size != size_by_seek) {
457                 prt("Size error: expected 0x%llx stat 0x%llx seek 0x%llx\n",
458                     (unsigned long long)file_size,
459                     (unsigned long long)statbuf.st_size,
460                     (unsigned long long)size_by_seek);
461                 report_failure(120);
462         }
463 }
464
465
466 void
467 check_trunc_hack(void)
468 {
469         struct stat statbuf;
470
471         ftruncate(fd, (off_t)0);
472         ftruncate(fd, (off_t)100000);
473         fstat(fd, &statbuf);
474         if (statbuf.st_size != (off_t)100000) {
475                 prt("no extend on truncate! not posix!\n");
476                 exit(130);
477         }
478         ftruncate(fd, 0);
479 }
480
481 void
482 doflush(unsigned offset, unsigned size)
483 {
484         unsigned pg_offset;
485         unsigned map_size;
486         char    *p;
487
488         if (o_direct == O_DIRECT)
489                 return;
490
491         pg_offset = offset & mmap_mask;
492         map_size  = pg_offset + size;
493
494         if ((p = (char *)mmap(0, map_size, PROT_READ | PROT_WRITE,
495                               MAP_FILE | MAP_SHARED, fd,
496                               (off_t)(offset - pg_offset))) == (char *)-1) {
497                 prterr("doflush: mmap");
498                 report_failure(202);
499         }
500         if (msync(p, map_size, MS_INVALIDATE) != 0) {
501                 prterr("doflush: msync");
502                 report_failure(203);
503         }
504         if (munmap(p, map_size) != 0) {
505                 prterr("doflush: munmap");
506                 report_failure(204);
507         }
508 }
509
510 void
511 doread(unsigned offset, unsigned size)
512 {
513         off_t ret;
514         unsigned iret;
515
516         offset -= offset % readbdy;
517         if (o_direct)
518                 size -= size % readbdy;
519         if (size == 0) {
520                 if (!quiet && testcalls > simulatedopcount && !o_direct)
521                         prt("skipping zero size read\n");
522                 log4(OP_SKIPPED, OP_READ, offset, size);
523                 return;
524         }
525         if (size + offset > file_size) {
526                 if (!quiet && testcalls > simulatedopcount)
527                         prt("skipping seek/read past end of file\n");
528                 log4(OP_SKIPPED, OP_READ, offset, size);
529                 return;
530         }
531
532         log4(OP_READ, offset, size, 0);
533
534         if (testcalls <= simulatedopcount)
535                 return;
536
537         if (!quiet &&
538                 ((progressinterval && testcalls % progressinterval == 0)  ||
539                 (debug &&
540                        (monitorstart == -1 ||
541                         (offset + size > monitorstart &&
542                         (monitorend == -1 || offset <= monitorend))))))
543                 prt("%lu read\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
544                     offset, offset + size - 1, size);
545         ret = lseek(fd, (off_t)offset, SEEK_SET);
546         if (ret == (off_t)-1) {
547                 prterr("doread: lseek");
548                 report_failure(140);
549         }
550         iret = fsxread(fd, temp_buf, size, offset);
551         if (iret != size) {
552                 if (iret == -1)
553                         prterr("doread: read");
554                 else
555                         prt("short read: 0x%x bytes instead of 0x%x\n",
556                             iret, size);
557                 report_failure(141);
558         }
559         check_buffers(offset, size);
560 }
561
562
563 void
564 check_eofpage(char *s, unsigned offset, char *p, int size)
565 {
566         unsigned long last_page, should_be_zero;
567
568         if (offset + size <= (file_size & ~page_mask))
569                 return;
570         /*
571          * we landed in the last page of the file
572          * test to make sure the VM system provided 0's 
573          * beyond the true end of the file mapping
574          * (as required by mmap def in 1996 posix 1003.1)
575          */
576         last_page = ((unsigned long)p + (offset & page_mask) + size) & ~page_mask;
577
578         for (should_be_zero = last_page + (file_size & page_mask);
579              should_be_zero < last_page + page_size;
580              should_be_zero++)
581                 if (*(char *)should_be_zero) {
582                         prt("Mapped %s: non-zero data past EOF (0x%llx) page offset 0x%x is 0x%04x\n",
583                             s, file_size - 1, should_be_zero & page_mask,
584                             short_at(should_be_zero));
585                         report_failure(205);
586                 }
587 }
588
589
590 void
591 domapread(unsigned offset, unsigned size)
592 {
593         unsigned pg_offset;
594         unsigned map_size;
595         char    *p;
596
597         offset -= offset % readbdy;
598         if (size == 0) {
599                 if (!quiet && testcalls > simulatedopcount)
600                         prt("skipping zero size read\n");
601                 log4(OP_SKIPPED, OP_MAPREAD, offset, size);
602                 return;
603         }
604         if (size + offset > file_size) {
605                 if (!quiet && testcalls > simulatedopcount)
606                         prt("skipping seek/read past end of file\n");
607                 log4(OP_SKIPPED, OP_MAPREAD, offset, size);
608                 return;
609         }
610
611         log4(OP_MAPREAD, offset, size, 0);
612
613         if (testcalls <= simulatedopcount)
614                 return;
615
616         if (!quiet &&
617                 ((progressinterval && testcalls % progressinterval == 0) ||
618                        (debug &&
619                        (monitorstart == -1 ||
620                         (offset + size > monitorstart &&
621                         (monitorend == -1 || offset <= monitorend))))))
622                 prt("%lu mapread\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
623                     offset, offset + size - 1, size);
624
625         pg_offset = offset & PAGE_MASK;
626         map_size  = pg_offset + size;
627
628         if ((p = (char *)mmap(0, map_size, PROT_READ, MAP_SHARED, fd,
629                               (off_t)(offset - pg_offset))) == (char *)-1) {
630                 prterr("domapread: mmap");
631                 report_failure(190);
632         }
633         memcpy(temp_buf, p + pg_offset, size);
634
635         check_eofpage("Read", offset, p, size);
636
637         if (munmap(p, map_size) != 0) {
638                 prterr("domapread: munmap");
639                 report_failure(191);
640         }
641
642         check_buffers(offset, size);
643 }
644
645
646 void
647 gendata(char *original_buf, char *good_buf, unsigned offset, unsigned size)
648 {
649         while (size--) {
650                 good_buf[offset] = testcalls % 256; 
651                 if (offset % 2)
652                         good_buf[offset] += original_buf[offset];
653                 offset++;
654         }
655 }
656
657
658 void
659 dowrite(unsigned offset, unsigned size)
660 {
661         off_t ret;
662         unsigned iret;
663
664         offset -= offset % writebdy;
665         if (o_direct)
666                 size -= size % writebdy;
667         if (size == 0) {
668                 if (!quiet && testcalls > simulatedopcount && !o_direct)
669                         prt("skipping zero size write\n");
670                 log4(OP_SKIPPED, OP_WRITE, offset, size);
671                 return;
672         }
673
674         log4(OP_WRITE, offset, size, file_size);
675
676         gendata(original_buf, good_buf, offset, size);
677         if (file_size < offset + size) {
678                 if (file_size < offset)
679                         memset(good_buf + file_size, '\0', offset - file_size);
680                 file_size = offset + size;
681                 if (lite) {
682                         warn("Lite file size bug in fsx!");
683                         report_failure(149);
684                 }
685         }
686
687         if (testcalls <= simulatedopcount)
688                 return;
689
690         if (!quiet &&
691                 ((progressinterval && testcalls % progressinterval == 0) ||
692                        (debug &&
693                        (monitorstart == -1 ||
694                         (offset + size > monitorstart &&
695                         (monitorend == -1 || offset <= monitorend))))))
696                 prt("%lu write\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
697                     offset, offset + size - 1, size);
698         ret = lseek(fd, (off_t)offset, SEEK_SET);
699         if (ret == (off_t)-1) {
700                 prterr("dowrite: lseek");
701                 report_failure(150);
702         }
703         iret = fsxwrite(fd, good_buf + offset, size, offset);
704         if (iret != size) {
705                 if (iret == -1)
706                         prterr("dowrite: write");
707                 else
708                         prt("short write: 0x%x bytes instead of 0x%x\n",
709                             iret, size);
710                 report_failure(151);
711         }
712         if (do_fsync) {
713                 if (fsync(fd)) {
714                         prt("fsync() failed: %s\n", strerror(errno));
715                         report_failure(152);
716                 }
717         }
718         if (flush) {
719                 doflush(offset, size);
720         }
721 }
722
723
724 void
725 domapwrite(unsigned offset, unsigned size)
726 {
727         unsigned pg_offset;
728         unsigned map_size;
729         off_t    cur_filesize;
730         char    *p;
731
732         offset -= offset % writebdy;
733         if (size == 0) {
734                 if (!quiet && testcalls > simulatedopcount)
735                         prt("skipping zero size write\n");
736                 log4(OP_SKIPPED, OP_MAPWRITE, offset, size);
737                 return;
738         }
739         cur_filesize = file_size;
740
741         log4(OP_MAPWRITE, offset, size, 0);
742
743         gendata(original_buf, good_buf, offset, size);
744         if (file_size < offset + size) {
745                 if (file_size < offset)
746                         memset(good_buf + file_size, '\0', offset - file_size);
747                 file_size = offset + size;
748                 if (lite) {
749                         warn("Lite file size bug in fsx!");
750                         report_failure(200);
751                 }
752         }
753
754         if (testcalls <= simulatedopcount)
755                 return;
756
757         if (!quiet &&
758                 ((progressinterval && testcalls % progressinterval == 0) ||
759                        (debug &&
760                        (monitorstart == -1 ||
761                         (offset + size > monitorstart &&
762                         (monitorend == -1 || offset <= monitorend))))))
763                 prt("%lu mapwrite\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
764                     offset, offset + size - 1, size);
765
766         if (file_size > cur_filesize) {
767                 if (ftruncate(fd, file_size) == -1) {
768                         prterr("domapwrite: ftruncate");
769                         exit(201);
770                 }
771         }
772         pg_offset = offset & PAGE_MASK;
773         map_size  = pg_offset + size;
774
775         if ((p = (char *)mmap(0, map_size, PROT_READ | PROT_WRITE,
776                               MAP_FILE | MAP_SHARED, fd,
777                               (off_t)(offset - pg_offset))) == (char *)-1) {
778                 prterr("domapwrite: mmap");
779                 report_failure(202);
780         }
781         memcpy(p + pg_offset, good_buf + offset, size);
782         if (msync(p, map_size, 0) != 0) {
783                 prterr("domapwrite: msync");
784                 report_failure(203);
785         }
786
787         check_eofpage("Write", offset, p, size);
788
789         if (munmap(p, map_size) != 0) {
790                 prterr("domapwrite: munmap");
791                 report_failure(204);
792         }
793 }
794
795
796 void
797 dotruncate(unsigned size)
798 {
799         int oldsize = file_size;
800
801         size -= size % truncbdy;
802         if (size > biggest) {
803                 biggest = size;
804                 if (!quiet && testcalls > simulatedopcount)
805                         prt("truncating to largest ever: 0x%x\n", size);
806         }
807
808         log4(OP_TRUNCATE, size, (unsigned)file_size, 0);
809
810         if (size > file_size)
811                 memset(good_buf + file_size, '\0', size - file_size);
812         file_size = size;
813
814         if (testcalls <= simulatedopcount)
815                 return;
816         
817         if ((progressinterval && testcalls % progressinterval == 0) ||
818             (debug && (monitorstart == -1 || monitorend == -1 ||
819                       size <= monitorend)))
820                 prt("%lu trunc\tfrom 0x%x to 0x%x\n", testcalls, oldsize, size);
821         if (ftruncate(fd, (off_t)size) == -1) {
822                 prt("ftruncate1: %x\n", size);
823                 prterr("dotruncate: ftruncate");
824                 report_failure(160);
825         }
826 }
827
828 #ifdef FALLOC_FL_PUNCH_HOLE
829 void
830 do_punch_hole(unsigned offset, unsigned length)
831 {
832         unsigned end_offset;
833         int max_offset = 0;
834         int max_len = 0;
835         int mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE;
836
837         if (length == 0) {
838                 if (!quiet && testcalls > simulatedopcount)
839                         prt("skipping zero length punch hole\n");
840                         log4(OP_SKIPPED, OP_PUNCH_HOLE, offset, length);
841                 return;
842         }
843
844         if (file_size <= (loff_t)offset) {
845                 if (!quiet && testcalls > simulatedopcount)
846                         prt("skipping hole punch off the end of the file\n");
847                         log4(OP_SKIPPED, OP_PUNCH_HOLE, offset, length);
848                 return;
849         }
850
851         end_offset = offset + length;
852
853         log4(OP_PUNCH_HOLE, offset, length, 0);
854
855         if (testcalls <= simulatedopcount)
856                 return;
857
858         if ((progressinterval && testcalls % progressinterval == 0) ||
859             (debug && (monitorstart == -1 || monitorend == -1 ||
860                       end_offset <= monitorend))) {
861                 prt("%lu punch\tfrom 0x%x to 0x%x, (0x%x bytes)\n", testcalls,
862                         offset, offset+length, length);
863         }
864         if (fallocate(fd, mode, (loff_t)offset, (loff_t)length) == -1) {
865                 prt("%punch hole: %x to %x\n", offset, length);
866                 prterr("do_punch_hole: fallocate");
867                 report_failure(161);
868         }
869
870
871         max_offset = offset < file_size ? offset : file_size;
872         max_len = max_offset + length <= file_size ? length :
873                         file_size - max_offset;
874         memset(good_buf + max_offset, '\0', max_len);
875 }
876
877 #else
878 void
879 do_punch_hole(unsigned offset, unsigned length)
880 {
881         return;
882 }
883 #endif
884
885 #ifdef FALLOCATE
886 /* fallocate is basically a no-op unless extending, then a lot like a truncate */
887 void
888 do_preallocate(unsigned offset, unsigned length)
889 {
890         unsigned end_offset;
891         int keep_size;
892
893         if (length == 0) {
894                 if (!quiet && testcalls > simulatedopcount)
895                         prt("skipping zero length fallocate\n");
896                 log4(OP_SKIPPED, OP_FALLOCATE, offset, length);
897                 return;
898         }
899
900         keep_size = random() % 2;
901
902         end_offset = keep_size ? 0 : offset + length;
903
904         if (end_offset > biggest) {
905                 biggest = end_offset;
906                 if (!quiet && testcalls > simulatedopcount)
907                         prt("fallocating to largest ever: 0x%x\n", end_offset);
908         }
909
910         /*
911          * last arg matches fallocate string array index in logdump:
912          *      0: allocate past EOF
913          *      1: extending prealloc
914          *      2: interior prealloc
915          */
916         log4(OP_FALLOCATE, offset, length, (end_offset > file_size) ? (keep_size ? 0 : 1) : 2);
917
918         if (end_offset > file_size) {
919                 memset(good_buf + file_size, '\0', end_offset - file_size);
920                 file_size = end_offset;
921         }
922
923         if (testcalls <= simulatedopcount)
924                 return;
925         
926         if ((progressinterval && testcalls % progressinterval == 0) ||
927             (debug && (monitorstart == -1 || monitorend == -1 ||
928                       end_offset <= monitorend)))
929                 prt("%lu falloc\tfrom 0x%x to 0x%x (0x%x bytes)\n", testcalls,
930                                 offset, offset + length, length);
931         if (fallocate(fd, keep_size ? FALLOC_FL_KEEP_SIZE : 0, (loff_t)offset, (loff_t)length) == -1) {
932                 prt("fallocate: %x to %x\n", offset, length);
933                 prterr("do_preallocate: fallocate");
934                 report_failure(161);
935         }
936 }
937 #else
938 void
939 do_preallocate(unsigned offset, unsigned length)
940 {
941         return;
942 }
943 #endif
944
945 void
946 writefileimage()
947 {
948         ssize_t iret;
949
950         if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
951                 prterr("writefileimage: lseek");
952                 report_failure(171);
953         }
954         iret = write(fd, good_buf, file_size);
955         if ((off_t)iret != file_size) {
956                 if (iret == -1)
957                         prterr("writefileimage: write");
958                 else
959                         prt("short write: 0x%x bytes instead of 0x%llx\n",
960                             iret, (unsigned long long)file_size);
961                 report_failure(172);
962         }
963         if (lite ? 0 : ftruncate(fd, file_size) == -1) {
964                 prt("ftruncate2: %llx\n", (unsigned long long)file_size);
965                 prterr("writefileimage: ftruncate");
966                 report_failure(173);
967         }
968 }
969
970
971 void
972 docloseopen(void)
973
974         if (testcalls <= simulatedopcount)
975                 return;
976
977         if (debug)
978                 prt("%lu close/open\n", testcalls);
979         if (close(fd)) {
980                 prterr("docloseopen: close");
981                 report_failure(180);
982         }
983         fd = open(fname, O_RDWR|o_direct, 0);
984         if (fd < 0) {
985                 prterr("docloseopen: open");
986                 report_failure(181);
987         }
988 }
989
990 #define TRIM_OFF_LEN(off, len, size)    \
991 do {                                    \
992         if (size)                       \
993                 (off) %= (size);        \
994         else                            \
995                 (off) = 0;              \
996         if ((off) + (len) > (size))     \
997                 (len) = (size) - (off); \
998 } while (0)
999
1000 void
1001 test(void)
1002 {
1003         unsigned long   offset;
1004         unsigned long   size = maxoplen;
1005         unsigned long   rv = random();
1006         unsigned long   op;
1007
1008         if (simulatedopcount > 0 && testcalls == simulatedopcount)
1009                 writefileimage();
1010
1011         testcalls++;
1012
1013         if (closeprob)
1014                 closeopen = (rv >> 3) < (1 << 28) / closeprob;
1015
1016         if (debugstart > 0 && testcalls >= debugstart)
1017                 debug = 1;
1018
1019         if (!quiet && testcalls < simulatedopcount && testcalls % 100000 == 0)
1020                 prt("%lu...\n", testcalls);
1021
1022         offset = random();
1023         if (randomoplen)
1024                 size = random() % (maxoplen + 1);
1025
1026         /* calculate appropriate op to run */
1027         if (lite)
1028                 op = rv % OP_MAX_LITE;
1029         else
1030                 op = rv % OP_MAX_FULL;
1031
1032         switch (op) {
1033         case OP_MAPREAD:
1034                 if (!mapped_reads)
1035                         op = OP_READ;
1036                 break;
1037         case OP_MAPWRITE:
1038                 if (!mapped_writes)
1039                         op = OP_WRITE;
1040                 break;
1041         case OP_FALLOCATE:
1042                 if (!fallocate_calls) {
1043                         log4(OP_SKIPPED, OP_FALLOCATE, offset, size);
1044                         goto out;
1045                 }
1046                 break;
1047         case OP_PUNCH_HOLE:
1048                 if (!punch_hole_calls) {
1049                         log4(OP_SKIPPED, OP_PUNCH_HOLE, offset, size);
1050                         goto out;
1051                 }
1052                 break;
1053         }
1054
1055         switch (op) {
1056         case OP_READ:
1057                 TRIM_OFF_LEN(offset, size, file_size);
1058                 doread(offset, size);
1059                 break;
1060
1061         case OP_WRITE:
1062                 TRIM_OFF_LEN(offset, size, maxfilelen);
1063                 dowrite(offset, size);
1064                 break;
1065
1066         case OP_MAPREAD:
1067                 TRIM_OFF_LEN(offset, size, file_size);
1068                 domapread(offset, size);
1069                 break;
1070
1071         case OP_MAPWRITE:
1072                 TRIM_OFF_LEN(offset, size, maxfilelen);
1073                 domapwrite(offset, size);
1074                 break;
1075
1076         case OP_TRUNCATE:
1077                 if (!style)
1078                         size = random() % maxfilelen;
1079                 dotruncate(size);
1080                 break;
1081
1082         case OP_FALLOCATE:
1083                 TRIM_OFF_LEN(offset, size, maxfilelen);
1084                 do_preallocate(offset, size);
1085                 break;
1086
1087         case OP_PUNCH_HOLE:
1088                 TRIM_OFF_LEN(offset, size, file_size);
1089                 do_punch_hole(offset, size);
1090                 break;
1091         default:
1092                 prterr("test: unknown operation");
1093                 report_failure(42);
1094                 break;
1095         }
1096
1097 out:
1098         if (sizechecks && testcalls > simulatedopcount)
1099                 check_size();
1100         if (closeopen)
1101                 docloseopen();
1102 }
1103
1104
1105 void
1106 cleanup(sig)
1107         int     sig;
1108 {
1109         if (sig)
1110                 prt("signal %d\n", sig);
1111         prt("testcalls = %lu\n", testcalls);
1112         exit(sig);
1113 }
1114
1115
1116 void
1117 usage(void)
1118 {
1119         fprintf(stdout, "usage: %s",
1120                 "fsx [-dnqxAFLOWZ] [-b opnum] [-c Prob] [-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\
1121         -b opnum: beginning operation number (default 1)\n\
1122         -c P: 1 in P chance of file close+open at each op (default infinity)\n\
1123         -d: debug output for all operations\n\
1124         -f flush and invalidate cache after I/O\n\
1125         -l flen: the upper bound on file size (default 262144)\n\
1126         -m startop:endop: monitor (print debug output) specified byte range (default 0:infinity)\n\
1127         -n: no verifications of file size\n\
1128         -o oplen: the upper bound on operation size (default 65536)\n\
1129         -p progressinterval: debug output at specified operation interval\n\
1130         -q: quieter operation\n\
1131         -r readbdy: 4096 would make reads page aligned (default 1)\n\
1132         -s style: 1 gives smaller truncates (default 0)\n\
1133         -t truncbdy: 4096 would make truncates page aligned (default 1)\n\
1134         -w writebdy: 4096 would make writes page aligned (default 1)\n\
1135         -x: preallocate file space before starting, XFS only (default 0)\n\
1136         -y synchronize changes to a file\n"
1137
1138 #ifdef AIO
1139 "       -A: Use the AIO system calls\n"
1140 #endif
1141 "       -D startingop: debug output starting at specified operation\n"
1142 #ifdef FALLOCATE
1143 "       -F: Do not use fallocate (preallocation) calls\n"
1144 #endif
1145 #ifdef FALLOC_FL_PUNCH_HOLE
1146 "       -H: Do not use punch hole calls\n"
1147 #endif
1148 "       -L: fsxLite - no file creations & no file size changes\n\
1149         -N numops: total # operations to do (default infinity)\n\
1150         -O: use oplen (see -o flag) for every op (default random)\n\
1151         -P: save .fsxlog and .fsxgood files in dirpath (default ./)\n\
1152         -S seed: for random # generator (default 1) 0 gets timestamp\n\
1153         -W: mapped write operations DISabled\n\
1154         -R: read() system calls only (mapped reads disabled)\n\
1155         -Z: O_DIRECT (use -R, -W, -r and -w too)\n\
1156         fname: this filename is REQUIRED (no default)\n");
1157         exit(90);
1158 }
1159
1160
1161 int
1162 getnum(char *s, char **e)
1163 {
1164         int ret;
1165
1166         *e = (char *) 0;
1167         ret = strtol(s, e, 0);
1168         if (*e)
1169                 switch (**e) {
1170                 case 'b':
1171                 case 'B':
1172                         ret *= 512;
1173                         *e = *e + 1;
1174                         break;
1175                 case 'k':
1176                 case 'K':
1177                         ret *= 1024;
1178                         *e = *e + 1;
1179                         break;
1180                 case 'm':
1181                 case 'M':
1182                         ret *= 1024*1024;
1183                         *e = *e + 1;
1184                         break;
1185                 case 'w':
1186                 case 'W':
1187                         ret *= 4;
1188                         *e = *e + 1;
1189                         break;
1190                 }
1191         return (ret);
1192 }
1193
1194 #ifdef AIO
1195
1196 #define QSZ     1024
1197 io_context_t    io_ctx;
1198 struct iocb     iocb;
1199
1200 int aio_setup()
1201 {
1202         int ret;
1203         ret = io_queue_init(QSZ, &io_ctx);
1204         if (ret != 0) {
1205                 fprintf(stderr, "aio_setup: io_queue_init failed: %s\n",
1206                         strerror(ret));
1207                 return(-1);
1208         }
1209         return(0);
1210 }
1211
1212 int
1213 __aio_rw(int rw, int fd, char *buf, unsigned len, unsigned offset)
1214 {
1215         struct io_event event;
1216         static struct timespec ts;
1217         struct iocb *iocbs[] = { &iocb };
1218         int ret;
1219         long res;
1220
1221         if (rw == READ) {
1222                 io_prep_pread(&iocb, fd, buf, len, offset);
1223         } else {
1224                 io_prep_pwrite(&iocb, fd, buf, len, offset);
1225         }
1226
1227         ts.tv_sec = 30;
1228         ts.tv_nsec = 0;
1229         ret = io_submit(io_ctx, 1, iocbs);
1230         if (ret != 1) {
1231                 fprintf(stderr, "errcode=%d\n", ret);
1232                 fprintf(stderr, "aio_rw: io_submit failed: %s\n",
1233                                 strerror(ret));
1234                 goto out_error;
1235         }
1236
1237         ret = io_getevents(io_ctx, 1, 1, &event, &ts);
1238         if (ret != 1) {
1239                 if (ret == 0)
1240                         fprintf(stderr, "aio_rw: no events available\n");
1241                 else {
1242                         fprintf(stderr, "errcode=%d\n", -ret);
1243                         fprintf(stderr, "aio_rw: io_getevents failed: %s\n",
1244                                         strerror(-ret));
1245                 }
1246                 goto out_error;
1247         }
1248         if (len != event.res) {
1249                 /*
1250                  * The b0rked libaio defines event.res as unsigned.
1251                  * However the kernel strucuture has it signed,
1252                  * and it's used to pass negated error value.
1253                  * Till the library is fixed use the temp var.
1254                  */
1255                 res = (long)event.res;
1256                 if (res >= 0)
1257                         fprintf(stderr, "bad io length: %lu instead of %u\n",
1258                                         res, len);
1259                 else {
1260                         fprintf(stderr, "errcode=%ld\n", -res);
1261                         fprintf(stderr, "aio_rw: async io failed: %s\n",
1262                                         strerror(-res));
1263                         ret = res;
1264                         goto out_error;
1265                 }
1266
1267         }
1268         return event.res;
1269
1270 out_error:
1271         /*
1272          * The caller expects error return in traditional libc
1273          * convention, i.e. -1 and the errno set to error.
1274          */
1275         errno = -ret;
1276         return -1;
1277 }
1278
1279 int aio_rw(int rw, int fd, char *buf, unsigned len, unsigned offset)
1280 {
1281         int ret;
1282
1283         if (aio) {
1284                 ret = __aio_rw(rw, fd, buf, len, offset);
1285         } else {
1286                 if (rw == READ)
1287                         ret = read(fd, buf, len);
1288                 else
1289                         ret = write(fd, buf, len);
1290         }
1291         return ret;
1292 }
1293
1294 #endif
1295
1296 void
1297 test_fallocate()
1298 {
1299 #ifdef FALLOCATE
1300         if (!lite && fallocate_calls) {
1301                 if (fallocate(fd, 0, 0, 1) && errno == EOPNOTSUPP) {
1302                         if(!quiet)
1303                                 warn("main: filesystem does not support fallocate, disabling\n");
1304                         fallocate_calls = 0;
1305                 } else {
1306                         ftruncate(fd, 0);
1307                 }
1308         }
1309 #else /* ! FALLOCATE */
1310         fallocate_calls = 0;
1311 #endif
1312
1313 }
1314
1315 void
1316 test_punch_hole()
1317 {
1318 #ifdef FALLOC_FL_PUNCH_HOLE
1319         if (!lite && punch_hole_calls) {
1320                 if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
1321                                 0, 1) && errno == EOPNOTSUPP) {
1322                         if(!quiet)
1323                                 warn("main: filesystem does not support fallocate punch hole, disabling");
1324                         punch_hole_calls = 0;
1325                 } else
1326                         ftruncate(fd, 0);
1327         }
1328 #else /* ! PUNCH HOLE */
1329         punch_hole_calls = 0;
1330 #endif
1331 }
1332
1333 int
1334 main(int argc, char **argv)
1335 {
1336         int     i, style, ch;
1337         char    *endp;
1338         char goodfile[1024];
1339         char logfile[1024];
1340
1341         goodfile[0] = 0;
1342         logfile[0] = 0;
1343
1344         page_size = getpagesize();
1345         page_mask = page_size - 1;
1346         mmap_mask = page_mask;
1347         
1348
1349         setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */
1350
1351         while ((ch = getopt(argc, argv, "b:c:dfl:m:no:p:qr:s:t:w:xyAD:FHLN:OP:RS:WZ"))
1352                != EOF)
1353                 switch (ch) {
1354                 case 'b':
1355                         simulatedopcount = getnum(optarg, &endp);
1356                         if (!quiet)
1357                                 fprintf(stdout, "Will begin at operation %ld\n",
1358                                         simulatedopcount);
1359                         if (simulatedopcount == 0)
1360                                 usage();
1361                         simulatedopcount -= 1;
1362                         break;
1363                 case 'c':
1364                         closeprob = getnum(optarg, &endp);
1365                         if (!quiet)
1366                                 fprintf(stdout,
1367                                         "Chance of close/open is 1 in %d\n",
1368                                         closeprob);
1369                         if (closeprob <= 0)
1370                                 usage();
1371                         break;
1372                 case 'd':
1373                         debug = 1;
1374                         break;
1375                 case 'f':
1376                         flush = 1;
1377                         break;
1378                 case 'l':
1379                         maxfilelen = getnum(optarg, &endp);
1380                         if (maxfilelen <= 0)
1381                                 usage();
1382                         break;
1383                 case 'm':
1384                         monitorstart = getnum(optarg, &endp);
1385                         if (monitorstart < 0)
1386                                 usage();
1387                         if (!endp || *endp++ != ':')
1388                                 usage();
1389                         monitorend = getnum(endp, &endp);
1390                         if (monitorend < 0)
1391                                 usage();
1392                         if (monitorend == 0)
1393                                 monitorend = -1; /* aka infinity */
1394                         debug = 1;
1395                 case 'n':
1396                         sizechecks = 0;
1397                         break;
1398                 case 'o':
1399                         maxoplen = getnum(optarg, &endp);
1400                         if (maxoplen <= 0)
1401                                 usage();
1402                         break;
1403                 case 'p':
1404                         progressinterval = getnum(optarg, &endp);
1405                         if (progressinterval == 0)
1406                                 usage();
1407                         break;
1408                 case 'q':
1409                         quiet = 1;
1410                         break;
1411                 case 'r':
1412                         readbdy = getnum(optarg, &endp);
1413                         if (readbdy <= 0)
1414                                 usage();
1415                         break;
1416                 case 's':
1417                         style = getnum(optarg, &endp);
1418                         if (style < 0 || style > 1)
1419                                 usage();
1420                         break;
1421                 case 't':
1422                         truncbdy = getnum(optarg, &endp);
1423                         if (truncbdy <= 0)
1424                                 usage();
1425                         break;
1426                 case 'w':
1427                         writebdy = getnum(optarg, &endp);
1428                         if (writebdy <= 0)
1429                                 usage();
1430                         break;
1431                 case 'x':
1432                         prealloc = 1;
1433                         break;
1434                 case 'y':
1435                         do_fsync = 1;
1436                         break;
1437                 case 'A':
1438                         aio = 1;
1439                         break;
1440                 case 'D':
1441                         debugstart = getnum(optarg, &endp);
1442                         if (debugstart < 1)
1443                                 usage();
1444                         break;
1445                 case 'F':
1446                         fallocate_calls = 0;
1447                         break;
1448                 case 'H':
1449                         punch_hole_calls = 0;
1450                         break;
1451                 case 'L':
1452                         lite = 1;
1453                         break;
1454                 case 'N':
1455                         numops = getnum(optarg, &endp);
1456                         if (numops < 0)
1457                                 usage();
1458                         break;
1459                 case 'O':
1460                         randomoplen = 0;
1461                         break;
1462                 case 'P':
1463                         strncpy(goodfile, optarg, sizeof(goodfile));
1464                         strcat(goodfile, "/");
1465                         strncpy(logfile, optarg, sizeof(logfile));
1466                         strcat(logfile, "/");
1467                         break;
1468                 case 'R':
1469                         mapped_reads = 0;
1470                         break;
1471                 case 'S':
1472                         seed = getnum(optarg, &endp);
1473                         if (seed == 0)
1474                                 seed = time(0) % 10000;
1475                         if (!quiet)
1476                                 fprintf(stdout, "Seed set to %d\n", seed);
1477                         if (seed < 0)
1478                                 usage();
1479                         break;
1480                 case 'W':
1481                         mapped_writes = 0;
1482                         if (!quiet)
1483                                 fprintf(stdout, "mapped writes DISABLED\n");
1484                         break;
1485                 case 'Z':
1486                         o_direct = O_DIRECT;
1487                         break;
1488                 default:
1489                         usage();
1490                         /* NOTREACHED */
1491                 }
1492         argc -= optind;
1493         argv += optind;
1494         if (argc != 1)
1495                 usage();
1496         fname = argv[0];
1497
1498         signal(SIGHUP,  cleanup);
1499         signal(SIGINT,  cleanup);
1500         signal(SIGPIPE, cleanup);
1501         signal(SIGALRM, cleanup);
1502         signal(SIGTERM, cleanup);
1503         signal(SIGXCPU, cleanup);
1504         signal(SIGXFSZ, cleanup);
1505         signal(SIGVTALRM,       cleanup);
1506         signal(SIGUSR1, cleanup);
1507         signal(SIGUSR2, cleanup);
1508
1509         initstate(seed, state, 256);
1510         setstate(state);
1511         fd = open(fname,
1512                 O_RDWR|(lite ? 0 : O_CREAT|O_TRUNC)|o_direct, 0666);
1513         if (fd < 0) {
1514                 prterr(fname);
1515                 exit(91);
1516         }
1517 #ifdef XFS
1518         if (prealloc) {
1519                 xfs_flock64_t   resv = { 0 };
1520 #ifdef HAVE_XFS_PLATFORM_DEFS_H
1521                 if (!platform_test_xfs_fd(fd)) {
1522                         prterr(fname);
1523                         fprintf(stderr, "main: cannot prealloc, non XFS\n");
1524                         exit(96);
1525                 }
1526 #endif
1527                 resv.l_len = maxfilelen;
1528                 if ((xfsctl(fname, fd, XFS_IOC_RESVSP, &resv)) < 0) {
1529                         prterr(fname);
1530                         exit(97);
1531                 }
1532         }
1533 #endif
1534         strncat(goodfile, fname, 256);
1535         strcat (goodfile, ".fsxgood");
1536         fsxgoodfd = open(goodfile, O_RDWR|O_CREAT|O_TRUNC, 0666);
1537         if (fsxgoodfd < 0) {
1538                 prterr(goodfile);
1539                 exit(92);
1540         }
1541         strncat(logfile, fname, 256);
1542         strcat (logfile, ".fsxlog");
1543         fsxlogf = fopen(logfile, "w");
1544         if (fsxlogf == NULL) {
1545                 prterr(logfile);
1546                 exit(93);
1547         }
1548
1549 #ifdef AIO
1550         if (aio) 
1551                 aio_setup();
1552 #endif
1553
1554         if (lite) {
1555                 off_t ret;
1556                 file_size = maxfilelen = lseek(fd, (off_t)0, SEEK_END);
1557                 if (file_size == (off_t)-1) {
1558                         prterr(fname);
1559                         warn("main: lseek eof");
1560                         exit(94);
1561                 }
1562                 ret = lseek(fd, (off_t)0, SEEK_SET);
1563                 if (ret == (off_t)-1) {
1564                         prterr(fname);
1565                         warn("main: lseek 0");
1566                         exit(95);
1567                 }
1568         }
1569         original_buf = (char *) malloc(maxfilelen);
1570         for (i = 0; i < maxfilelen; i++)
1571                 original_buf[i] = random() % 256;
1572         good_buf = (char *) malloc(maxfilelen + writebdy);
1573         good_buf = round_ptr_up(good_buf, writebdy, 0);
1574         memset(good_buf, '\0', maxfilelen);
1575         temp_buf = (char *) malloc(maxoplen + readbdy);
1576         temp_buf = round_ptr_up(temp_buf, readbdy, 0);
1577         memset(temp_buf, '\0', maxoplen);
1578         if (lite) {     /* zero entire existing file */
1579                 ssize_t written;
1580
1581                 written = write(fd, good_buf, (size_t)maxfilelen);
1582                 if (written != maxfilelen) {
1583                         if (written == -1) {
1584                                 prterr(fname);
1585                                 warn("main: error on write");
1586                         } else
1587                                 warn("main: short write, 0x%x bytes instead "
1588                                         "of 0x%lx\n",
1589                                         (unsigned)written,
1590                                         maxfilelen);
1591                         exit(98);
1592                 }
1593         } else 
1594                 check_trunc_hack();
1595
1596         test_fallocate();
1597         test_punch_hole();
1598
1599         while (numops == -1 || numops--)
1600                 test();
1601
1602         if (close(fd)) {
1603                 prterr("close");
1604                 report_failure(99);
1605         }
1606         prt("All operations completed A-OK!\n");
1607
1608         exit(0);
1609         return 0;
1610 }