Subject: [jkh@winston.freebsd.org: NFS: How to make FreeBSD fall on its face in one easy step]
To: None <tech-kern@netbsd.org>
From: void <float@firedrake.org>
List: tech-kern
Date: 12/13/2001 19:15:07
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 majordomo@FreeBSD.org
with "unsubscribe freebsd-hackers" in the body of the message
--bg08WKrSYDhXBjb5--