Browse Source

Plan 9 from Bell Labs 2007-10-25

David du Colombier 16 years ago
parent
commit
00ae009773
8 changed files with 827 additions and 11 deletions
  1. 9 4
      dist/replica/_plan9.db
  2. 9 4
      dist/replica/plan9.db
  3. 13 0
      dist/replica/plan9.log
  4. 158 0
      sys/man/4/usbdisk
  5. 67 0
      sys/man/8/partfs
  6. 4 3
      sys/man/8/prep
  7. 1 0
      sys/src/cmd/disk/mkfile
  8. 566 0
      sys/src/cmd/disk/partfs.c

+ 9 - 4
dist/replica/_plan9.db

@@ -1,8 +1,8 @@
 386 - 20000000775 sys sys 1010957353 0
 386/9load - 775 sys sys 1191886962 330276
-386/9loaddebug - 775 sys sys 1191962479 435156
+386/9loaddebug - 775 sys sys 1193198599 439897
 386/9loadlite - 775 sys sys 1191886972 153588
-386/9loadlitedebug - 775 sys sys 1191962483 220684
+386/9loadlitedebug - 775 sys sys 1193198603 225425
 386/9pc - 775 sys sys 1189053434 2038499
 386/9pc.gz - 664 sys sys 1189053435 865376
 386/9pccpu - 775 sys sys 1189053453 1705749
@@ -229,7 +229,7 @@
 386/bin/faces - 775 sys sys 1181507266 193265
 386/bin/factor - 775 sys sys 1168402307 61699
 386/bin/fcp - 775 sys sys 1168402307 82433
-386/bin/file - 775 sys sys 1191528973 128646
+386/bin/file - 775 sys sys 1193198589 128958
 386/bin/fmt - 775 sys sys 1168402307 65567
 386/bin/fortune - 775 sys sys 1168402308 67356
 386/bin/fossil - 20000000775 sys sys 1042005470 0
@@ -481,6 +481,7 @@
 386/bin/upas/unspam - 775 sys sys 1064598367 38
 386/bin/upas/vf - 775 sys sys 1181704900 97444
 386/bin/usb - 20000000775 sys sys 1019538890 0
+386/bin/usb/disk - 775 sys sys 1193197083 186944
 386/bin/usb/usbaudio - 775 sys sys 1184731243 188000
 386/bin/usb/usbd - 775 sys sys 1184731243 130757
 386/bin/usb/usbmouse - 775 sys sys 1184731244 109661
@@ -7738,6 +7739,7 @@ sys/man/4/u9fs - 664 sys sys 1043769139 4748
 sys/man/4/upasfs - 664 sys sys 1034348505 6212
 sys/man/4/usb - 664 sys sys 1172764045 4682
 sys/man/4/usbd - 664 sys sys 1164149441 712
+sys/man/4/usbdisk - 664 sys sys 1193264908 2800
 sys/man/4/vacfs - 664 sys sys 1084333062 1545
 sys/man/4/webcookies - 664 sys sys 1019828742 3525
 sys/man/4/webfs - 664 sys sys 1124711926 6518
@@ -7840,6 +7842,7 @@ sys/man/8/na - 664 sys sys 958527089 859
 sys/man/8/ndb - 664 sys sys 1191867910 14502
 sys/man/8/newuser - 664 sys sys 1116954242 2418
 sys/man/8/nfsserver - 664 sys sys 1191524525 3576
+sys/man/8/partfs - 664 sys sys 1193264932 851
 sys/man/8/pcmcia - 664 sys sys 944959679 408
 sys/man/8/pem - 664 sys sys 1060263669 1189
 sys/man/8/ping - 664 sys sys 1169105315 3650
@@ -10234,8 +10237,9 @@ sys/src/cmd/disk/kfs/uid.c - 664 sys sys 1143759347 6779
 sys/src/cmd/disk/kfscmd.c - 664 sys sys 1015009135 1109
 sys/src/cmd/disk/mbr.c - 664 sys sys 1080218150 4325
 sys/src/cmd/disk/mkext.c - 664 sys sys 1166823931 5805
