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