It came up in a meeting today at Apple just how fragile the BSD NFS
implementation was before significant work was put in to stabilizing it,
and in that discussion came up a little test tool written originally by
Avie Tevanian and subsequently improved by one of the folks here.

This tool basically tries to do everything it can (legally) to confuse an
NFS server.  It seeks around, does I/O to and truncates/changes the size
of a test file, all while doing everything it can to detect data corruption
or other signs of misbehavior which might result from out-of-order replies
or any other previously-observed NFS pathology.  Very few NFS implementations
apparently survive this test and FreeBSD's is no exception.  The sources are
provided below, courtesy of Avie, for the education and enjoyment(?) of
anyone who's motivated to play with (or even pretends to understand) NFS.

Usage:
        cc fsx.c -o fsx
        ./fsx /some/nfs/mounted/scratchfile
        [ ** kaboom! ** ]

I'm also trying to determine which of the fixes Apple has made to NFS might
be adapted to FreeBSD, something which is made more difficult by the fact
that much of the code was taken straight from 4.4 Lite some time back and
both operating systems have diverged significantly since then.  Anyone
really keen on investigating this further themselves on it can also go to
http://www.opensource.apple.com/projects/darwin and register themselves
online (it's easy) to access the Darwin CVS repository, the module in
question being "xnu" (the Darwin kernel).  Thanks.

- Jordan

/*
 *      Copyright (C) 1991, NeXT Computer, Inc.  All Rights Reserverd.
 *
 *      File:   fsx.c
 *      Author: Avadis Tevanian, Jr.
 *
 *      File system exerciser. 
 *
 *      Rewritten 8/98 by Conrad Minshall.
 */

#include <sys/types.h>
#include <sys/stat.h>
#ifdef _UWIN
# include <sys/param.h>
# include <limits.h>
# include <time.h>
# include <strings.h>
# define MAP_FILE 0
#else
# include <sys/dirent.h>
#endif
#include <sys/file.h>
#include <sys/mman.h>
#include <limits.h>
#include <err.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
#include <errno.h>

#define NUMPRINTCOLUMNS 32      /* # columns of data to print on each line */

/*
 *      A log entry is an operation and a bunch of arguments.
 */

struct log_entry {
        int     operation;
        int     args[3];
};

#define LOGSIZE 1000

struct log_entry        oplog[LOGSIZE]; /* the log */
int                     logptr = 0;     /* current position in log */
int                     logcount = 0;   /* total ops */

/*
 *      Define operations
 */

#define OP_READ         1
#define OP_WRITE        2
#define OP_TRUNCATE     3
#define OP_CLOSEOPEN    4
#define OP_MAPREAD      5
#define OP_MAPWRITE     6
#define OP_SKIPPED      7

#ifndef PAGE_SIZE
#define PAGE_SIZE       4096
#endif
#define PAGE_MASK       (PAGE_SIZE - 1)

char    *original_buf;                  /* a pointer to the original data */
char    *good_buf;                      /* a pointer to the correct data */
char    *temp_buf;                      /* a pointer to the current data */
char    *fname;                         /* name of our test file */
int     fd;                             /* fd for our test file */

off_t           file_size = 0;
off_t           biggest = 0;
char            state[256];
unsigned long   testcalls = 0;          /* calls to function "test" */

unsigned long   simulatedopcount = 0;   /* -b flag */
int     closeprob = 0;                  /* -c flag */
int     debug = 0;                      /* -d flag */
unsigned long   debugstart = 0;         /* -D flag */
unsigned long   maxfilelen = 256 * 1024;        /* -l flag */
int     sizechecks = 1;                 /* -n flag disables them */
int     maxoplen = 64 * 1024;           /* -o flag */
int     quiet = 0;                      /* -q flag */
unsigned long progressinterval = 0;     /* -p flag */
int     readbdy = 1;                    /* -r flag */
int     style = 0;                      /* -s flag */
int     truncbdy = 1;                   /* -t flag */
int     writebdy = 1;                   /* -w flag */
long    monitorstart = -1;              /* -m flag */
long    monitorend = -1;                /* -m flag */
int     lite = 0;                       /* -L flag */
long    numops = -1;                    /* -N flag */
int     randomoplen = 1;                /* -O flag disables it */
int     seed = 1;                       /* -S flag */
int     mapped_writes = 1;              /* -W flag disables */
int     mapped_reads = 1;               /* -R flag disables it */
int     fsxgoodfd = 0;
FILE *  fsxlogf = NULL;
int badoff = -1;
int closeopen = 0;


void
prt(char *fmt, ...)
{
        va_list args;

        va_start(args, fmt);
        vfprintf(stdout, fmt, args);
        if (fsxlogf)
                vfprintf(fsxlogf, fmt, args);
        va_end(args);
}

void
prterr(char *prefix)
{
        prt("%s%s%s\n", prefix, prefix ? ": " : "", strerror(errno));
}


void
log4(int operation, int arg0, int arg1, int arg2)
{
        struct log_entry *le;

        le = &oplog[logptr];
        le->operation = operation;
        if (closeopen)
                le->operation = ~ le->operation;
        le->args[0] = arg0;
        le->args[1] = arg1;
        le->args[2] = arg2;
        logptr++;
        logcount++;
        if (logptr >= LOGSIZE)
                logptr = 0;
}


void
logdump(void)
{
        int     i, count, down;
        struct log_entry        *lp;

        prt("LOG DUMP (%d total operations):\n", logcount);
        if (logcount < LOGSIZE) {
                i = 0;
                count = logcount;
        } else {
                i = logptr;
                count = LOGSIZE;
        }
        for ( ; count > 0; count--) {
                int opnum;

                opnum = i+1 + (logcount/LOGSIZE)*LOGSIZE;
                prt("%d(%d mod 256): ", opnum, opnum%256);
                lp = &oplog[i];
                if ((closeopen = lp->operation < 0))
                        lp->operation = ~ lp->operation;
                        
                switch (lp->operation) {
                case OP_MAPREAD:
                        prt("MAPREAD\t0x%x thru 0x%x\t(0x%x bytes)",
                            lp->args[0], lp->args[0] + lp->args[1] - 1,
                            lp->args[1]);
                        if (badoff >= lp->args[0] && badoff <
                                                     lp->args[0] + lp->args[1])
                                prt("\t***RRRR***");
                        break;
                case OP_MAPWRITE:
                        prt("MAPWRITE 0x%x thru 0x%x\t(0x%x bytes)",
                            lp->args[0], lp->args[0] + lp->args[1] - 1,
                            lp->args[1]);
                        if (badoff >= lp->args[0] && badoff <
                                                     lp->args[0] + lp->args[1])
                                prt("\t******WWWW");
                        break;
                case OP_READ:
                        prt("READ\t0x%x thru 0x%x\t(0x%x bytes)",
                            lp->args[0], lp->args[0] + lp->args[1] - 1,
                            lp->args[1]);
                        if (badoff >= lp->args[0] &&
                            badoff < lp->args[0] + lp->args[1])
                                prt("\t***RRRR***");
                        break;
                case OP_WRITE:
                        prt("WRITE\t0x%x thru 0x%x\t(0x%x bytes)",
                            lp->args[0], lp->args[0] + lp->args[1] - 1,
                            lp->args[1]);
                        if (lp->args[0] > lp->args[2])
                                prt(" HOLE");
                        else if (lp->args[0] + lp->args[1] > lp->args[2])
                                prt(" EXTEND");
                        if ((badoff >= lp->args[0] || badoff >=lp->args[2]) &&
                            badoff < lp->args[0] + lp->args[1])
                                prt("\t***WWWW");
                        break;
                case OP_TRUNCATE:
                        down = lp->args[0] < lp->args[1];
                        prt("TRUNCATE %s\tfrom 0x%x to 0x%x",
                            down ? "DOWN" : "UP", lp->args[1], lp->args[0]);
                        if (badoff >= lp->args[!down] &&
                            badoff < lp->args[!!down])
                                prt("\t******WWWW");
                        break;
                case OP_SKIPPED:
                        prt("SKIPPED (no operation)");
                        break;
                default:
                        prt("BOGUS LOG ENTRY (operation code = %d)!",
                            lp->operation);
                }
                if (closeopen)
                        prt("\n\t\tCLOSE/OPEN");
                prt("\n");
                i++;
                if (i == LOGSIZE)
                        i = 0;
        }
}


void
save_buffer(char *buffer, off_t bufferlength, int fd)
{
        off_t ret;
        ssize_t byteswritten;

        if (fd <= 0 || bufferlength == 0)
                return;

        if (bufferlength > SSIZE_MAX) {
                prt("fsx flaw: overflow in save_buffer\n");
                exit(67);
        }
        if (lite) {
                off_t size_by_seek = lseek(fd, (off_t)0, L_XTND);
                if (size_by_seek == (off_t)-1)
                        prterr("save_buffer: lseek eof");
                else if (bufferlength > size_by_seek) {
                        warn("save_buffer: .fsxgood file too short... will save 0x%qx 
bytes instead of 0x%qx\n", (unsigned long long)size_by_seek,
                             (unsigned long long)bufferlength);
                        bufferlength = size_by_seek;
                }
        }

        ret = lseek(fd, (off_t)0, SEEK_SET);
        if (ret == (off_t)-1)
                prterr("save_buffer: lseek 0");
        
        byteswritten = write(fd, buffer, (size_t)bufferlength);
        if (byteswritten != bufferlength) {
                if (byteswritten == -1)
                        prterr("save_buffer write");
                else
                        warn("save_buffer: short write, 0x%x bytes instead of 0x%qx\n",
                             (unsigned)byteswritten,
                             (unsigned long long)bufferlength);
        }
}


void
report_failure(int status)
{
        logdump();
        
        if (fsxgoodfd) {
                if (good_buf) {
                        save_buffer(good_buf, file_size, fsxgoodfd);
                        prt("Correct content saved for comparison\n");
                        prt("(maybe hexdump \"%s\" vs \"%s.fsxgood\")\n",
                            fname, fname);
                }
                close(fsxgoodfd);
        }
        exit(status);
}


#define short_at(cp) ((unsigned short)((*((unsigned char *)(cp)) << 8) | \
                                        *(((unsigned char *)(cp)) + 1)))