-sys/src/cmd/disk/mkfile - 664 sys sys 1022385851 500
+sys/src/cmd/disk/mkfile - 664 sys sys 1193264991 509
 sys/src/cmd/disk/mkfs.c - 664 sys sys 1108000852 14697
+sys/src/cmd/disk/partfs.c - 664 sys sys 1193279294 9714
 sys/src/cmd/disk/prep - 20000000775 sys sys 1055692957 0
 sys/src/cmd/disk/prep/calc.y - 664 sys sys 1135487935 2406
 sys/src/cmd/disk/prep/edit.c - 664 sys sys 1017854327 9714
@@ -15739,3 +15743,4 @@ usr/glenda/lib/profile - 664 glenda glenda 1105128663 890
 usr/glenda/readme.acme - 664 glenda glenda 1019860628 4753
 usr/glenda/readme.rio - 664 glenda glenda 1019860628 6370
 usr/glenda/tmp - 20000000775 glenda glenda 1018802620 0
+386/bin/disk/partfs - 775 sys sys 1193282249 149872

+ 9 - 4
dist/replica/plan9.db

@@ -1,8 +1,8 @@
 386 - 20000000775 sys sys 1010957353 0
 386/9load - 775 sys sys 1191886962 330276
-386/9loaddebug - 775 sys sys 1191962479 435156
+386/9loaddebug - 775 sys sys 1193198599 439897
 386/9loadlite - 775 sys sys 1191886972 153588
-386/9loadlitedebug - 775 sys sys 1191962483 220684
+386/9loadlitedebug - 775 sys sys 1193198603 225425
 386/9pc - 775 sys sys 1189053434 2038499
 386/9pc.gz - 664 sys sys 1189053435 865376
 386/9pccpu - 775 sys sys 1189053453 1705749
@@ -215,6 +215,7 @@
 386/bin/disk/mkext - 775 sys sys 1168402303 82886
 386/bin/disk/mkfs - 775 sys sys 1168402303 87969
 386/bin/disk/mksacfs - 775 sys sys 1020319074 71451
+386/bin/disk/partfs - 775 sys sys 1193282249 149872
 386/bin/disk/prep - 775 sys sys 1168402303 96167
 386/bin/disk/sacfs - 775 sys sys 1020319075 79882
 386/bin/dossrv - 775 sys sys 1178568267 136537
@@ -229,7 +230,7 @@
 386/bin/faces - 775 sys sys 1181507266 193265
 386/bin/factor - 775 sys sys 1168402307 61699
 386/bin/fcp - 775 sys sys 1168402307 82433
-386/bin/file - 775 sys sys 1191528973 128646
+386/bin/file - 775 sys sys 1193198589 128958
 386/bin/fmt - 775 sys sys 1168402307 65567
 386/bin/fortune - 775 sys sys 1168402308 67356
 386/bin/fossil - 20000000775 sys sys 1042005470 0
@@ -481,6 +482,7 @@
 386/bin/upas/unspam - 775 sys sys 1064598367 38
 386/bin/upas/vf - 775 sys sys 1181704900 97444
 386/bin/usb - 20000000775 sys sys 1019538890 0
+386/bin/usb/disk - 775 sys sys 1193197083 186944
 386/bin/usb/usbaudio - 775 sys sys 1184731243 188000
 386/bin/usb/usbd - 775 sys sys 1184731243 130757
 386/bin/usb/usbmouse - 775 sys sys 1184731244 109661
@@ -7738,6 +7740,7 @@ sys/man/4/u9fs - 664 sys sys 1043769139 4748
 sys/man/4/upasfs - 664 sys sys 1034348505 6212
 sys/man/4/usb - 664 sys sys 1172764045 4682
 sys/man/4/usbd - 664 sys sys 1164149441 712
+sys/man/4/usbdisk - 664 sys sys 1193264908 2800
 sys/man/4/vacfs - 664 sys sys 1084333062 1545
 sys/man/4/webcookies - 664 sys sys 1019828742 3525
 sys/man/4/webfs - 664 sys sys 1124711926 6518
@@ -7840,6 +7843,7 @@ sys/man/8/na - 664 sys sys 958527089 859
 sys/man/8/ndb - 664 sys sys 1191867910 14502
 sys/man/8/newuser - 664 sys sys 1116954242 2418
 sys/man/8/nfsserver - 664 sys sys 1191524525 3576
