Subject: Re: [jkh@winston.freebsd.org: NFS: How to make FreeBSD fall on
To: void <float@firedrake.org>
From: Conrad Minshall <conrad@apple.com>
List: tech-kern
Date: 12/15/2001 11:06:30
The attached code also has a divide by zero bug. Please make this change
to your copy:
diff -u -d -b -w -r1.21 fsx.c
--- fsx.c 2001/12/11 23:27:20 1.21
+++ fsx.c 2001/12/15 15:31:19
@@ -701,6 +701,7 @@
testcalls++;
+ if (closeprob)
closeopen = (rv >> 3) < (1 << 28) / closeprob;
if (debugstart > 0 && testcalls >= debugstart)
At 11:15 AM -0800 12/13/01, void wrote:
>The attached code has exposed several bugs in FreeBSD's NFS client
>code, and at least one general filesystem bug, related to soft updates.
>The full details are in the freebsd-hackers archive.
>
>--
> Ben
>
>"An art scene of delight
> I created this to be ..." -- Sun Ra
>
>Return-path: <owner-freebsd-hackers@FreeBSD.ORG>
>Envelope-to: float@firedrake.org
>Received: from mx2.freebsd.org ([216.136.204.119])
> by parhelion.firedrake.org with esmtp (Exim 3.32 #1 (Debian))
> id 16ECCT-0001C6-00
> for <float@firedrake.org>; Wed, 12 Dec 2001 16:25:41 +0000
>Received: from hub.freebsd.org (hub.FreeBSD.org [216.136.204.18])
> by mx2.freebsd.org (Postfix) with ESMTP
> id 819F15545D; Wed, 12 Dec 2001 08:25:00 -0800 (PST)
> (envelope-from owner-freebsd-hackers@FreeBSD.ORG)
>Received: by hub.freebsd.org (Postfix, from userid 538)
> id D111137B405; Wed, 12 Dec 2001 08:24:57 -0800 (PST)
>Received: from localhost (localhost [127.0.0.1])
> by hub.freebsd.org (Postfix) with SMTP
> id A4F252E8013; Wed, 12 Dec 2001 08:24:57 -0800 (PST)
>Received: by hub.freebsd.org (bulk_mailer v1.12); Wed, 12 Dec 2001
>08:24:57 -0800
>Received: from winston.freebsd.org
>(adsl-64-173-15-98.dsl.sntc01.pacbell.net [64.173.15.98])
> by hub.freebsd.org (Postfix) with ESMTP id 303D537B417
> for <hackers@freebsd.org>; Wed, 12 Dec 2001 08:24:22 -0800 (PST)
>Received: (from jkh@localhost)
> by winston.freebsd.org (8.11.6/8.11.6) id fBCGNuY52543;
> Wed, 12 Dec 2001 08:23:56 -0800 (PST)
> (envelope-from jkh)
>Date: Wed, 12 Dec 2001 08:23:56 -0800 (PST)
>From: Jordan Hubbard <jkh@winston.freebsd.org>
>Message-Id: <200112121623.fBCGNuY52543@winston.freebsd.org>
>To: hackers@freebsd.org
>Subject: NFS: How to make FreeBSD fall on its face in one easy step
>Sender: owner-freebsd-hackers@FreeBSD.ORG
>List-ID: <freebsd-hackers.FreeBSD.ORG>
>List-Archive: <http://docs.freebsd.org/mail/> (Web Archive)
>List-Help: <mailto:majordomo@FreeBSD.ORG?subject=help> (List Instructions)
>List-Subscribe:
><mailto:majordomo@FreeBSD.ORG?subject=subscribe%20freebsd-hackers>
>List-Unsubscribe:
><mailto:majordomo@FreeBSD.ORG?subject=unsubscribe%20freebsd-hackers>
>X-Loop: FreeBSD.ORG
>Precedence: bulk
>
>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
--
Conrad Minshall, conrad@apple.com, 408 974-2749
Apple Computer, Mac OS X Core Operating Systems