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