+sys/man/8/partfs - 664 sys sys 1193264932 851
 sys/man/8/pcmcia - 664 sys sys 944959679 408
 sys/man/8/pem - 664 sys sys 1060263669 1189
 sys/man/8/ping - 664 sys sys 1169105315 3650
@@ -10234,8 +10238,9 @@ sys/src/cmd/disk/kfs/uid.c - 664 sys sys 1143759347 6779
 sys/src/cmd/disk/kfscmd.c - 664 sys sys 1015009135 1109
 sys/src/cmd/disk/mbr.c - 664 sys sys 1080218150 4325
 sys/src/cmd/disk/mkext.c - 664 sys sys 1166823931 5805
-sys/src/cmd/disk/mkfile - 664 sys sys 1022385851 500
+sys/src/cmd/disk/mkfile - 664 sys sys 1193264991 509
 sys/src/cmd/disk/mkfs.c - 664 sys sys 1108000852 14697
+sys/src/cmd/disk/partfs.c - 664 sys sys 1193279294 9714
 sys/src/cmd/disk/prep - 20000000775 sys sys 1055692957 0
 sys/src/cmd/disk/prep/calc.y - 664 sys sys 1135487935 2406
 sys/src/cmd/disk/prep/edit.c - 664 sys sys 1017854327 9714

+ 13 - 0
dist/replica/plan9.log

@@ -53113,3 +53113,16 @@
 1193182203 3 a sys/src/cmd/usb/disk/mkfile - 664 sys sys 1193181450 359
 1193182203 4 c sys/src/cmd/usb/mkfile - 664 sys sys 1193181446 361
 1193182203 5 c sys/src/cmd/file.c - 664 sys sys 1193181267 28836
+1193198402 0 a 386/bin/usb/disk - 775 sys sys 1193197083 186944
+1193198402 1 c 386/bin/file - 775 sys sys 1193197079 128958
+1193200204 0 c 386/9loaddebug - 775 sys sys 1193198599 439897
+1193200204 1 c 386/9loadlitedebug - 775 sys sys 1193198603 225425
+1193200204 2 c 386/bin/file - 775 sys sys 1193198589 128958
+1193265004 0 a sys/man/4/usbdisk - 664 sys sys 1193264908 2800
+1193265004 1 a sys/man/8/partfs - 664 sys sys 1193264932 851
+1193265004 2 c sys/src/cmd/disk/mkfile - 664 sys sys 1193264991 509
+1193265004 3 a sys/src/cmd/disk/partfs.c - 664 sys sys 1193264961 9509
+1193268603 0 c sys/src/cmd/disk/partfs.c - 664 sys sys 1193268602 9594
+1193272204 0 c sys/src/cmd/disk/partfs.c - 664 sys sys 1193271505 9718
+1193279405 0 c sys/src/cmd/disk/partfs.c - 664 sys sys 1193279294 9714
+1193283004 0 a 386/bin/disk/partfs - 775 sys sys 1193282249 149872

+ 158 - 0
sys/man/4/usbdisk

