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