From owner-freebsd-hackers Thu Dec 13 8:59:49 2001 Delivered-To: freebsd-hackers@freebsd.org Received: from parhelion.firedrake.org (parhelion.firedrake.org [212.135.138.219]) by hub.freebsd.org (Postfix) with ESMTP id C70EC37B420 for ; Thu, 13 Dec 2001 08:58:39 -0800 (PST) Received: from float by parhelion.firedrake.org with local (Exim 3.32 #1 (Debian)) id 16EZBp-0006Fx-00; Thu, 13 Dec 2001 16:58:33 +0000 Date: Thu, 13 Dec 2001 16:58:33 +0000 To: Jordan Hubbard Cc: hackers@freebsd.org Subject: Re: NFS: How to make FreeBSD fall on its face in one easy step Message-ID: <20011213165833.A23824@parhelion.firedrake.org> Mail-Followup-To: Jordan Hubbard , hackers@freebsd.org References: <200112121623.fBCGNuY52543@winston.freebsd.org> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <200112121623.fBCGNuY52543@winston.freebsd.org> User-Agent: Mutt/1.3.23i From: void Sender: owner-freebsd-hackers@FreeBSD.ORG Precedence: bulk List-ID: List-Archive: (Web Archive) List-Help: (List Instructions) List-Subscribe: List-Unsubscribe: X-Loop: FreeBSD.ORG Has anyone brought this code to the attention of the NetBSD people? I imagine that they would be interested. If not, I will forward it along myself, as soon as I determine the appropriate list. (List recommendations from the dual citizens here are welcome.) On Wed, Dec 12, 2001 at 08:23:56AM -0800, Jordan Hubbard wrote: > 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 > #include > #ifdef _UWIN > # include > # include > # include > # include > # define MAP_FILE 0 > #else > # include > #endif > #include > #include > #include > #include > #include > #include > #include > #include > #include > #include > #include > > #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 -- Ben "An art scene of delight I created this to be ..." -- Sun Ra To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-hackers" in the body of the message