@@ -0,0 +1,158 @@
+.TH USBDISK 4
+.SH NAME
+usbdisk, usbfat: - serve USB mass storage devices
+.SH SYNOPSIS
+.B usb/disk
+[
+.B -dD
+] [
+.B -m
+.I mountpoint
+] [
+.B -s
+.I srvname
+] [
+.I ctrlno
+.I id
+]
+.LP
+.B usbfat:
+[
+.B -f
+] [
+.I disk
+[
+.I mntpt
+] ]
+.SH DESCRIPTION
+.I Disk
+serves a directory named
+.I mountpoint
+(default
+.BR /n/disk )
+containing the files
+.B ctl
+and
+.IR n ,
+where
+.I n
+is the number of a LUN to access,
+for all LUNs of the named or found USB device.
+.I Disk
+searches for a USB device with class 8 (storage),
+subclass 2 (ATAPI CD/DVD),
+5 (removable, ATAPI-like) or
+6 (SCSI transparent),
+and
+protocol 0x50 (bulk), or uses
+.BI /dev/usb/ ctrlno / id
+if specified.
+In every LUN directory are the files
+.B data
+and
+.BR raw .
+.B data
+gives read/write access to blocks of the device.
+.B raw
+is a raw SCSI-like interface (talk to it with
+.IR scuzz (8)).
+.PP
+The root directory's
+.B ctl
+file accepts the command
+.LR reset ,
+which tells
+.I disk
+to again fetch the information about all LUNs,
+and yields the device geometries when read.
+.PP
+The
+.B -d
+option generates debugging output.
+.\" The
+.\" .B -f
+.\" option avoids freaking out some cheap USB flash disks by not issuing
+.\" SCSI reset commands to them.
+The
+.B -m
+option mounts the served hierarchy on
+.I mountpoint
+instead of
+.BR /n/disk .
+The
+.B -s
+option posts a mountable file descriptor as
+.BI /srv/ srvname.
+The
+.B -D
+option prints all 9P messages.
+.SH EXAMPLES
+Access the usual preformatted FAT partition:
+.IP
+.EX
+% usb/usbd
+% usb/disk
+% disk/fdisk -p /n/disk/0/data
+part dos 7 31559
+% dossrv -f /n/disk/0/data:7 usbstorage
+dossrv: serving #s/usbstorage
+% mount /srv/usbstorage /n/d:
+% ls -l /n/d:
+alrw-rw-rw- M 39 bill trog 180364 Oct  5 18:14 /n/d:/9LOAD
+d-rwxrwxrwx M 39 bill trog      0 Nov 13 14:30 /n/d:/benedict
+.EE
+.LP
+The latter part of the above example, from
+.B disk/fdisk
+to
+.BR mount ,
+has been packaged up as
+.IR usbfat: ,
+which mounts the FAT file system in the DOS partition of the named
+.I disk
+(by default
+.BR /n/disk/0/data )
+on
+.I mntpt
+(by default
+.BR /n/usb ).
+.SH FILES
+.TF /srv/usbsfs.ctlr.id
+.TP
+.B /n/disk
+default mountpoint
+.TP
+.B /n/usb
+default mountpoint for FAT file system on the disk
+.TP
+.B /srv/usbfat.$user
+FAT file system service
+.SH SOURCE
+.B /sys/src/cmd/usb/disk
+.br
+.B /rc/bin/usbfat:
+.SH SEE ALSO
+.IR fs (3),
+.IR sd (3),
+.IR usb (3),
+.IR prep (8),
+.IR scuzz (8)
+.SH BUGS
+Should implement
+.IR sd (3)'s
+interface for each LUN.
+Currently,
+if there are multiple partitions, you can use
+.IR fs (3)
+to manage them.
+If
+.L "fdisk -p"
+prints nothing,
+there is no partition table on the device,
+so one can invoke
+.IR dossrv (4)
+directly.
+.PP
+USB 1.0 flash disks I/O rates are slow and variable.
+Reading and writing in large units (e.g., 4KB) seems to be faster
+by an order of magnitude when transferring large volumes of data.

+ 67 - 0
sys/man/8/partfs

@@ -0,0 +1,67 @@
+.TH PARTFS 8
+.SH NAME
+partfs \- serve file, with partitions
+.SH SYNOPSIS
+.B disk/partfs
+[
+.B -r
+]
+[
+.B -f
+.I file
+]
+[
+.B -m
+.I mtpt
+]
+[
+.B -s
+.I srvname
+]
+[
+.I diskname
+]
+.SH DESCRIPTION
+.I Partfs
+presents
+.I file
+in the manner of
+.IR sd (3)
+on
+.IB mtpt / diskname
+(default
+.BR /dev/sdXX ).
+Changes made to the disk are written through to
+.I file
+unless the
+.B -r
+option is given.
+.PP
+When setting disk geometry with the
+.B geometry
+control message,
+the arguments are
+sectors and sector size.
+.PP
+The
+.B -s
+option causes
+.I partfs
+to post its 9P service at
+.BI /srv/ service \fR.
+.SH EXAMPLES
+Partition a USB flash device:
+.IP
+.EX
+usb/disk
+disk/partfs -f /n/disk/0/data
+disk/mbr /dev/sdXX/data
+disk/fdisk -baw /dev/sdXX/data
+disk/prep /dev/sdXX/plan9
+.EE
+.SH SOURCE
+.B /sys/src/cmd/disk/partfs.c
+.SH SEE ALSO
+.IR sd (3),
+.IR disksim (8),
+.IR prep (8)

