Whamcloud - gitweb
0e0d198d55a85ca12c4c8697f194f29c7e8e8a0d
[fs/lustre-release.git] / lustre / tests / fsx.c
1 /*
2  * Copyright (c) 1998-2001 Apple Computer, Inc. All rights reserved.
3  *
4  * @APPLE_LICENSE_HEADER_START@
5  *
6  * The contents of this file constitute Original Code as defined in and
7  * are subject to the Apple Public Source License Version 1.1 (the
8  * "License").  You may not use this file except in compliance with the
9  * License.  Please obtain a copy of the License at
10  * http://www.apple.com/publicsource and read it before using this file.
11  *
12  * This Original Code and all software distributed under the License are
13  * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
17  * License for the specific language governing rights and limitations
18  * under the License.
19  *
20  * @APPLE_LICENSE_HEADER_END@
21  *
22  *      File:   fsx.c
23  *      Author: Avadis Tevanian, Jr.
24  *
25  *      File system exerciser.
26  *
27  *      Rewrite and enhancements 1998-2001 Conrad Minshall -- conrad@mac.com
28  *
29  *      Various features from Joe Sokol, Pat Dirks, and Clark Warner.
30  *
31  *      Small changes to work under Linux -- davej@suse.de
32  *
33  *      Sundry porting patches from Guy Harris 12/2001
34  * $FreeBSD: src/tools/regression/fsx/fsx.c,v 1.1 2001/12/20 04:15:57 jkh Exp $
35  */
36
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #if defined(_UWIN) || defined(__linux__)
40 # include <sys/param.h>
41 # include <limits.h>
42 # include <time.h>
43 # include <strings.h>
44 # include <sys/time.h>
45 #endif
46 #include <fcntl.h>
47 #include <sys/mman.h>
48 #ifndef MAP_FILE
49 # define MAP_FILE 0
50 #endif
51 #include <limits.h>
52 #include <signal.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <unistd.h>
57 #include <stdarg.h>
58 #include <errno.h>
59
60 #define NUMPRINTCOLUMNS 32      /* # columns of data to print on each line */
61
62 /*
63  *      A log entry is an operation and a bunch of arguments.
64  */
65
66 struct log_entry {
67         int     operation;
68         struct timeval tv;
69         int     args[3];
70 };
71
72 #define LOGSIZE 1000
73
74 struct log_entry        oplog[LOGSIZE]; /* the log */
75 int                     logptr = 0;     /* current position in log */
76 int                     logcount = 0;   /* total ops */
77
78 /*
79  *      Define operations
80  */
81
82 #define OP_READ         1
83 #define OP_WRITE        2
84 #define OP_TRUNCATE     3
85 #define OP_CLOSEOPEN    4
86 #define OP_MAPREAD      5
87 #define OP_MAPWRITE     6
88 #define OP_SKIPPED      7
89
90 int page_size;
91 int page_mask;
92
93 char    *original_buf;                  /* a pointer to the original data */
94 char    *good_buf;                      /* a pointer to the correct data */
95 char    *temp_buf;                      /* a pointer to the current data */
96 char    *fname;                         /* name of our test file */
97 char    logfile[1024];                  /* name of our log file */
98 char    goodfile[1024];                 /* name of our test file */
99
100 off_t           file_size = 0;
101 off_t           biggest = 0;
102 char            state[256];
103 unsigned long   testcalls = 0;          /* calls to function "test" */
104
105 unsigned long   simulatedopcount = 0;   /* -b flag */
106 int     closeprob = 0;                  /* -c flag */
107 int     debug = 0;                      /* -d flag */
108 unsigned long   debugstart = 0;         /* -D flag */
109 unsigned long   maxfilelen = 256 * 1024;        /* -l flag */
110 int     sizechecks = 1;                 /* -n flag disables them */
111 int     maxoplen = 64 * 1024;           /* -o flag */
112 int     quiet = 0;                      /* -q flag */
113 unsigned long progressinterval = 0;     /* -p flag */
114 int     readbdy = 1;                    /* -r flag */
115 int     style = 0;                      /* -s flag */
116 int     truncbdy = 1;                   /* -t flag */
117 int     writebdy = 1;                   /* -w flag */
118 long    monitorstart = -1;              /* -m flag */
119 long    monitorend = -1;                /* -m flag */
120 int     lite = 0;                       /* -L flag */
121 long    numops = -1;                    /* -N flag */
122 int     randomoplen = 1;                /* -O flag disables it */
123 int     seed = 1;                       /* -S flag */
124 int     mapped_writes = 1;              /* -W flag disables */
125 int     mapped_reads = 1;               /* -R flag disables it */
126 int     fsxgoodfd = 0;
127 FILE *  fsxlogf = NULL;
128 int badoff = -1;
129
130
131 void
132 vwarnc(code, fmt, ap)
133         int code;
134         const char *fmt;
135         va_list ap;
136 {
137         fprintf(stderr, "fsx: ");
138         if (fmt != NULL) {
139                 vfprintf(stderr, fmt, ap);
140                 fprintf(stderr, ": ");
141         }
142         fprintf(stderr, "%s\n", strerror(code));
143 }
144
145
146 void
147 warn(const char * fmt, ...)
148 {
149         va_list ap;
150         va_start(ap, fmt);
151         vwarnc(errno, fmt, ap);
152         va_end(ap);
153 }
154
155
156 void
157 __attribute__((format(printf, 1, 2)))
158 prt(char *fmt, ...)
159 {
160         va_list args;
161
162         va_start(args, fmt);
163         vfprintf(stdout, fmt, args);
164         va_end(args);
165
166         if (fsxlogf) {
167                 va_start(args, fmt);
168                 vfprintf(fsxlogf, fmt, args);
169                 va_end(args);
170         }
171 }
172
173 void
174 prterr(char *prefix)
175 {
176         prt("%s%s%s\n", prefix, prefix ? ": " : "", strerror(errno));
177 }
178
179
180 void
181 log4(int operation, int arg0, int arg1, int arg2, struct timeval *tv)
182 {
183         struct log_entry *le;
184
185         le = &oplog[logptr];
186         le->tv = *tv;
187         le->operation = 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                 lp = &oplog[i];
217                 prt("%d: %lu.%06lu ", opnum,
218                     lp->tv.tv_sec, lp->tv.tv_usec);
219
220                 switch (lp->operation) {
221                 case OP_MAPREAD:
222                         prt("MAPREAD  0x%x thru 0x%x (0x%x bytes)",
223                             lp->args[0], lp->args[0] + lp->args[1] - 1,
224                             lp->args[1]);
225                         if (badoff >= lp->args[0] && badoff <
226                                                      lp->args[0] + lp->args[1])
227                                 prt("\t***RRRR***");
228                         break;
229                 case OP_MAPWRITE:
230                         prt("MAPWRITE 0x%x thru 0x%x (0x%x bytes)",
231                             lp->args[0], lp->args[0] + lp->args[1] - 1,
232                             lp->args[1]);
233                         if (badoff >= lp->args[0] && badoff <
234                                                      lp->args[0] + lp->args[1])
235                                 prt("\t******WWWW");
236                         break;
237                 case OP_READ:
238                         prt("READ     0x%x thru 0x%x (0x%x bytes)",
239                             lp->args[0], lp->args[0] + lp->args[1] - 1,
240                             lp->args[1]);
241                         if (badoff >= lp->args[0] &&
242                             badoff < lp->args[0] + lp->args[1])
243                                 prt("\t***RRRR***");
244                         break;
245                 case OP_WRITE:
246                         prt("WRITE    0x%x thru 0x%x (0x%x bytes)",
247                             lp->args[0], lp->args[0] + lp->args[1] - 1,
248                             lp->args[1]);
249                         if (lp->args[0] > lp->args[2])
250                                 prt(" HOLE");
251                         else if (lp->args[0] + lp->args[1] > lp->args[2])
252                                 prt(" EXTEND");
253                         if ((badoff >= lp->args[0] || badoff >=lp->args[2]) &&
254                             badoff < lp->args[0] + lp->args[1])
255                                 prt("\t***WWWW");
256                         break;
257                 case OP_TRUNCATE:
258                         down = lp->args[0] < lp->args[1];
259                         prt("TRUNCATE %s\tfrom 0x%x to 0x%x",
260                             down ? "DOWN" : "UP", lp->args[1], lp->args[0]);
261                         if (badoff >= lp->args[!down] &&
262                             badoff < lp->args[!!down])
263                                 prt("\t******WWWW");
264                         break;
265                 case OP_CLOSEOPEN:
266                         prt("CLOSE/OPEN");
267                         break;
268                 case OP_SKIPPED:
269                         prt("SKIPPED (no operation)");
270                         break;
271                 default:
272                         prt("BOGUS LOG ENTRY (operation code = %d)!",
273                             lp->operation);
274                 }
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"
302                                 "save 0x%llx bytes instead of 0x%llx\n", 
303                                 (unsigned long long)size_by_seek,
304                                 (unsigned long long)bufferlength);
305                         bufferlength = size_by_seek;
306                 }
307         }
308
309         ret = lseek(fd, (off_t)0, SEEK_SET);
310         if (ret == (off_t)-1)
311                 prterr("save_buffer: lseek 0");
312
313         byteswritten = write(fd, buffer, (size_t)bufferlength);
314         if (byteswritten != bufferlength) {
315                 if (byteswritten == -1)
316                         prterr("save_buffer write");
317                 else
318                         warn("save_buffer: short write, 0x%x bytes instead"
319                                 "of 0x%llx\n",
320                              (unsigned)byteswritten,
321                              (unsigned long long)bufferlength);
322         }
323 }
324
325
326 void
327 report_failure(int status)
328 {
329         logdump();
330
331         if (fsxgoodfd) {
332                 if (good_buf) {
333                         save_buffer(good_buf, file_size, fsxgoodfd);
334                         prt("Correct content saved for comparison\n");
335                         prt("(maybe hexdump \"%s\" vs \"%s\")\n",
336                             fname, goodfile);
337                 }
338                 close(fsxgoodfd);
339         }
340         exit(status);
341 }
342
343
344 #define short_at(cp) ((unsigned short)((*((unsigned char *)(cp)) << 8) | \
345                                         *(((unsigned char *)(cp)) + 1)))
346
347 void
348 check_buffers(unsigned offset, unsigned size)
349 {
350         unsigned char c, t;
351         unsigned i = 0;
352         unsigned n = 0;
353         unsigned op = 0;
354         unsigned bad = 0;
355
356         if (memcmp(good_buf + offset, temp_buf, size) != 0) {
357                 prt("READ BAD DATA: offset = 0x%x, size = 0x%x\n",
358                     offset, size);
359                 prt("OFFSET\tGOOD\tBAD\tRANGE\n");
360                 while (size > 0) {
361                         c = good_buf[offset];
362                         t = temp_buf[i];
363                         if (c != t) {
364                                 if (n == 0) {
365                                         bad = short_at(&temp_buf[i]);
366                                         prt("0x%5x\t0x%04x\t0x%04x", offset,
367                                             short_at(&good_buf[offset]), bad);
368                                         op = temp_buf[offset & 1 ? i+1 : i];
369                                 }
370                                 n++;
371                                 badoff = offset;
372                         }
373                         offset++;
374                         i++;
375                         size--;
376                 }
377                 if (n) {
378                         prt("\t0x%5x\n", n);
379                         if (bad)
380                                 prt("operation# (mod 256) for the bad data"
381                                         "may be %u\n", ((unsigned)op & 0xff));
382                         else
383                                 prt("operation# (mod 256) for the bad data"
384                                         "unknown, check HOLE and EXTEND ops\n");
385                 } else
386                         prt("????????????????\n");
387                 report_failure(110);
388         }
389 }
390
391 struct test_file {
392         char *path;
393         int fd;
394 } *test_files = NULL;
395
396 int num_test_files = 0;
397 enum fd_iteration_policy {
398         FD_SINGLE,
399         FD_ROTATE,
400         FD_RANDOM,
401 };
402 int fd_policy = FD_RANDOM;
403 int fd_last = 0;
404
405 struct test_file * 
406 get_tf(void)
407 {
408         unsigned index = 0;
409
410         switch (fd_policy) {
411                 case FD_ROTATE:
412                         index = fd_last++;
413                         break;
414                 case FD_RANDOM:
415                         index = random();
416                         break;
417                 case FD_SINGLE:
418                         index = 0;
419                         break;
420                 default:
421                         prt("unknown policy");
422                         exit(1);
423                         break;
424         }
425         return &test_files[ index % num_test_files ];
426 }
427
428 void
429 assign_fd_policy(char *policy)
430 {
431         if (!strcmp(policy, "random"))
432                 fd_policy = FD_RANDOM;
433         else if (!strcmp(policy, "rotate"))
434                 fd_policy = FD_ROTATE;
435         else {
436                 prt("unknown -I policy: '%s'\n", policy);
437                 exit(1);
438         }
439 }
440
441 int 
442 get_fd(void)
443 {
444         struct test_file *tf = get_tf();
445         return tf->fd;
446 }
447
448 static const char *basename(const char *path)
449 {
450         char *c = strrchr(path, '/');
451
452         return c ? c++ : path;
453 }
454
455 void 
456 open_test_files(char **argv, int argc)
457 {
458         struct test_file *tf;
459         int i;
460
461         num_test_files = argc;
462         if (num_test_files == 1)
463                 fd_policy = FD_SINGLE;
464
465         test_files = calloc(num_test_files, sizeof(*test_files));
466         if (test_files == NULL) {
467                 prterr("reallocating space for test files");
468                 exit(1);
469         }
470
471         for (i = 0, tf = test_files; i < num_test_files; i++, tf++) {
472
473                 tf->path = argv[i];
474                 tf->fd = open(tf->path, O_RDWR|(lite ? 0 : O_CREAT|O_TRUNC), 
475                                 0666);
476                 if (tf->fd < 0) {
477                         prterr(tf->path);
478                         exit(91);
479                 }
480         }
481
482         if (quiet || fd_policy == FD_SINGLE)
483                 return;
484
485         for (i = 0, tf = test_files; i < num_test_files; i++, tf++)
486                 prt("fd %d: %s\n", i, tf->path);
487 }
488
489 void
490 close_test_files(void)
491 {
492         int i;
493         struct test_file *tf;
494
495         for (i = 0, tf = test_files; i < num_test_files; i++, tf++) {
496                 if (close(tf->fd)) {
497                         prterr("close");
498                         report_failure(99);
499                 }
500         }
501 }
502
503
504 void
505 check_size(void)
506 {
507         struct stat     statbuf;
508         off_t   size_by_seek;
509         int fd = get_fd();
510
511         if (fstat(fd, &statbuf)) {
512                 prterr("check_size: fstat");
513                 statbuf.st_size = -1;
514         }
515         size_by_seek = lseek(fd, (off_t)0, SEEK_END);
516         if (file_size != statbuf.st_size || file_size != size_by_seek) {
517                 prt("Size error: expected 0x%llx stat 0x%llx seek 0x%llx\n",
518                     (unsigned long long)file_size,
519                     (unsigned long long)statbuf.st_size,
520                     (unsigned long long)size_by_seek);
521                 report_failure(120);
522         }
523 }
524
525
526 void
527 check_trunc_hack(void)
528 {
529         struct stat statbuf;
530         int fd = get_fd();
531
532         ftruncate(fd, (off_t)0);
533         ftruncate(fd, (off_t)100000);
534         fstat(fd, &statbuf);
535         if (statbuf.st_size != (off_t)100000) {
536                 prt("no extend on truncate! not posix!\n");
537                 exit(130);
538         }
539         ftruncate(fd, 0);
540 }
541
542 static char *tf_buf = NULL;
543 static int max_tf_len = 0;
544
545 void
546 alloc_tf_buf(void)
547 {
548         char dummy = '\0';
549         int highest = num_test_files - 1;
550         int len;
551
552         len = snprintf(&dummy, 0, "%u ", highest);
553         if (len < 1) {
554                 prterr("finding max tf_buf");
555                 exit(1);
556         }
557         len++;
558         tf_buf = malloc(len);
559         if (tf_buf == NULL) {
560                 prterr("allocating tf_buf");
561                 exit(1);
562         }
563         max_tf_len = snprintf(tf_buf, len, "%u ", highest);
564         if (max_tf_len < 1) {
565                 prterr("fiding max_tv_len\n");
566                 exit(1);
567         }
568         if (max_tf_len != len - 1) {
569                 warn("snprintf() gave %d instead of %d?\n",
570                                 max_tf_len, len - 1);
571                 exit(1);
572         }
573 }
574
575 char * 
576 fill_tf_buf(struct test_file *tf)
577 {
578         if (tf_buf == NULL)
579                 alloc_tf_buf();
580
581         sprintf(tf_buf,"%lu ", (unsigned long)(tf - test_files));
582         return tf_buf;
583 }
584
585 void
586 output_line(struct test_file *tf, int op, unsigned offset, 
587                 unsigned size, struct timeval *tv)
588 {
589         char *tf_num = "";
590
591         char *ops[] = {
592                 [OP_READ] = "read",
593                 [OP_WRITE] = "write",
594                 [OP_TRUNCATE] = "trunc from",
595                 [OP_MAPREAD] = "mapread",
596                 [OP_MAPWRITE] = "mapwrite",
597         };
598
599         if (fd_policy != FD_SINGLE)
600                 tf_num = fill_tf_buf(tf);
601
602         /* W. */
603         if (!(!quiet && ((progressinterval &&
604                         testcalls % progressinterval == 0) ||
605                        (debug &&
606                         (monitorstart == -1 ||
607                          (offset + size > monitorstart &&
608                           (monitorend == -1 || offset <= monitorend)))))))
609                 return;
610
611         prt("%06lu %lu.%06lu %.*s%-10s %#08x %s %#08x\t(0x%x bytes)\n",
612                 testcalls, tv->tv_sec, tv->tv_usec, max_tf_len,
613                 tf_num, ops[op], 
614                 offset, op == OP_TRUNCATE ? " to " : "thru",
615                 offset + size - 1, size);
616 }
617
618 void
619 doread(unsigned offset, unsigned size)
620 {
621         struct timeval t;
622         off_t ret;
623         unsigned iret;
624         struct test_file *tf = get_tf();
625         int fd = tf->fd;
626
627         offset -= offset % readbdy;
628         gettimeofday(&t, NULL);
629         if (size == 0) {
630                 if (!quiet && testcalls > simulatedopcount)
631                         prt("skipping zero size read\n");
632                 log4(OP_SKIPPED, OP_READ, offset, size, &t);
633                 return;
634         }
635         if (size + offset > file_size) {
636                 if (!quiet && testcalls > simulatedopcount)
637                         prt("skipping seek/read past end of file\n");
638                 log4(OP_SKIPPED, OP_READ, offset, size, &t);
639                 return;
640         }
641
642         log4(OP_READ, offset, size, 0, &t);
643
644         if (testcalls <= simulatedopcount)
645                 return;
646
647         output_line(tf, OP_READ, offset, size, &t);
648
649         ret = lseek(fd, (off_t)offset, SEEK_SET);
650         if (ret == (off_t)-1) {
651                 prterr("doread: lseek");
652                 report_failure(140);
653         }
654         iret = read(fd, temp_buf, size);
655         if (!quiet && (debug > 1 &&
656                         (monitorstart == -1 ||
657                          (offset + size > monitorstart &&
658                           (monitorend == -1 || offset <= monitorend))))) {
659                 gettimeofday(&t, NULL);
660                 prt("       %lu.%06lu read done\n", t.tv_sec, t.tv_usec);
661         }
662         if (iret != size) {
663                 if (iret == -1)
664                         prterr("doread: read");
665                 else
666                         prt("short read: 0x%x bytes instead of 0x%x\n",
667                             iret, size);
668                 report_failure(141);
669         }
670         check_buffers(offset, size);
671 }
672
673
674 void
675 domapread(unsigned offset, unsigned size)
676 {
677         struct timeval t;
678         unsigned pg_offset;
679         unsigned map_size;
680         char    *p;
681         struct test_file *tf = get_tf();
682         int fd = tf->fd;
683
684         offset -= offset % readbdy;
685         gettimeofday(&t, NULL);
686         if (size == 0) {
687                 if (!quiet && testcalls > simulatedopcount)
688                         prt("skipping zero size read\n");
689                 log4(OP_SKIPPED, OP_MAPREAD, offset, size, &t);
690                 return;
691         }
692         if (size + offset > file_size) {
693                 if (!quiet && testcalls > simulatedopcount)
694                         prt("skipping seek/read past end of file\n");
695                 log4(OP_SKIPPED, OP_MAPREAD, offset, size, &t);
696                 return;
697         }
698
699         log4(OP_MAPREAD, offset, size, 0, &t);
700
701         if (testcalls <= simulatedopcount)
702                 return;
703
704         output_line(tf, OP_MAPREAD, offset, size, &t);
705
706         pg_offset = offset & page_mask;
707         map_size  = pg_offset + size;
708
709         if ((p = mmap(0, map_size, PROT_READ, MAP_FILE | MAP_SHARED, fd,
710                       (off_t)(offset - pg_offset))) == MAP_FAILED) {
711                 prterr("domapread: mmap");
712                 report_failure(190);
713         }
714         if (!quiet && (debug > 1 &&
715                         (monitorstart == -1 ||
716                          (offset + size > monitorstart &&
717                           (monitorend == -1 || offset <= monitorend))))) {
718                 gettimeofday(&t, NULL);
719                 prt("       %lu.%06lu mmap done\n", t.tv_sec, t.tv_usec);
720         }
721         memcpy(temp_buf, p + pg_offset, size);
722         if (!quiet && (debug > 1 &&
723                         (monitorstart == -1 ||
724                          (offset + size > monitorstart &&
725                           (monitorend == -1 || offset <= monitorend))))) {
726                 gettimeofday(&t, NULL);
727                 prt("       %lu.%06lu memcpy done\n", t.tv_sec, t.tv_usec);
728         }
729         if (munmap(p, map_size) != 0) {
730                 prterr("domapread: munmap");
731                 report_failure(191);
732         }
733         if (!quiet && (debug > 1 &&
734                         (monitorstart == -1 ||
735                          (offset + size > monitorstart &&
736                           (monitorend == -1 || offset <= monitorend))))) {
737                 gettimeofday(&t, NULL);
738                 prt("       %lu.%06lu munmap done\n", t.tv_sec, t.tv_usec);
739         }
740
741         check_buffers(offset, size);
742 }
743
744
745 void
746 gendata(char *original_buf, char *good_buf, unsigned offset, unsigned size)
747 {
748         while (size--) {
749                 good_buf[offset] = testcalls % 256;
750                 if (offset % 2)
751                         good_buf[offset] += original_buf[offset];
752                 offset++;
753         }
754 }
755
756
757 void
758 dowrite(unsigned offset, unsigned size)
759 {
760         struct timeval t;
761         off_t ret;
762         unsigned iret;
763         struct test_file *tf = get_tf();
764         int fd = tf->fd;
765
766         offset -= offset % writebdy;
767         gettimeofday(&t, NULL);
768         if (size == 0) {
769                 if (!quiet && testcalls > simulatedopcount)
770                         prt("skipping zero size write\n");
771                 log4(OP_SKIPPED, OP_WRITE, offset, size, &t);
772                 return;
773         }
774
775         log4(OP_WRITE, offset, size, file_size, &t);
776
777         gendata(original_buf, good_buf, offset, size);
778         if (file_size < offset + size) {
779                 if (file_size < offset)
780                         memset(good_buf + file_size, '\0', offset - file_size);
781                 file_size = offset + size;
782                 if (lite) {
783                         warn("Lite file size bug in fsx!");
784                         report_failure(149);
785                 }
786         }
787
788         if (testcalls <= simulatedopcount)
789                 return;
790
791         output_line(tf, OP_WRITE, offset, size, &t);
792
793         ret = lseek(fd, (off_t)offset, SEEK_SET);
794         if (ret == (off_t)-1) {
795                 prterr("dowrite: lseek");
796                 report_failure(150);
797         }
798         iret = write(fd, good_buf + offset, size);
799         if (!quiet && (debug > 1 &&
800                         (monitorstart == -1 ||
801                          (offset + size > monitorstart &&
802                           (monitorend == -1 || offset <= monitorend))))) {
803                 gettimeofday(&t, NULL);
804                 prt("       %lu.%06lu write done\n", t.tv_sec, t.tv_usec);
805         }
806         if (iret != size) {
807                 if (iret == -1)
808                         prterr("dowrite: write");
809                 else
810                         prt("short write: 0x%x bytes instead of 0x%x\n",
811                             iret, size);
812                 report_failure(151);
813         }
814 }
815
816
817 void
818 domapwrite(unsigned offset, unsigned size)
819 {
820         struct timeval t;
821         unsigned pg_offset;
822         unsigned map_size;
823         off_t    cur_filesize;
824         char    *p;
825         struct test_file *tf = get_tf();
826         int fd = tf->fd;
827
828         offset -= offset % writebdy;
829         gettimeofday(&t, NULL);
830         if (size == 0) {
831                 if (!quiet && testcalls > simulatedopcount)
832                         prt("skipping zero size write\n");
833                 log4(OP_SKIPPED, OP_MAPWRITE, offset, size, &t);
834                 return;
835         }
836         cur_filesize = file_size;
837
838         log4(OP_MAPWRITE, offset, size, 0, &t);
839
840         gendata(original_buf, good_buf, offset, size);
841         if (file_size < offset + size) {
842                 if (file_size < offset)
843                         memset(good_buf + file_size, '\0', offset - file_size);
844                 file_size = offset + size;
845                 if (lite) {
846                         warn("Lite file size bug in fsx!");
847                         report_failure(200);
848                 }
849         }
850
851         if (testcalls <= simulatedopcount)
852                 return;
853
854         output_line(tf, OP_MAPWRITE, offset, size, &t);
855
856         if (file_size > cur_filesize) {
857                 if (ftruncate(fd, file_size) == -1) {
858                         prterr("domapwrite: ftruncate");
859                         exit(201);
860                 }
861                 if (!quiet && (debug > 1 &&
862                                (monitorstart == -1 ||
863                                 (offset + size > monitorstart &&
864                                  (monitorend == -1 || offset <= monitorend))))) {
865                         gettimeofday(&t, NULL);
866                         prt("       %lu.%06lu truncate done\n", t.tv_sec, t.tv_usec);
867         }
868         }
869         pg_offset = offset & page_mask;
870         map_size  = pg_offset + size;
871
872         if ((p = mmap(0, map_size, PROT_READ | PROT_WRITE, MAP_FILE|MAP_SHARED,
873                       fd, (off_t)(offset - pg_offset))) == MAP_FAILED) {
874                 prterr("domapwrite: mmap");
875                 report_failure(202);
876         }
877         if (!quiet && (debug > 1 &&
878                         (monitorstart == -1 ||
879                          (offset + size > monitorstart &&
880                           (monitorend == -1 || offset <= monitorend))))) {
881                 gettimeofday(&t, NULL);
882                 prt("       %lu.%06lu mmap done\n", t.tv_sec, t.tv_usec);
883         }
884         memcpy(p + pg_offset, good_buf + offset, size);
885         if (!quiet && (debug > 1 &&
886                         (monitorstart == -1 ||
887                          (offset + size > monitorstart &&
888                           (monitorend == -1 || offset <= monitorend))))) {
889                 gettimeofday(&t, NULL);
890                 prt("       %lu.%06lu memcpy done\n", t.tv_sec, t.tv_usec);
891         }
892         if (msync(p, map_size, 0) != 0) {
893                 prterr("domapwrite: msync");
894                 report_failure(203);
895         }
896         if (!quiet && (debug > 1 &&
897                         (monitorstart == -1 ||
898                          (offset + size > monitorstart &&
899                           (monitorend == -1 || offset <= monitorend))))) {
900                 gettimeofday(&t, NULL);
901                 prt("       %lu.%06lu msync done\n", t.tv_sec, t.tv_usec);
902         }
903         if (munmap(p, map_size) != 0) {
904                 prterr("domapwrite: munmap");
905                 report_failure(204);
906         }
907         if (!quiet && (debug > 1 &&
908                         (monitorstart == -1 ||
909                          (offset + size > monitorstart &&
910                           (monitorend == -1 || offset <= monitorend))))) {
911                 gettimeofday(&t, NULL);
912                 prt("       %lu.%06lu munmap done\n", t.tv_sec, t.tv_usec);
913         }
914 }
915
916
917 void
918 dotruncate(unsigned size)
919 {
920         struct timeval t;
921         int oldsize = file_size;
922         struct test_file *tf = get_tf();
923         int fd = tf->fd;
924
925         size -= size % truncbdy;
926         gettimeofday(&t, NULL);
927         if (size > biggest) {
928                 biggest = size;
929                 if (!quiet && testcalls > simulatedopcount)
930                         prt("truncating to largest ever: 0x%x\n", size);
931         }
932
933         log4(OP_TRUNCATE, size, (unsigned)file_size, 0, &t);
934
935         if (size > file_size)
936                 memset(good_buf + file_size, '\0', size - file_size);
937         file_size = size;
938
939         if (testcalls <= simulatedopcount)
940                 return;
941
942         output_line(tf, OP_TRUNCATE, oldsize, size, &t);
943
944         if (ftruncate(fd, (off_t)size) == -1) {
945                 prt("ftruncate1: %x\n", size);
946                 prterr("dotruncate: ftruncate");
947                 report_failure(160);
948         }
949         if (!quiet && debug > 1) {
950                 gettimeofday(&t, NULL);
951                 prt("       %lu.%06lu trunc done\n", t.tv_sec, t.tv_usec);
952         }
953 }
954
955
956 void
957 writefileimage()
958 {
959         ssize_t iret;
960         int fd = get_fd();
961
962         if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
963                 prterr("writefileimage: lseek");
964                 report_failure(171);
965         }
966         iret = write(fd, good_buf, file_size);
967         if ((off_t)iret != file_size) {
968                 if (iret == -1)
969                         prterr("writefileimage: write");
970                 else
971                         prt("short write: 0x%lx bytes instead of 0x%llx\n",
972                             (unsigned long)iret, 
973                             (unsigned long long)file_size);
974                 report_failure(172);
975         }
976         if (lite ? 0 : ftruncate(fd, file_size) == -1) {
977                 prt("ftruncate2: %llx\n", (unsigned long long)file_size);
978                 prterr("writefileimage: ftruncate");
979                 report_failure(173);
980         }
981 }
982
983
984 void
985 docloseopen(void)
986 {
987         struct timeval t;
988         struct test_file *tf = get_tf();
989
990         if (testcalls <= simulatedopcount)
991                 return;
992
993         gettimeofday(&t, NULL);
994         log4(OP_CLOSEOPEN, file_size, (unsigned)file_size, 0, &t);
995
996         if (debug)
997                 prt("%06lu %lu.%06lu close/open\n", testcalls, t.tv_sec,
998                     t.tv_usec);
999         if (close(tf->fd)) {
1000                 prterr("docloseopen: close");
1001                 report_failure(180);
1002         }
1003         if (!quiet && debug > 1) {
1004                 gettimeofday(&t, NULL);
1005                 prt("       %lu.%06lu close done\n", t.tv_sec, t.tv_usec);
1006         }
1007         tf->fd = open(tf->path, O_RDWR, 0);
1008         if (tf->fd < 0) {
1009                 prterr("docloseopen: open");
1010                 report_failure(181);
1011         }
1012         if (!quiet && debug > 1) {
1013                 gettimeofday(&t, NULL);
1014                 prt("       %lu.%06lu open done\n", t.tv_sec, t.tv_usec);
1015         }
1016 }
1017
1018
1019 void
1020 test(void)
1021 {
1022         unsigned long   offset;
1023         unsigned long   size = maxoplen;
1024         unsigned long   rv = random();
1025         unsigned long   op = rv % (3 + !lite + mapped_writes);
1026
1027         /* turn off the map read if necessary */
1028
1029         if (op == 2 && !mapped_reads)
1030             op = 0;
1031
1032         if (simulatedopcount > 0 && testcalls == simulatedopcount)
1033                 writefileimage();
1034
1035         testcalls++;
1036
1037         if (debugstart > 0 && testcalls >= debugstart)
1038                 debug = 1;
1039
1040         if (!quiet && testcalls < simulatedopcount && testcalls % 100000 == 0)
1041                 prt("%lu...\n", testcalls);
1042
1043         /*
1044          * READ:        op = 0
1045          * WRITE:       op = 1
1046          * MAPREAD:     op = 2
1047          * TRUNCATE:    op = 3
1048          * MAPWRITE:    op = 3 or 4
1049          */
1050         if (lite ? 0 : op == 3 && (style & 1) == 0) /* vanilla truncate? */
1051                 dotruncate(random() % maxfilelen);
1052         else {
1053                 if (randomoplen)
1054                         size = random() % (maxoplen+1);
1055                 if (lite ? 0 : op == 3)
1056                         dotruncate(size);
1057                 else {
1058                         offset = random();
1059                         if (op == 1 || op == (lite ? 3 : 4)) {
1060                                 offset %= maxfilelen;
1061                                 if (offset + size > maxfilelen)
1062                                         size = maxfilelen - offset;
1063                                 if (op != 1)
1064                                         domapwrite(offset, size);
1065                                 else
1066                                         dowrite(offset, size);
1067                         } else {
1068                                 if (file_size)
1069                                         offset %= file_size;
1070                                 else
1071                                         offset = 0;
1072                                 if (offset + size > file_size)
1073                                         size = file_size - offset;
1074                                 if (op != 0)
1075                                         domapread(offset, size);
1076                                 else
1077                                         doread(offset, size);
1078                         }
1079                 }
1080         }
1081         if (sizechecks && testcalls > simulatedopcount)
1082                 check_size();
1083         if (closeprob && (rv >> 3) < (1 << 28) / closeprob)
1084                 docloseopen();
1085 }
1086
1087
1088 void
1089 cleanup(sig)
1090         int     sig;
1091 {
1092         if (sig)
1093                 prt("signal %d\n", sig);
1094         prt("testcalls = %lu\n", testcalls);
1095         exit(sig);
1096 }
1097
1098
1099 void
1100 usage(void)
1101 {
1102         fprintf(stdout, "usage: %s",
1103                 "fsx [-dnqLOW] [-b opnum] [-c Prob] [-l flen] [-m "
1104 "start:end] [-o oplen] [-p progressinterval] [-r readbdy] [-s style] [-t "
1105 "truncbdy] [-w writebdy] [-D startingop] [-N numops] [-P dirpath] [-S seed] "
1106 "[ -I random|rotate ] fname [additional paths to fname..]\n"
1107 "       -b opnum: beginning operation number (default 1)\n"
1108 "       -c P: 1 in P chance of file close+open at each op (default infinity)\n"
1109 "       -d: debug output for all operations [-d -d = more debugging]\n"
1110 "       -l flen: the upper bound on file size (default 262144)\n"
1111 "       -m startop:endop: monitor (print debug output) specified byte rang"
1112 "(default 0:infinity)\n"
1113 "       -n: no verifications of file size\n"
1114 "       -o oplen: the upper bound on operation size (default 65536)\n"
1115 "       -p progressinterval: debug output at specified operation interval\n"
1116 "       -q: quieter operation\n"
1117 "       -r readbdy: 4096 would make reads page aligned (default 1)\n"
1118 "       -s style: 1 gives smaller truncates (default 0)\n"
1119 "       -t truncbdy: 4096 would make truncates page aligned (default 1)\n"
1120 "       -w writebdy: 4096 would make writes page aligned (default 1)\n"
1121 "       -D startingop: debug output starting at specified operation\n"
1122 "       -L: fsxLite - no file creations & no file size changes\n"
1123 "       -N numops: total # operations to do (default infinity)\n"
1124 "       -O: use oplen (see -o flag) for every op (default random)\n"
1125 "       -P: save .fsxlog and .fsxgood files in dirpath (default ./)\n"
1126 "       -S seed: for random # generator (default 1) 0 gets timestamp\n"
1127 "       -W: mapped write operations DISabled\n"
1128 "        -R: read() system calls only (mapped reads disabled)\n"
1129 "       -I: When multiple paths to the file are given each operation uses"
1130 "           a different path.  Iterate through them in order with 'rotate'"
1131 "           or chose then at 'random'.  (defaults to random)\n"
1132 "       fname: this filename is REQUIRED (no default)\n");
1133         exit(90);
1134 }
1135
1136
1137 int
1138 getnum(char *s, char **e)
1139 {
1140         int ret = -1;
1141
1142         *e = (char *) 0;
1143         ret = strtol(s, e, 0);
1144         if (*e)
1145                 switch (**e) {
1146                 case 'b':
1147                 case 'B':
1148                         ret *= 512;
1149                         *e = *e + 1;
1150                         break;
1151                 case 'k':
1152                 case 'K':
1153                         ret *= 1024;
1154                         *e = *e + 1;
1155                         break;
1156                 case 'm':
1157                 case 'M':
1158                         ret *= 1024*1024;
1159                         *e = *e + 1;
1160                         break;
1161                 case 'w':
1162                 case 'W':
1163                         ret *= 4;
1164                         *e = *e + 1;
1165                         break;
1166                 }
1167         return (ret);
1168 }
1169
1170 int
1171 main(int argc, char **argv)
1172 {
1173         int     i, style, ch;
1174         char    *endp;
1175         int  dirpath = 0;
1176
1177         goodfile[0] = 0;
1178         logfile[0] = 0;
1179
1180         page_size = getpagesize();
1181         page_mask = page_size - 1;
1182
1183         setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */
1184
1185         while ((ch = getopt(argc, argv, 
1186                                 "b:c:dl:m:no:p:qr:s:t:w:D:I:LN:OP:RS:W"))
1187                != EOF)
1188                 switch (ch) {
1189                 case 'b':
1190                         simulatedopcount = getnum(optarg, &endp);
1191                         if (!quiet)
1192                                 fprintf(stdout, "Will begin at operation"
1193                                         "%ld\n",
1194                                         simulatedopcount);
1195                         if (simulatedopcount == 0)
1196                                 usage();
1197                         simulatedopcount -= 1;
1198                         break;
1199                 case 'c':
1200                         closeprob = getnum(optarg, &endp);
1201                         if (!quiet)
1202                                 fprintf(stdout,
1203                                         "Chance of close/open is 1 in %d\n",
1204                                         closeprob);
1205                         if (closeprob <= 0)
1206                                 usage();
1207                         break;
1208                 case 'd':
1209                         debug++;
1210                         break;
1211                 case 'l':
1212                         maxfilelen = getnum(optarg, &endp);
1213                         if (maxfilelen <= 0)
1214                                 usage();
1215                         break;
1216                 case 'm':
1217                         monitorstart = getnum(optarg, &endp);
1218                         if (monitorstart < 0)
1219                                 usage();
1220                         if (!endp || *endp++ != ':')
1221                                 usage();
1222                         monitorend = getnum(endp, &endp);
1223                         if (monitorend < 0)
1224                                 usage();
1225                         if (monitorend == 0)
1226                                 monitorend = -1; /* aka infinity */
1227                         debug = 1;
1228                 case 'n':
1229                         sizechecks = 0;
1230                         break;
1231                 case 'o':
1232                         maxoplen = getnum(optarg, &endp);
1233                         if (maxoplen <= 0)
1234                                 usage();
1235                         break;
1236                 case 'p':
1237                         progressinterval = getnum(optarg, &endp);
1238                         if (progressinterval < 0)
1239                                 usage();
1240                         break;
1241                 case 'q':
1242                         quiet = 1;
1243                         break;
1244                 case 'r':
1245                         readbdy = getnum(optarg, &endp);
1246                         if (readbdy <= 0)
1247                                 usage();
1248                         break;
1249                 case 's':
1250                         style = getnum(optarg, &endp);
1251                         if (style < 0 || style > 1)
1252                                 usage();
1253                         break;
1254                 case 't':
1255                         truncbdy = getnum(optarg, &endp);
1256                         if (truncbdy <= 0)
1257                                 usage();
1258                         break;
1259                 case 'w':
1260                         writebdy = getnum(optarg, &endp);
1261                         if (writebdy <= 0)
1262                                 usage();
1263                         break;
1264                 case 'D':
1265                         debugstart = getnum(optarg, &endp);
1266                         if (debugstart < 1)
1267                                 usage();
1268                         break;
1269                 case 'I':
1270                         assign_fd_policy(optarg);
1271                         break;
1272                 case 'L':
1273                         lite = 1;
1274                         break;
1275                 case 'N':
1276                         numops = getnum(optarg, &endp);
1277                         if (numops < 0)
1278                                 usage();
1279                         break;
1280                 case 'O':
1281                         randomoplen = 0;
1282                         break;
1283                 case 'P':
1284                         strncpy(goodfile, optarg, sizeof(goodfile));
1285                         strcat(goodfile, "/");
1286                         strncpy(logfile, optarg, sizeof(logfile));
1287                         strcat(logfile, "/");
1288                         dirpath = 1;
1289                         break;
1290                 case 'R':
1291                         mapped_reads = 0;
1292                         break;
1293                 case 'S':
1294                         seed = getnum(optarg, &endp);
1295                         if (seed == 0)
1296                                 seed = time(0) % 10000;
1297                         if (!quiet)
1298                                 fprintf(stdout, "Seed set to %d\n", seed);
1299                         if (seed < 0)
1300                                 usage();
1301                         break;
1302                 case 'W':
1303                         mapped_writes = 0;
1304                         if (!quiet)
1305                                 fprintf(stdout, "mapped writes DISABLED\n");
1306                         break;
1307
1308                 default:
1309                         usage();
1310                         /* NOTREACHED */
1311                 }
1312         argc -= optind;
1313         argv += optind;
1314         if (argc < 1)
1315                 usage();
1316         fname = argv[0];
1317
1318         signal(SIGHUP,  cleanup);
1319         signal(SIGINT,  cleanup);
1320         signal(SIGPIPE, cleanup);
1321         signal(SIGALRM, cleanup);
1322         signal(SIGTERM, cleanup);
1323         signal(SIGXCPU, cleanup);
1324         signal(SIGXFSZ, cleanup);
1325         signal(SIGVTALRM,       cleanup);
1326         signal(SIGUSR1, cleanup);
1327         signal(SIGUSR2, cleanup);
1328
1329         initstate(seed, state, 256);
1330         setstate(state);
1331
1332         open_test_files(argv, argc);
1333
1334         strncat(goodfile, dirpath ? basename(fname) : fname, 256);
1335         strcat (goodfile, ".fsxgood");
1336         fsxgoodfd = open(goodfile, O_RDWR|O_CREAT|O_TRUNC, 0666);
1337         if (fsxgoodfd < 0) {
1338                 prterr(goodfile);
1339                 exit(92);
1340         }
1341         strncat(logfile, dirpath ? basename(fname) : fname, 256);
1342         strcat (logfile, ".fsxlog");
1343         fsxlogf = fopen(logfile, "w");
1344         if (fsxlogf == NULL) {
1345                 prterr(logfile);
1346                 exit(93);
1347         }
1348         if (lite) {
1349                 off_t ret;
1350                 int fd = get_fd();
1351                 file_size = maxfilelen = lseek(fd, (off_t)0, SEEK_END);
1352                 if (file_size == (off_t)-1) {
1353                         prterr(fname);
1354                         warn("main: lseek eof");
1355                         exit(94);
1356                 }
1357                 ret = lseek(fd, (off_t)0, SEEK_SET);
1358                 if (ret == (off_t)-1) {
1359                         prterr(fname);
1360                         warn("main: lseek 0");
1361                         exit(95);
1362                 }
1363         }
1364         original_buf = (char *) malloc(maxfilelen);
1365         for (i = 0; i < maxfilelen; i++)
1366                 original_buf[i] = random() % 256;
1367         good_buf = (char *) malloc(maxfilelen);
1368         memset(good_buf, '\0', maxfilelen);
1369         temp_buf = (char *) malloc(maxoplen);
1370         memset(temp_buf, '\0', maxoplen);
1371         if (lite) {     /* zero entire existing file */
1372                 ssize_t written;
1373                 int fd = get_fd();
1374
1375                 written = write(fd, good_buf, (size_t)maxfilelen);
1376                 if (written != maxfilelen) {
1377                         if (written == -1) {
1378                                 prterr(fname);
1379                                 warn("main: error on write");
1380                         } else
1381                                 warn("main: short write, 0x%x bytes instead"
1382                                         "of 0x%x\n",
1383                                      (unsigned)written, maxfilelen);
1384                         exit(98);
1385                 }
1386         } else
1387                 check_trunc_hack();
1388
1389         while (numops == -1 || numops--)
1390                 test();
1391
1392         close_test_files();
1393         prt("All operations completed A-OK!\n");
1394
1395         exit(0);
1396         return 0;
1397 }