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