Re: [Yaffs] Tests status

Top Page
Attachments:
Message as email
+ (text/plain)
+ fsx-test.gz (application/x-gzip)
+ fsx-test.fsxgood.gz (application/x-gzip)
+ fsx-test.fsxlog.gz (application/x-gzip)
+ fsx-linux.c (text/plain)
Delete this message
Reply to this message
Author: Luc Van Oostenryck
Date:  
To: Charles Manning
CC: yaffs
Subject: Re: [Yaffs] Tests status
Charles Manning wrote:
> On Monday 01 August 2005 11:04, Luc Van Oostenryck wrote:
>>- There is a problem related to truncation and rewrite of files but I
>> have not yet investigated this one.
>> May be related to not using USE_HEADER_SIZE ???
>
>
> If you can make a test case I will investigate further.
>
>


Yesterday, it's was 100% reproductible at the very beginning of the test,
but now it happen much later and not at the same place (no more just after a truncate).
I didn't had the time to really investigate it.

The test went good on nandemul2k (but the test must run much longer that I did)
but fail on nandsim.

The output of the program is:
    # fsx-linux -p -W -N 102400000 /mnt/nandsim


    ...
    139315(51 mod 256): READ        0x6e73 thru 0x9ce4      (0x2e72 bytes)
    139316(52 mod 256): READ        0x8e3b thru 0x9ce4      (0xeaa bytes)
    139317(53 mod 256): MAPREAD     0x9326 thru 0x9ce4      (0x9bf bytes)
    139318(54 mod 256): MAPWRITE 0x3552c thru 0x3ffff       (0xaad4 bytes)
    139319(55 mod 256): MAPWRITE 0x33797 thru 0x3ec5f       (0xb4c9 bytes)
    139320(56 mod 256): READ        0x1209 thru 0x34bd      (0x22b5 bytes)
    139321(57 mod 256): WRITE       0x27adf thru 0x2f666    (0x7b88 bytes)
    139322(58 mod 256): MAPREAD     0x9b04 thru 0xaa67      (0xf64 bytes)
    139323(59 mod 256): WRITE       0x14804 thru 0x1bc3b    (0x7438 bytes)
    139324(60 mod 256): WRITE       0xc842 thru 0x1bf0f     (0xf6ce bytes)
    139325(61 mod 256): MAPREAD     0x12f69 thru 0x1409f    (0x1137 bytes)
    139326(62 mod 256): MAPREAD     0x14c53 thru 0x1657a    (0x1928 bytes)
    139327(63 mod 256): MAPREAD     0x17f1f thru 0x22ebc    (0xaf9e bytes)  ***RRRR***
    Correct content saved for comparison
    (maybe hexdump "/mnt/nandsim/fsx-test" vs "/mnt/nandsim/fsx-test.fsxgood")



I put in attachement the two test files, the log and the test program.


Luc
/*
 * Copyright (C) 1991, NeXT Computer, Inc.  All Rights Reserverd.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 *
 *
 *    File:    fsx.c
 *    Author:    Avadis Tevanian, Jr.
 *
 *    File system exerciser. 
 *
 *    Rewritten 8/98 by Conrad Minshall.
 *
 *    Small changes to work under Linux -- davej.
 *
 */


#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       getpagesize()
#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 > INT_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) {
                           fprintf(stderr, "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
                           fprintf(stderr, "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_SHARED, 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) {
                           fprintf(stderr, "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) {
                           fprintf(stderr, "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++;


    if (closeprob)
        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);
                           fprintf(stderr, "main: lseek eof");
            exit(94);
        }
        ret = lseek(fd, (off_t)0, SEEK_SET);
        if (ret == (off_t)-1) {
            prterr(fname);
                           fprintf(stderr, "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);
                                    fprintf(stderr, "main: error on write");
            } else
                                    fprintf(stderr, "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;
}