void
check_buffers(unsigned offset, unsigned size)
{
        unsigned char c, t;
        unsigned i = 0;
        unsigned n = 0;
        unsigned op = 0;
        unsigned bad = 0;

        if (bcmp(good_buf + offset, temp_buf, size) != 0) {
                prt("READ BAD DATA: offset = 0x%x, size = 0x%x\n",
                    offset, size);
                prt("OFFSET\tGOOD\tBAD\tRANGE\n");
                while (size > 0) {
                        c = good_buf[offset];
                        t = temp_buf[i];
                        if (c != t) {
                                if (n == 0) {
                                        bad = short_at(&temp_buf[i]);
                                        prt("0x%5x\t0x%04x\t0x%04x", offset,
                                            short_at(&good_buf[offset]), bad);
                                        op = temp_buf[offset & 1 ? i+1 : i];
                                }
                                n++;
                                badoff = offset;
                        }
                        offset++;
                        i++;
                        size--;
                }
                if (n) {
                        prt("\t0x%5x\n", n);
                        if (bad)
                                prt("operation# (mod 256) for the bad data may be 
%u\n", ((unsigned)op & 0xff));
                        else
                                prt("operation# (mod 256) for the bad data unknown, 
check HOLE and EXTEND ops\n");
                } else
                        prt("????????????????\n");
                report_failure(110);
        }
}


void
check_size(void)
{
        struct stat     statbuf;
        off_t   size_by_seek;

        if (fstat(fd, &statbuf)) {
                prterr("check_size: fstat");
                statbuf.st_size = -1;
        }
        size_by_seek = lseek(fd, (off_t)0, L_XTND);
        if (file_size != statbuf.st_size || file_size != size_by_seek) {
                prt("Size error: expected 0x%qx stat 0x%qx seek 0x%qx\n",
                    (unsigned long long)file_size,
                    (unsigned long long)statbuf.st_size,
                    (unsigned long long)size_by_seek);
                report_failure(120);
        }
}


void
check_trunc_hack(void)
{
        struct stat statbuf;

        ftruncate(fd, (off_t)0);
        ftruncate(fd, (off_t)100000);
        fstat(fd, &statbuf);
        if (statbuf.st_size != (off_t)100000) {
                prt("no extend on truncate! not posix!\n");
                exit(130);
        }
        ftruncate(fd, 0);
}


void
doread(unsigned offset, unsigned size)
{
        off_t ret;
        unsigned iret;

        offset -= offset % readbdy;
        if (size == 0) {
                if (!quiet && testcalls > simulatedopcount)
                        prt("skipping zero size read\n");
                log4(OP_SKIPPED, OP_READ, offset, size);
                return;
        }
        if (size + offset > file_size) {
                if (!quiet && testcalls > simulatedopcount)
                        prt("skipping seek/read past end of file\n");
                log4(OP_SKIPPED, OP_READ, offset, size);
                return;
        }

        log4(OP_READ, offset, size, 0);

        if (testcalls <= simulatedopcount)
                return;

        if (!quiet && (progressinterval && testcalls % progressinterval == 0 ||
                       debug &&
                       (monitorstart == -1 ||
                        offset + size > monitorstart &&
                        (monitorend == -1 || offset <= monitorend))))
                prt("%lu read\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
                    offset, offset + size - 1, size);
        ret = lseek(fd, (off_t)offset, SEEK_SET);
        if (ret == (off_t)-1) {
                prterr("doread: lseek");
                report_failure(140);
        }
        iret = read(fd, temp_buf, size);
        if (iret != size) {
                if (iret == -1)
                        prterr("doread: read");
                else
                        prt("short read: 0x%x bytes instead of 0x%x\n",
                            iret, size);
                report_failure(141);
        }
        check_buffers(offset, size);
}


void
domapread(unsigned offset, unsigned size)
{
        unsigned pg_offset;
        unsigned map_size;
        char    *p;

        offset -= offset % readbdy;
        if (size == 0) {
                if (!quiet && testcalls > simulatedopcount)
                        prt("skipping zero size read\n");
                log4(OP_SKIPPED, OP_MAPREAD, offset, size);
                return;
        }
        if (size + offset > file_size) {
                if (!quiet && testcalls > simulatedopcount)
                        prt("skipping seek/read past end of file\n");
                log4(OP_SKIPPED, OP_MAPREAD, offset, size);
                return;
        }

        log4(OP_MAPREAD, offset, size, 0);

        if (testcalls <= simulatedopcount)
                return;

        if (!quiet && (progressinterval && testcalls % progressinterval == 0 ||
                       debug &&
                       (monitorstart == -1 ||
                        offset + size > monitorstart &&
                        (monitorend == -1 || offset <= monitorend))))
                prt("%lu mapread\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
                    offset, offset + size - 1, size);

        pg_offset = offset & PAGE_MASK;
        map_size  = pg_offset + size;

        if ((p = (char *)mmap(0, map_size, PROT_READ, MAP_FILE, fd,
                              (off_t)(offset - pg_offset))) == (char *)-1) {
                prterr("domapread: mmap");
                report_failure(190);
        }
        memcpy(temp_buf, p + pg_offset, size);
        if (munmap(p, map_size) != 0) {
                prterr("domapread: munmap");
                report_failure(191);
        }

        check_buffers(offset, size);
}


void
gendata(char *original_buf, char *good_buf, unsigned offset, unsigned size)
{
        while (size--) {
                good_buf[offset] = testcalls % 256; 
                if (offset % 2)
                        good_buf[offset] += original_buf[offset];
                offset++;
        }
}


void
dowrite(unsigned offset, unsigned size)
{
        off_t ret;
        unsigned iret;

        offset -= offset % writebdy;
        if (size == 0) {
                if (!quiet && testcalls > simulatedopcount)
                        prt("skipping zero size write\n");
                log4(OP_SKIPPED, OP_WRITE, offset, size);
                return;
        }

        log4(OP_WRITE, offset, size, file_size);

        gendata(original_buf, good_buf, offset, size);
        if (file_size < offset + size) {
                if (file_size < offset)
                        bzero(good_buf + file_size, offset - file_size);
                file_size = offset + size;
                if (lite) {
                        warn("Lite file size bug in fsx!");
                        report_failure(149);
                }
        }

        if (testcalls <= simulatedopcount)
                return;

        if (!quiet && (progressinterval && testcalls % progressinterval == 0 ||
                       debug &&
                       (monitorstart == -1 ||
                        offset + size > monitorstart &&
                        (monitorend == -1 || offset <= monitorend))))
                prt("%lu write\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
                    offset, offset + size - 1, size);
        ret = lseek(fd, (off_t)offset, SEEK_SET);
        if (ret == (off_t)-1) {
                prterr("dowrite: lseek");
                report_failure(150);
        }
        iret = write(fd, good_buf + offset, size);
        if (iret != size) {
                if (iret == -1)
                        prterr("dowrite: write");
                else
                        prt("short write: 0x%x bytes instead of 0x%x\n",
                            iret, size);
                report_failure(151);
        }
}


void
domapwrite(unsigned offset, unsigned size)
{
        unsigned pg_offset;
        unsigned map_size;
        off_t    cur_filesize;
        char    *p;

        offset -= offset % writebdy;
        if (size == 0) {
                if (!quiet && testcalls > simulatedopcount)
                        prt("skipping zero size write\n");
                log4(OP_SKIPPED, OP_MAPWRITE, offset, size);
                return;
        }
        cur_filesize = file_size;

        log4(OP_MAPWRITE, offset, size, 0);

        gendata(original_buf, good_buf, offset, size);
        if (file_size < offset + size) {
                if (file_size < offset)
                        bzero(good_buf + file_size, offset - file_size);
                file_size = offset + size;
                if (lite) {
                        warn("Lite file size bug in fsx!");
                        report_failure(200);
                }
        }

        if (testcalls <= simulatedopcount)
                return;

        if (!quiet && (progressinterval && testcalls % progressinterval == 0 ||
                       debug &&
                       (monitorstart == -1 ||
                        offset + size > monitorstart &&
                        (monitorend == -1 || offset <= monitorend))))
                prt("%lu mapwrite\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls,
                    offset, offset + size - 1, size);

        if (file_size > cur_filesize) {
                if (ftruncate(fd, file_size) == -1) {
                        prterr("domapwrite: ftruncate");
                        exit(201);
                }
        }
        pg_offset = offset & PAGE_MASK;
        map_size  = pg_offset + size;

        if ((p = (char *)mmap(0, map_size, PROT_READ | PROT_WRITE,
                              MAP_FILE | MAP_SHARED, fd,
                              (off_t)(offset - pg_offset))) == (char *)-1) {
                prterr("domapwrite: mmap");
                report_failure(202);
        }
        memcpy(p + pg_offset, good_buf + offset, size);
        if (msync(p, map_size, 0) != 0) {
                prterr("domapwrite: msync");
                report_failure(203);
        }
        if (munmap(p, map_size) != 0) {
                prterr("domapwrite: munmap");
                report_failure(204);
        }
}


void
dotruncate(unsigned size)
{
        int oldsize = file_size;

        size -= size % truncbdy;
        if (size > biggest) {
                biggest = size;
                if (!quiet && testcalls > simulatedopcount)
                        prt("truncating to largest ever: 0x%x\n", size);
        }

        log4(OP_TRUNCATE, size, (unsigned)file_size, 0);

        if (size > file_size)
                bzero(good_buf + file_size, size - file_size);
        file_size = size;

        if (testcalls <= simulatedopcount)
                return;
        
        if (progressinterval && testcalls % progressinterval == 0 ||
            debug && (monitorstart == -1 || monitorend == -1 ||
                      size <= monitorend))
                prt("%lu trunc\tfrom 0x%x to 0x%x\n", testcalls, oldsize, size);
        if (ftruncate(fd, (off_t)size) == -1) {
                prt("ftruncate1: %x\n", size);
                prterr("dotruncate: ftruncate");
                report_failure(160);
        }
}


void
writefileimage()
{
        ssize_t iret;

        if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
                prterr("writefileimage: lseek");
                report_failure(171);
        }
        iret = write(fd, good_buf, file_size);
        if ((off_t)iret != file_size) {
                if (iret == -1)
                        prterr("writefileimage: write");
                else
                        prt("short write: 0x%x bytes instead of 0x%qx\n",
                            iret, (unsigned long long)file_size);
                report_failure(172);
        }
        if (lite ? 0 : ftruncate(fd, file_size) == -1) {
                prt("ftruncate2: %qx\n", (unsigned long long)file_size);
                prterr("writefileimage: ftruncate");
                report_failure(173);
        }
}


void
docloseopen(void)
{ 
        if (testcalls <= simulatedopcount)
                return;

        if (debug)
                prt("%lu close/open\n", testcalls);
        if (close(fd)) {
                prterr("docloseopen: close");
                report_failure(180);
        }
        fd = open(fname, O_RDWR, 0);
        if (fd < 0) {
                prterr("docloseopen: open");
                report_failure(181);
        }
}


void
test(void)
{
        unsigned long   offset;
        unsigned long   size = maxoplen;
        unsigned long   rv = random();
        unsigned long   op = rv % (3 + !lite + mapped_writes);

        /* turn off the map read if necessary */

        if (op == 2 && !mapped_reads)
            op = 0;

        if (simulatedopcount > 0 && testcalls == simulatedopcount)
                writefileimage();

        testcalls++;

        closeopen = (rv >> 3) < (1 << 28) / closeprob;

        if (debugstart > 0 && testcalls >= debugstart)
                debug = 1;

        if (!quiet && testcalls < simulatedopcount && testcalls % 100000 == 0)
                prt("%lu...\n", testcalls);

        /*
         * READ:        op = 0
         * WRITE:       op = 1
         * MAPREAD:     op = 2
         * TRUNCATE:    op = 3
         * MAPWRITE:    op = 3 or 4
         */
        if (lite ? 0 : op == 3 && (style & 1) == 0) /* vanilla truncate? */
                dotruncate(random() % maxfilelen);
        else {
                if (randomoplen)
                        size = random() % (maxoplen+1);
                if (lite ? 0 : op == 3)
                        dotruncate(size);
                else {
                        offset = random();
                        if (op == 1 || op == (lite ? 3 : 4)) {
                                offset %= maxfilelen;
                                if (offset + size > maxfilelen)
                                        size = maxfilelen - offset;
                                if (op != 1)
                                        domapwrite(offset, size);
                                else
                                        dowrite(offset, size);
                        } else {
                                if (file_size)
                                        offset %= file_size;
                                else
                                        offset = 0;
                                if (offset + size > file_size)
                                        size = file_size - offset;
                                if (op != 0)
                                        domapread(offset, size);
                                else
                                        doread(offset, size);
                        }
                }
        }
        if (sizechecks && testcalls > simulatedopcount)
                check_size();
        if (closeopen)
                docloseopen();
}


void
cleanup(sig)
        int     sig;
{
        if (sig)
                prt("signal %d\n", sig);
        prt("testcalls = %lu\n", testcalls);
        exit(sig);
}


void
usage(void)
{
        fprintf(stdout, "usage: %s",
                "fsx [-dnqLOW] [-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\
        -b opnum: beginning operation number (default 1)\n\
        -c P: 1 in P chance of file close+open at each op (default infinity)\n\
        -d: debug output for all operations\n\
        -l flen: the upper bound on file size (default 262144)\n\
        -m startop:endop: monitor (print debug output) specified byte range (default 
0:infinity)\n\
        -n: no verifications of file size\n\
        -o oplen: the upper bound on operation size (default 65536)\n\
        -p progressinterval: debug output at specified operation interval\n\
        -q: quieter operation\n\
        -r readbdy: 4096 would make reads page aligned (default 1)\n\
        -s style: 1 gives smaller truncates (default 0)\n\
        -t truncbdy: 4096 would make truncates page aligned (default 1)\n\
        -w writebdy: 4096 would make writes page aligned (default 1)\n\
        -D startingop: debug output starting at specified operation\n\
        -L: fsxLite - no file creations & no file size changes\n\
        -N numops: total # operations to do (default infinity)\n\
        -O: use oplen (see -o flag) for every op (default random)\n\
        -P: save .fsxlog and .fsxgood files in dirpath (default ./)\n\
        -S seed: for random # generator (default 1) 0 gets timestamp\n\
        -W: mapped write operations DISabled\n\
        -R: read() system calls only (mapped reads disabled)\n\
        fname: this filename is REQUIRED (no default)\n");
        exit(90);
}


int
getnum(char *s, char **e)
{
        int ret = -1;

        *e = (char *) 0;
        ret = strtol(s, e, 0);
        if (*e)
                switch (**e) {
                case 'b':
                case 'B':
                        ret *= 512;
                        *e = *e + 1;
                        break;
                case 'k':
                case 'K':
                        ret *= 1024;
                        *e = *e + 1;
                        break;
                case 'm':
                case 'M':
                        ret *= 1024*1024;
                        *e = *e + 1;
                        break;
                case 'w':
                case 'W':
                        ret *= 4;
                        *e = *e + 1;
                        break;
                }
        return (ret);
}


int
main(int argc, char **argv)
{
        int     i, style, ch;
        char    *endp;
        char goodfile[1024];
        char logfile[1024];

        goodfile[0] = 0;
        logfile[0] = 0;

        setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */

        while ((ch = getopt(argc, argv, "b:c:dl:m:no:p:qr:s:t:w:D:LN:OP:RS:W"))
               != EOF)
                switch (ch) {
                case 'b':
                        simulatedopcount = getnum(optarg, &endp);
                        if (!quiet)
                                fprintf(stdout, "Will begin at operation %ld\n",
                                        simulatedopcount);
                        if (simulatedopcount == 0)
                                usage();
                        simulatedopcount -= 1;
                        break;
                case 'c':
                        closeprob = getnum(optarg, &endp);
                        if (!quiet)
                                fprintf(stdout,
                                        "Chance of close/open is 1 in %d\n",
                                        closeprob);
                        if (closeprob <= 0)
                                usage();
                        break;
                case 'd':
                        debug = 1;
                        break;
                case 'l':
                        maxfilelen = getnum(optarg, &endp);
                        if (maxfilelen <= 0)
                                usage();
                        break;
                case 'm':
                        monitorstart = getnum(optarg, &endp);
                        if (monitorstart < 0)
                                usage();
                        if (!endp || *endp++ != ':')
                                usage();
                        monitorend = getnum(endp, &endp);
                        if (monitorend < 0)
                                usage();
                        if (monitorend == 0)
                                monitorend = -1; /* aka infinity */
                        debug = 1;
                case 'n':
                        sizechecks = 0;
                        break;
                case 'o':
                        maxoplen = getnum(optarg, &endp);
                        if (maxoplen <= 0)
                                usage();
                        break;
                case 'p':
                        progressinterval = getnum(optarg, &endp);
                        if (progressinterval < 0)
                                usage();
                        break;
                case 'q':
                        quiet = 1;
                        break;
                case 'r':
                        readbdy = getnum(optarg, &endp);
                        if (readbdy <= 0)
                                usage();
                        break;
                case 's':
                        style = getnum(optarg, &endp);
                        if (style < 0 || style > 1)
                                usage();
                        break;
                case 't':
                        truncbdy = getnum(optarg, &endp);
                        if (truncbdy <= 0)
                                usage();
                        break;
                case 'w':
                        writebdy = getnum(optarg, &endp);
                        if (writebdy <= 0)
                                usage();
                        break;
                case 'D':
                        debugstart = getnum(optarg, &endp);
                        if (debugstart < 1)
                                usage();
                        break;
                case 'L':
                        lite = 1;
                        break;
                case 'N':
                        numops = getnum(optarg, &endp);
                        if (numops < 0)
                                usage();
                        break;
                case 'O':
                        randomoplen = 0;
                        break;
                case 'P':
                        strncpy(goodfile, optarg, sizeof(goodfile));
                        strcat(goodfile, "/");
                        strncpy(logfile, optarg, sizeof(logfile));
                        strcat(logfile, "/");
                        break;
                case 'R':
                        mapped_reads = 0;
                        break;
                case 'S':
                        seed = getnum(optarg, &endp);
                        if (seed == 0)
                                seed = time(0) % 10000;
                        if (!quiet)
                                fprintf(stdout, "Seed set to %d\n", seed);
                        if (seed < 0)
                                usage();
                        break;
                case 'W':
                        mapped_writes = 0;
                        if (!quiet)
                                fprintf(stdout, "mapped writes DISABLED\n");
                        break;
              
                default:
                        usage();
                        /* NOTREACHED */
                }
        argc -= optind;
        argv += optind;
        if (argc != 1)
                usage();
        fname = argv[0];

        signal(SIGHUP,  cleanup);
        signal(SIGINT,  cleanup);
        signal(SIGPIPE, cleanup);
        signal(SIGALRM, cleanup);
        signal(SIGTERM, cleanup);
        signal(SIGXCPU, cleanup);
        signal(SIGXFSZ, cleanup);
        signal(SIGVTALRM,       cleanup);
        signal(SIGUSR1, cleanup);
        signal(SIGUSR2, cleanup);

        initstate(seed, state, 256);
        setstate(state);
        fd = open(fname, O_RDWR|(lite ? 0 : O_CREAT|O_TRUNC), 0666);
        if (fd < 0) {
                prterr(fname);
                exit(91);
        }
        strncat(goodfile, fname, 256);
        strcat (goodfile, ".fsxgood");
        fsxgoodfd = open(goodfile, O_RDWR|O_CREAT|O_TRUNC, 0666);
        if (fsxgoodfd < 0) {
                prterr(goodfile);
                exit(92);
        }
        strncat(logfile, fname, 256);
        strcat (logfile, ".fsxlog");
        fsxlogf = fopen(logfile, "w");
        if (fsxlogf == NULL) {
                prterr(logfile);
                exit(93);
        }
        if (lite) {
                off_t ret;
                file_size = maxfilelen = lseek(fd, (off_t)0, L_XTND);
                if (file_size == (off_t)-1) {
                        prterr(fname);
                        warn("main: lseek eof");
                        exit(94);
                }
                ret = lseek(fd, (off_t)0, SEEK_SET);
                if (ret == (off_t)-1) {
                        prterr(fname);
                        warn("main: lseek 0");
                        exit(95);
                }
        }
        original_buf = (char *) malloc(maxfilelen);
        for (i = 0; i < maxfilelen; i++)
                original_buf[i] = random() % 256;
        good_buf = (char *) malloc(maxfilelen);
        bzero(good_buf, maxfilelen);
        temp_buf = (char *) malloc(maxoplen);
        bzero(temp_buf, maxoplen);
        if (lite) {     /* zero entire existing file */
                ssize_t written;

                written = write(fd, good_buf, (size_t)maxfilelen);
                if (written != maxfilelen) {
                        if (written == -1) {
                                prterr(fname);
                                warn("main: error on write");
                        } else
                                warn("main: short write, 0x%x bytes instead of 0x%x\n",
                                     (unsigned)written, maxfilelen);
                        exit(98);
                }
        } else 
                check_trunc_hack();

        while (numops == -1 || numops--)
                test();

        if (close(fd)) {
                prterr("close");
                report_failure(99);
        }
        prt("All operations completed A-OK!\n");

        exit(0);
        return 0;
}


To Unsubscribe: send mail to [EMAIL PROTECTED]
with "unsubscribe freebsd-hackers" in the body of the message

Reply via email to