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