+ 4 - 3
sys/man/8/prep

@@ -690,8 +690,8 @@ disk/format -b /386/pbslba -d -r 2 /dev/sdC0/9fat \e
 	/386/9load /386/9pcf /tmp/plan9.ini
 .EE
 .PP
-Create a bootable USB disk or flash-memory device to be loaded
-via the BIOS:
+Create a bootable USB disk or flash-memory device to be booted
+via the BIOS and with no partitions:
 .IP
 .EX
 usb/disk
@@ -705,7 +705,8 @@ disk/format -b /386/pbslba -df /n/disk/0/data \e
 .SH SEE ALSO
 .IR floppy (3),
 .IR sd (3),
-.IR 9load (8)
+.IR 9load (8),
+.IR partfs (8)
 .SH BUGS
 .I Format
 can create FAT12 and FAT16

+ 1 - 0
sys/src/cmd/disk/mkfile

@@ -6,6 +6,7 @@ TARG=exsort\
 	mbr\
 	mkext\
 	mkfs\
+	partfs\
 
 DIRS=\
 	9660\

+ 566 - 0
sys/src/cmd/disk/partfs.c

@@ -0,0 +1,566 @@
+/*
+ * partfs - serve an underlying file, with devsd-style partitions
+ */
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+
+typedef struct Part Part;
+struct Part
+{
+	int	inuse;
+	int	vers;
+	ulong	mode;
+	char	*name;
+	vlong	offset;		/* in sectors */
+	vlong	length;		/* in sectors */
+};
+
+enum
+{
+	Qroot = 0,
+	Qdir,
+	Qctl,
+	Qpart,
+};
+
+int fd = -1, ctlfd = -1;
+int rdonly;
+ulong ctlmode = 0666;
+ulong time0;
+vlong nsect, sectsize;
+
+char *inquiry = "partfs hard drive";
+char *sdname = "sdXX";
+Part tab[64];
+
+char*
+ctlstring(void)
+{
+	Part *p;
+	Fmt fmt;
+
+	fmtstrinit(&fmt);
+	fmtprint(&fmt, "inquiry %s\n", inquiry);
+	fmtprint(&fmt, "geometry %lld %lld\n", nsect, sectsize);
+	for (p = tab; p < tab + nelem(tab); p++)
+		if (p->inuse)
+			fmtprint(&fmt, "part %s %lld %lld\n",
+				p->name, p->offset, p->length);
+	return fmtstrflush(&fmt);
+}
+
+int
+addpart(char *name, vlong start, vlong end)
+{
+	Part *p;
+
+	if(start < 0 || start > end || end > nsect){
+		werrstr("bad partition boundaries");
+		return -1;
+	}
+
+	if (strcmp(name, "ctl") == 0 || strcmp(name, "data") == 0) {
+		werrstr("partition name already in use");
+		return -1;
+	}
+	for (p = tab; p < tab + nelem(tab) && p->inuse; p++)
+		if (strcmp(p->name, name) == 0) {
+			werrstr("partition name already in use");
+			return -1;
+		}
+	if(p == tab + nelem(tab)){
+		werrstr("no free partition slots");
+		return -1;
+	}
+
+	p->inuse = 1;
+	free(p->name);
+	p->name = estrdup9p(name);
+	p->offset = start;
+	p->length = end - start;
+	p->mode = ctlmode;
+	p->vers++;
+	return 0;
+}
+
+int
+delpart(char *s)
+{
+	Part *p;
+
+	for (p = tab; p < tab + nelem(tab); p++)
+		if(p->inuse && strcmp(p->name, s) == 0)
+			break;
+	if(p == tab + nelem(tab)){
+		werrstr("partition not found");
+		return -1;
+	}
+
+	p->inuse = 0;
+	free(p->name);
+	p->name = nil;
+	return 0;
+}
+
+static void
+ctlwrite0(Req *r, char *msg, Cmdbuf *cb)
+{
+	vlong start, end;
+	Part *p;
+
+	r->ofcall.count = r->ifcall.count;
+
+	if(cb->nf < 1){
+		respond(r, "empty control message");
+		return;
+	}
+
+	if(strcmp(cb->f[0], "part") == 0){
+		if(cb->nf != 4){
+			respondcmderror(r, cb, "part takes 3 args");
+			return;
+		}
+		start = strtoll(cb->f[2], 0, 0);
+		end = strtoll(cb->f[3], 0, 0);
+		if(addpart(cb->f[1], start, end) < 0){
+			respondcmderror(r, cb, "%r");
+			return;
+		}
+	}
+	else if(strcmp(cb->f[0], "delpart") == 0){
+		if(cb->nf != 2){
+			respondcmderror(r, cb, "delpart takes 1 arg");
+			return;
+		}
+		if(delpart(cb->f[1]) < 0){
+			respondcmderror(r, cb, "%r");
+			return;
+		}
+	}
+	else if(strcmp(cb->f[0], "inquiry") == 0){
+		if(cb->nf != 2){
+			respondcmderror(r, cb, "inquiry takes 1 arg");
+			return;
+		}
+		free(inquiry);
+		inquiry = estrdup9p(cb->f[1]);
+	}
+	else if(strcmp(cb->f[0], "geometry") == 0){
+		if(cb->nf != 3){
+			respondcmderror(r, cb, "geometry takes 2 args");
+			return;
+		}
+		nsect = strtoll(cb->f[1], 0, 0);
+		sectsize = strtoll(cb->f[2], 0, 0);
+		if(tab[0].inuse && strcmp(tab[0].name, "data") == 0 &&
+		    tab[0].vers == 0){
+			tab[0].offset = 0;
+			tab[0].length = nsect;
+		}
+		for(p = tab; p < tab + nelem(tab); p++)
+			if(p->inuse && p->offset + p->length > nsect){
+				p->inuse = 0;
+				free(p->name);
+				p->name = nil;
+			}
+	} else
+		/* pass through to underlying ctl file, if any */
+		if (write(ctlfd, msg, r->ifcall.count) != r->ifcall.count) {
+			respondcmderror(r, cb, "%r");
+			return;
+		}
+	respond(r, nil);
+}
+
+void
+ctlwrite(Req *r)
+{
+	char *msg;
+	Cmdbuf *cb;
+
+	r->ofcall.count = r->ifcall.count;
+
+	msg = emalloc9p(r->ifcall.count+1);
+	memmove(msg, r->ifcall.data, r->ifcall.count);
+	msg[r->ifcall.count] = '\0';
+
+	cb = parsecmd(r->ifcall.data, r->ifcall.count);
+	ctlwrite0(r, msg, cb);
+
+	free(cb);
+	free(msg);
+}
+
+int
+rootgen(int off, Dir *d, void*)
+{
+	memset(d, 0, sizeof *d);
+	d->atime = time0;
+	d->mtime = time0;
+	if(off == 0){
+		d->name = estrdup9p(sdname);
+		d->mode = DMDIR|0777;
+		d->qid.path = Qdir;
+		d->qid.type = QTDIR;
+		d->uid = estrdup9p("partfs");
+		d->gid = estrdup9p("partfs");
+		d->muid = estrdup9p("");
+		return 0;
+	}
+	return -1;
+}
+
+int
+dirgen(int off, Dir *d, void*)
+{
+	int n;
+	Part *p;
+
+	memset(d, 0, sizeof *d);
+	d->atime = time0;
+	d->mtime = time0;
+	if(off == 0){
+		d->name = estrdup9p("ctl");
+		d->mode = ctlmode;
+		d->qid.path = Qctl;
+		goto Have;
+	}
+
+	off--;
+	n = 0;
+	for(p = tab; p < tab + nelem(tab); p++, n++){
+		if(!p->inuse)
+			continue;
+		if(n == off){
+			d->name = estrdup9p(p->name);
+			d->length = p->length*sectsize;
+			d->mode = p->mode;
+			d->qid.path = Qpart + p - tab;
+			d->qid.vers = p->vers;
+			goto Have;
+		}
+	}
+	return -1;
+
+Have:
+	d->uid = estrdup9p("partfs");
+	d->gid = estrdup9p("partfs");
+	d->muid = estrdup9p("");
+	return 0;
+}
+
+void*
+evommem(void *a, void *b, ulong n)
+{
+	return memmove(b, a, n);
+}
+
+int
+rdwrpart(Req *r)
+{
+	int q;
+	long count, tot;
+	vlong offset;
+	uchar *dat;
+	Part *p;
+
+	q = r->fid->qid.path - Qpart;
+	if(q < 0 || q > nelem(tab) || !tab[q].inuse ||
+	    tab[q].vers != r->fid->qid.vers){
+		respond(r, "unknown partition");
+		return -1;
+	}
+	p = &tab[q];
+
+	offset = r->ifcall.offset;
+	count = r->ifcall.count;
+	if(offset < 0){
+		respond(r, "negative offset");
+		return -1;
+	}
+	if(count < 0){
+		respond(r, "negative count");
+		return -1;
+	}
+	if(offset > p->length*sectsize){
+		respond(r, "offset past end of partition");
+		return -1;
+	}
+	if(offset+count > p->length*sectsize)
+		count = p->length*sectsize - offset;
+	offset += p->offset*sectsize;
+
+	if(r->ifcall.type == Tread)
+		dat = (uchar*)r->ofcall.data;
+	else
+		dat = (uchar*)r->ifcall.data;
+
+	/* pass i/o through to underlying file */
+	seek(fd, offset, 0);
+	if (r->ifcall.type == Twrite) {
+		tot = write(fd, dat, count);
+		if (tot != count) {
+			respond(r, "%r");
+			return -1;
+		}
+	} else {
+		tot = read(fd, dat, count);
+		if (tot < 0) {
+			respond(r, "%r");
+			return -1;
+		}
+	}
+	r->ofcall.count = tot;
+	respond(r, nil);
+	return 0;
+}
+
+void
+fsread(Req *r)
+{
+	char *s;
+
+	switch((int)r->fid->qid.path){
+	case Qroot:
+		dirread9p(r, rootgen, nil);
+		break;
+	case Qdir:
+		dirread9p(r, dirgen, nil);
+		break;
+	case Qctl:
+		s = ctlstring();
+		readstr(r, s);
+		free(s);
+		break;
+	default:
+		rdwrpart(r);
+		return;
+	}
+	respond(r, nil);
+}
+
+void
+fswrite(Req *r)
+{
+	switch((int)r->fid->qid.path){
+	case Qroot:
+	case Qdir:
+		respond(r, "write to a directory?");
+		break;
+	case Qctl:
+		ctlwrite(r);
+		break;
+	default:
+		rdwrpart(r);
+		break;
+	}
+}
+
+void
+fsopen(Req *r)
+{
+	if(r->ifcall.mode&ORCLOSE)
+		respond(r, "cannot open ORCLOSE");
+
+	switch((int)r->fid->qid.path){
+	case Qroot:
+	case Qdir:
+		if(r->ifcall.mode != OREAD){
+			respond(r, "bad mode for directory open");
+			return;
+		}
+	}
+
+	respond(r, nil);
+}
+
+void
+fsstat(Req *r)
+{
+	int q;
+	Dir *d;
+	Part *p;
+
+	d = &r->d;
+	memset(d, 0, sizeof *d);
+	d->qid = r->fid->qid;
+	d->atime = d->mtime = time0;
+	q = r->fid->qid.path;
+	switch(q){
+	case Qroot:
+		d->name = estrdup9p("/");
+		d->mode = DMDIR|0777;
+		break;
+
+	case Qdir:
+		d->name = estrdup9p(sdname);
+		d->mode = DMDIR|0777;
+		break;
+
+	case Qctl:
+		d->name = estrdup9p("ctl");
+		d->mode = 0666;
+		break;
+
+	default:
+		q -= Qpart;
+		if(q < 0 || q > nelem(tab) || tab[q].inuse == 0 ||
+		    r->fid->qid.vers != tab[q].vers){
+			respond(r, "partition no longer exists");
+			return;
+		}
+		p = &tab[q];
+		d->name = estrdup9p(p->name);
+		d->length = p->length * sectsize;
+		d->mode = p->mode;
+		break;
+	}
+
+	d->uid = estrdup9p("partfs");
+	d->gid = estrdup9p("partfs");
+	d->muid = estrdup9p("");
+	respond(r, nil);
+}
+
+void
+fsattach(Req *r)
+{
+	char *spec;
+
+	spec = r->ifcall.aname;
+	if(spec && spec[0]){
+		respond(r, "invalid attach specifier");
+		return;
+	}
+	r->ofcall.qid = (Qid){Qroot, 0, QTDIR};
+	r->fid->qid = r->ofcall.qid;
+	respond(r, nil);
+}
+
+char*
+fswalk1(Fid *fid, char *name, Qid *qid)
+{
+	Part *p;
+
+	switch((int)fid->qid.path){
+	case Qroot:
+		if(strcmp(name, sdname) == 0){
+			fid->qid.path = Qdir;
+			fid->qid.type = QTDIR;
+			*qid = fid->qid;
+			return nil;
+		}
+		break;
+	case Qdir:
+		if(strcmp(name, "ctl") == 0){
+			fid->qid.path = Qctl;
+			fid->qid.vers = 0;
+			fid->qid.type = 0;
+			*qid = fid->qid;
+			return nil;
+		}
+		for(p = tab; p < tab + nelem(tab); p++)
+			if(p->inuse && strcmp(p->name, name) == 0){
+				fid->qid.path = p - tab + Qpart;
+				fid->qid.vers = p->vers;
+				fid->qid.type = 0;
+				*qid = fid->qid;
+				return nil;
+			}
+		break;
+	}
+	return "file not found";
+}
+
+Srv fs = {
+	.attach=fsattach,
+	.open=	fsopen,
+	.read=	fsread,
+	.write=	fswrite,
+	.stat=	fsstat,
+	.walk1=	fswalk1,
+};
+
+char *mtpt = "/dev";
+char *srvname;
+
+void
+usage(void)
+{
+	fprint(2, "usage: %s [-D] [-f file] [-s srvname] [-m mtpt] [sdXX]\n",
+		argv0);
+	fprint(2, "\tdefault mtpt is /dev\n");
+	exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+	int isdir;
+	char *file, *cname;
+	Dir *dir;
+
+	file = nil;
+	quotefmtinstall();
+	time0 = time(0);
+
+	ARGBEGIN{
+	case 'D':
+		chatty9p++;
+		break;
+	case 'f':
+		file = EARGF(usage());
+		break;
+	case 'm':
+		mtpt = EARGF(usage());
+		break;
+	case 'r':
+		rdonly = 1;
+		break;
+	case 's':
+		srvname = EARGF(usage());
+		break;
+	default:
+		usage();
+	}ARGEND
+
+	if(argc > 1)
+		usage();
+	if(argc == 1)
+		sdname = argv[0];
+	if(!file)
+		sysfatal("no underlying file named");
+	dir = dirstat(file);
+	if(!dir)
+		sysfatal("%s: %r", file);
+	isdir = (dir->mode & DMDIR) != 0;
+	free(dir);
+
+	if (isdir) {
+		cname = smprint("%s/ctl", file);
+		if ((ctlfd = open(cname, ORDWR)) < 0)
+			sysfatal("open %s: %r", cname);
+		file = smprint("%s/data", file);
+	}
+	if((fd = open(file, rdonly? OREAD: ORDWR)) < 0)
+		sysfatal("open %s: %r", file);
+
+	sectsize = 512;			/* conventional */
+	dir = dirfstat(fd);
+	if (dir)
+		nsect = dir->length / sectsize;
+	free(dir);
+
+	inquiry = estrdup9p(inquiry);
+	tab[0].inuse = 1;
+	tab[0].name = estrdup9p("data");
+	tab[0].mode = 0666;
+	tab[0].length = nsect;
+
+	postmountsrv(&fs, srvname, mtpt, MBEFORE);
+	exits(nil);
+}