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