git: drawterm

Download patch

ref: de0baba0ab52f9279212233558bbb1ff67e5106c
parent: 7fee327ee530409dee0d36cb258927934ba97aff
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Mon Aug 18 09:58:49 EDT 2025

exportfs: backport updated exportfs from 9front

This has some improvements for handling the processes
and handles flushes properly.

--- a/exportfs/Makefile
+++ b/exportfs/Makefile
@@ -2,9 +2,12 @@
 include ../Make.config
 LIB=libexportfs.a
 
+HFILES=exportfs.h
+
 OFILES=\
 	exportfs.$O\
-	exportsrv.$O
+	exportsrv.$O\
+	io.$O\
 
 default: $(LIB)
 $(LIB): $(OFILES)
--- a/exportfs/exportfs.c
+++ b/exportfs/exportfs.c
@@ -6,57 +6,18 @@
 #include <fcall.h>
 #include <libsec.h>
 #include "drawterm.h"
-#define Extern
+#define Extern extern
 #include "exportfs.h"
 
-#define QIDPATH	((((vlong)1)<<48)-1)
-vlong newqid = 0;
-
-void (*fcalls[256])(Fsrpc*);
-
-/* accounting and debugging counters */
-int	filecnt;
-int	freecnt;
-int	qidcnt;
-int	qfreecnt;
-int	ncollision;
-int	netfd[2];
-
 int
 exportfs(int rfd, int wfd)
 {
-	char buf[ERRMAX], ebuf[ERRMAX];
-	Fsrpc *r;
-	int i, n;
+	DEBUG("exportfs: started\n");
 
-
-	fcalls[Tversion] = Xversion;
-	fcalls[Tauth] = Xauth;
-	fcalls[Tflush] = Xflush;
-	fcalls[Tattach] = Xattach;
-	fcalls[Twalk] = Xwalk;
-	fcalls[Topen] = slave;
-	fcalls[Tcreate] = Xcreate;
-	fcalls[Tclunk] = Xclunk;
-	fcalls[Tread] = slave;
-	fcalls[Twrite] = slave;
-	fcalls[Tremove] = Xremove;
-	fcalls[Tstat] = Xstat;
-	fcalls[Twstat] = Xwstat;
-
-	srvfd = -1;
-	netfd[0] = rfd;
-	netfd[1] = wfd;
-
-	strcpy(buf, "this is buf");
-	strcpy(ebuf, "this is ebuf");
-	DEBUG(DFD, "exportfs: started\n");
-
 	messagesize = iounit(rfd);
 	if(messagesize == 0)
 		messagesize = IOUNIT+IOHDRSZ;
 
-	Workq = emallocz(sizeof(Fsrpc)*Nr_workbufs);
 	fhash = emallocz(sizeof(Fid*)*FHASHSIZE);
 
 	fmtinstall('F', fcallfmt);
@@ -63,443 +24,7 @@
 
 	initroot();
 
-	DEBUG(DFD, "exportfs: %s\n", buf);
+	io(rfd, wfd);
 
-	/*
-	 * Start serving file requests from the network
-	 */
-	for(;;) {
-		r = getsbuf();
-		if(r == 0)
-			fatal("Out of service buffers");
-			
-		DEBUG(DFD, "read9p...");
-		n = read9pmsg(netfd[0], r->buf, messagesize);
-		if(n <= 0)
-			fatal(nil);
-
-		if(convM2S(r->buf, n, &r->work) == 0){
-			iprint("convM2S %d byte message\n", n);
-			for(i=0; i<n; i++){
-				iprint(" %.2ux", r->buf[i]);
-				if(i%16 == 15)
-					iprint("\n");
-			}
-			if(i%16)
-				iprint("\n");
-			fatal("convM2S format error");
-		}
-
-if(0) iprint("<- %F\n", &r->work);
-		DEBUG(DFD, "%F\n", &r->work);
-		(fcalls[r->work.type])(r);
-	}
-}
-
-void
-reply(Fcall *r, Fcall *t, char *err)
-{
-	uchar *data;
-	int m, n;
-
-	t->tag = r->tag;
-	t->fid = r->fid;
-	if(err) {
-		t->type = Rerror;
-		t->ename = err;
-	}
-	else 
-		t->type = r->type + 1;
-
-if(0) iprint("-> %F\n", t);
-	DEBUG(DFD, "\t%F\n", t);
-
-	data = malloc(messagesize);	/* not mallocz; no need to clear */
-	if(data == nil)
-		fatal(Enomem);
-	n = convS2M(t, data, messagesize);
-	if((m=write(netfd[1], data, n))!=n){
-		iprint("wrote %d got %d (%r)\n", n, m);
-		fatal("write");
-	}
-	free(data);
-}
-
-Fid *
-getfid(int nr)
-{
-	Fid *f;
-
-	for(f = fidhash(nr); f; f = f->next)
-		if(f->nr == nr)
-			return f;
-
 	return 0;
 }
-
-int
-freefid(int nr)
-{
-	Fid *f, **l;
-	char buf[128];
-
-	l = &fidhash(nr);
-	for(f = *l; f; f = f->next) {
-		if(f->nr == nr) {
-			if(f->mid) {
-				sprint(buf, "/mnt/exportfs/%d", f->mid);
-				unmount(0, buf);
-				psmap[f->mid] = 0;
-			}
-			if(f->f) {
-				freefile(f->f);
-				f->f = nil;
-			}
-			*l = f->next;
-			f->next = fidfree;
-			fidfree = f;
-			return 1;
-		}
-		l = &f->next;
-	}
-
-	return 0;	
-}
-
-Fid *
-newfid(int nr)
-{
-	Fid *new, **l;
-	int i;
-
-	l = &fidhash(nr);
-	for(new = *l; new; new = new->next)
-		if(new->nr == nr)
-			return 0;
-
-	if(fidfree == 0) {
-		fidfree = emallocz(sizeof(Fid) * Fidchunk);
-
-		for(i = 0; i < Fidchunk-1; i++)
-			fidfree[i].next = &fidfree[i+1];
-
-		fidfree[Fidchunk-1].next = 0;
-	}
-
-	new = fidfree;
-	fidfree = new->next;
-
-	memset(new, 0, sizeof(Fid));
-	new->next = *l;
-	*l = new;
-	new->nr = nr;
-	new->fid = -1;
-	new->mid = 0;
-
-	return new;	
-}
-
-Fsrpc *
-getsbuf(void)
-{
-	static int ap;
-	int look, rounds;
-	Fsrpc *wb;
-	int small_instead_of_fast = 1;
-
-	if(small_instead_of_fast)
-		ap = 0;	/* so we always start looking at the beginning and reuse buffers */
-
-	for(rounds = 0; rounds < 10; rounds++) {
-		for(look = 0; look < Nr_workbufs; look++) {
-			if(++ap == Nr_workbufs)
-				ap = 0;
-			if(Workq[ap].busy == 0)
-				break;
-		}
-
-		if(look == Nr_workbufs){
-			sleep(10 * rounds);
-			continue;
-		}
-
-		wb = &Workq[ap];
-		wb->kp = nil;
-		wb->canint = 0;
-		wb->flushtag = NOTAG;
-		wb->busy = 1;
-		if(wb->buf == nil)	/* allocate buffers dynamically to keep size down */
-			wb->buf = emallocz(messagesize);
-		return wb;
-	}
-	fatal("No more work buffers");
-	return nil;
-}
-
-void
-freefile(File *f)
-{
-	File *parent, *child;
-
-Loop:
-	f->ref--;
-	if(f->ref > 0)
-		return;
-	freecnt++;
-	if(f->ref < 0) abort();
-	DEBUG(DFD, "free %s\n", f->name);
-	/* delete from parent */
-	parent = f->parent;
-	if(parent->child == f)
-		parent->child = f->childlist;
-	else{
-		for(child=parent->child; child->childlist!=f; child=child->childlist)
-			if(child->childlist == nil)
-				fatal("bad child list");
-		child->childlist = f->childlist;
-	}
-	freeqid(f->qidt);
-	free(f->name);
-	f->name = nil;
-	free(f);
-	f = parent;
-	if(f != nil)
-		goto Loop;
-}
-
-File *
-file(File *parent, char *name)
-{
-	Dir *dir;
-	char *path;
-	File *f;
-
-	DEBUG(DFD, "\tfile: 0x%p %s name %s\n", parent, parent->name, name);
-
-	path = makepath(parent, name);
-	dir = dirstat(path);
-	free(path);
-	if(dir == nil)
-		return nil;
-
-	for(f = parent->child; f; f = f->childlist)
-		if(strcmp(name, f->name) == 0)
-			break;
-
-	if(f == nil){
-		f = emallocz(sizeof(File));
-		f->name = estrdup(name);
-
-		f->parent = parent;
-		f->childlist = parent->child;
-		parent->child = f;
-		parent->ref++;
-		f->ref = 0;
-		filecnt++;
-	}
-	f->ref++;
-	f->qid.type = dir->qid.type;
-	f->qid.vers = dir->qid.vers;
-	f->qidt = uniqueqid(dir);
-	f->qid.path = f->qidt->uniqpath;
-
-	f->inval = 0;
-
-	free(dir);
-
-	return f;
-}
-
-void
-initroot(void)
-{
-	Dir *dir;
-
-	root = emallocz(sizeof(File));
-	root->name = estrdup(".");
-
-	dir = dirstat(root->name);
-	if(dir == nil)
-		fatal("root stat");
-
-	root->ref = 1;
-	root->qid.vers = dir->qid.vers;
-	root->qidt = uniqueqid(dir);
-	root->qid.path = root->qidt->uniqpath;
-	root->qid.type = QTDIR;
-	free(dir);
-
-	psmpt = emallocz(sizeof(File));
-	psmpt->name = estrdup("/");
-
-	dir = dirstat(psmpt->name);
-	if(dir == nil)
-		return;
-
-	psmpt->ref = 1;
-	psmpt->qid.vers = dir->qid.vers;
-	psmpt->qidt = uniqueqid(dir);
-	psmpt->qid.path = psmpt->qidt->uniqpath;
-	free(dir);
-
-	psmpt = file(psmpt, "mnt");
-	if(psmpt == 0)
-		return;
-	psmpt = file(psmpt, "exportfs");
-}
-
-char*
-makepath(File *p, char *name)
-{
-	int i, n;
-	char *c, *s, *path, *seg[256];
-
-	seg[0] = name;
-	n = strlen(name)+2;
-	for(i = 1; i < 256 && p; i++, p = p->parent){
-		seg[i] = p->name;
-		n += strlen(p->name)+1;
-	}
-	path = malloc(n);
-	if(path == nil)
-		fatal("out of memory");
-	s = path;
-
-	while(i--) {
-		for(c = seg[i]; *c; c++)
-			*s++ = *c;
-		*s++ = '/';
-	}
-	while(s[-1] == '/')
-		s--;
-	*s = '\0';
-
-	return path;
-}
-
-int
-qidhash(vlong path)
-{
-	int h, n;
-
-	h = 0;
-	for(n=0; n<64; n+=Nqidbits){
-		h ^= path;
-		path >>= Nqidbits;
-	}
-	return h & (Nqidtab-1);
-}
-
-void
-freeqid(Qidtab *q)
-{
-	ulong h;
-	Qidtab *l;
-
-	q->ref--;
-	if(q->ref > 0)
-		return;
-	qfreecnt++;
-	h = qidhash(q->path);
-	if(qidtab[h] == q)
-		qidtab[h] = q->next;
-	else{
-		for(l=qidtab[h]; l->next!=q; l=l->next)
-			if(l->next == nil)
-				fatal("bad qid list");
-		l->next = q->next;
-	}
-	free(q);
-}
-
-Qidtab*
-qidlookup(Dir *d)
-{
-	ulong h;
-	Qidtab *q;
-
-	h = qidhash(d->qid.path);
-	for(q=qidtab[h]; q!=nil; q=q->next)
-		if(q->type==d->type && q->dev==d->dev && q->path==d->qid.path)
-			return q;
-	return nil;
-}
-
-int
-qidexists(vlong path)
-{
-	int h;
-	Qidtab *q;
-
-	for(h=0; h<Nqidtab; h++)
-		for(q=qidtab[h]; q!=nil; q=q->next)
-			if(q->uniqpath == path)
-				return 1;
-	return 0;
-}
-
-Qidtab*
-uniqueqid(Dir *d)
-{
-	ulong h;
-	vlong path;
-	Qidtab *q;
-
-	q = qidlookup(d);
-	if(q != nil){
-		q->ref++;
-		return q;
-	}
-	path = d->qid.path;
-	while(qidexists(path)){
-		DEBUG(DFD, "collision on %s\n", d->name);
-		/* collision: find a new one */
-		ncollision++;
-		path &= QIDPATH;
-		++newqid;
-		if(newqid >= (1<<16)){
-			DEBUG(DFD, "collision wraparound\n");
-			newqid = 1;
-		}
-		path |= newqid<<48;
-		DEBUG(DFD, "assign qid %.16llux\n", path);
-	}
-	q = mallocz(sizeof(Qidtab), 1);
-	if(q == nil)
-		fatal("no memory for qid table");
-	qidcnt++;
-	q->ref = 1;
-	q->type = d->type;
-	q->dev = d->dev;
-	q->path = d->qid.path;
-	q->uniqpath = path;
-	h = qidhash(d->qid.path);
-	q->next = qidtab[h];
-	qidtab[h] = q;
-	return q;
-}
-
-void
-fatal(char *s, ...)
-{
-	char buf[ERRMAX];
-	va_list arg;
-	Proc *m;
-
-	if (s != nil) {
-		va_start(arg, s);
-		vsnprint(buf, ERRMAX, s, arg);
-		va_end(arg);
-	}
-
-	/* Clear away the slave children */
-	for(m = Proclist; m; m = m->next)
-		kprocint(m->kp);
-
-	if (s != nil) { 
-		DEBUG(DFD, "%s\n", buf);
-		sysfatal("%s", buf);
-	} else
-		exits(nil);
-}
-
--- a/exportfs/exportfs.h
+++ b/exportfs/exportfs.h
@@ -2,13 +2,11 @@
  * exportfs.h - definitions for exporting file server
  */
 
-#define DEBUG		if(!dbg){}else fprint
-#define DFD		2
+#define DEBUG(...)
 #define fidhash(s)	fhash[s%FHASHSIZE]
 
 #define Proc	Exproc
 
-
 typedef struct Fsrpc Fsrpc;
 typedef struct Fid Fid;
 typedef struct File File;
@@ -17,12 +15,10 @@
 
 struct Fsrpc
 {
-	int	busy;		/* Work buffer has pending rpc to service */
-	void*	kp;		/* slave process executing the rpc */
-	int	canint;		/* Interrupt gate */
+	Fsrpc	*next;		/* freelist */
 	int	flushtag;	/* Tag on which to reply to flush */
-	Fcall work;		/* Plan 9 incoming Fcall */
-	uchar	*buf;	/* Data buffer */
+	Fcall	work;		/* Plan 9 incoming Fcall */
+	uchar	buf[];		/* Data buffer */
 };
 
 struct Fid
@@ -33,6 +29,13 @@
 	int	nr;		/* fid number */
 	int	mid;		/* Mount id */
 	Fid	*next;		/* hash link */
+
+	/* for preaddir -- ARRGH! */
+	Dir	*dir;		/* buffer for reading directories */
+	int	ndir;		/* number of entries in dir */
+	int	cdir;		/* number of consumed entries in dir */
+	int	gdir;		/* glue index */
+	vlong	offset;		/* offset in virtual directory */
 };
 
 struct File
@@ -49,9 +52,10 @@
 
 struct Proc
 {
-	void	*kp;
-	int	busy;
+	Lock	lock;
+	Fsrpc	*busy;
 	Proc	*next;
+	void	*kp;
 };
 
 struct Qidtab
@@ -66,9 +70,7 @@
 
 enum
 {
-	MAXPROC		= 50,
 	FHASHSIZE	= 64,
-	Nr_workbufs 	= 50,
 	Fidchunk	= 1000,
 	Npsmpt		= 32,
 	Nqidbits		= 5,
@@ -76,34 +78,26 @@
 };
 
 #define Enomem Exenomem
-#define Ebadfix Exebadfid
+#define Ebadfid Exebadfid
 #define Enotdir Exenotdir
 #define Edupfid Exedupfid
 #define Eopen Exeopen
 #define Exmnt Exexmnt
-#define Emip Exemip
-#define Enopsmt Exenopsmt
 
+extern char Enomem[];
 extern char Ebadfid[];
 extern char Enotdir[];
 extern char Edupfid[];
 extern char Eopen[];
 extern char Exmnt[];
-extern char Enomem[];
-extern char Emip[];
-extern char Enopsmt[];
 
-Extern Fsrpc	*Workq;
-Extern int  	dbg;
 Extern File	*root;
 Extern File	*psmpt;
 Extern Fid	**fhash;
 Extern Fid	*fidfree;
 Extern Proc	*Proclist;
-Extern char	psmap[Npsmpt];
 Extern Qidtab	*qidtab[Nqidtab];
 Extern ulong	messagesize;
-Extern int		srvfd;
 
 /* File system protocol service procedures */
 void Xattach(Fsrpc*);
@@ -119,11 +113,15 @@
 void Xwstat(Fsrpc*);
 void slave(Fsrpc*);
 
+void	io(int, int);
 void	reply(Fcall*, Fcall*, char*);
+void	mounterror(char*);
+
 Fid 	*getfid(int);
 int	freefid(int);
 Fid	*newfid(int);
 Fsrpc	*getsbuf(void);
+void	putsbuf(Fsrpc*);
 void	initroot(void);
 void	fatal(char*, ...);
 char*	makepath(File*, char*);
@@ -141,8 +139,4 @@
 void	freeqid(Qidtab*);
 char*	estrdup(char*);
 void*	emallocz(uint);
-int		readmessage(int, char*, int);
-
-#define notify(x)
-#define noted(x)
-
+int	readmessage(int, char*, int);
--- a/exportfs/exportsrv.c
+++ b/exportfs/exportsrv.c
@@ -1,5 +1,6 @@
 #include <u.h>
 #include <libc.h>
+#include <auth.h>
 #include <fcall.h>
 #define Extern	extern
 #include "exportfs.h"
@@ -12,36 +13,29 @@
 char Emip[] = "Mount in progress";
 char Enopsmt[] = "Out of pseudo mount points";
 char Enomem[] = "No memory";
-char Eversion[] = "Bad 9P2000 version";
+char Ereadonly[] = "File system read only";
 
-void*
-emallocz(uint n)
-{
-	void *v;
+int readonly;
 
-	v = mallocz(n, 1);
-	if(v == nil)
-		panic("out of memory");
-	return v;
-}
-
-
 void
 Xversion(Fsrpc *t)
 {
 	Fcall rhdr;
 
+	if(t->work.msize < 256){
+		reply(&t->work, &rhdr, "version: message size too small");
+		putsbuf(t);
+		return;
+	}
 	if(t->work.msize > messagesize)
 		t->work.msize = messagesize;
 	messagesize = t->work.msize;
-	if(strncmp(t->work.version, "9P2000", 6) != 0){
-		reply(&t->work, &rhdr, Eversion);
-		return;
-	}
-	rhdr.version = "9P2000";
 	rhdr.msize = t->work.msize;
+	rhdr.version = "9P2000";
+	if(strncmp(t->work.version, "9P", 2) != 0)
+		rhdr.version = "unknown";
 	reply(&t->work, &rhdr, 0);
-	t->busy = 0;
+	putsbuf(t);
 }
 
 void
@@ -50,34 +44,37 @@
 	Fcall rhdr;
 
 	reply(&t->work, &rhdr, "exportfs: authentication not required");
-	t->busy = 0;
+	putsbuf(t);
 }
 
 void
 Xflush(Fsrpc *t)
 {
-	Fsrpc *w, *e;
 	Fcall rhdr;
+	Fsrpc *w;
+	Proc *m;
 
-	e = &Workq[Nr_workbufs];
+	for(m = Proclist; m != nil; m = m->next){
+		w = m->busy;
+		if(w == nil || w->work.tag != t->work.oldtag)
+			continue;
 
-	for(w = Workq; w < e; w++) {
-		if(w->work.tag == t->work.oldtag) {
-			DEBUG(DFD, "\tQ busy %d kp %d can %d\n", w->busy, w->kp, w->canint);
-			if(w->busy && w->kp) {
-				w->flushtag = t->work.tag;
-				DEBUG(DFD, "\tset flushtag %d\n", t->work.tag);
-				if(w->canint)
-					kprocint(w->kp);
-				t->busy = 0;
-				return;
-			}
+		lock(&m->lock);
+		w = m->busy;
+		if(w != nil && w->work.tag == t->work.oldtag) {
+			w->flushtag = t->work.tag;
+			DEBUG("\tset flushtag %d\n", t->work.tag);
+			kprocint(m->kp);
+			unlock(&m->lock);
+			putsbuf(t);
+			return;
 		}
+		unlock(&m->lock);
 	}
 
 	reply(&t->work, &rhdr, 0);
-	DEBUG(DFD, "\tflush reply\n");
-	t->busy = 0;
+	DEBUG("\tflush reply\n");
+	putsbuf(t);
 }
 
 void
@@ -87,51 +84,18 @@
 	Fid *f;
 
 	f = newfid(t->work.fid);
-	if(f == 0) {
+	if(f == nil) {
 		reply(&t->work, &rhdr, Ebadfid);
-		t->busy = 0;
+		putsbuf(t);
 		return;
 	}
 
-	if(srvfd >= 0){
-/*
-		if(psmpt == 0){
-		Nomount:
-			reply(&t->work, &rhdr, Enopsmt);
-			t->busy = 0;
-			freefid(t->work.fid);
-			return;
-		}
-		for(i=0; i<Npsmpt; i++)
-			if(psmap[i] == 0)
-				break;
-		if(i >= Npsmpt)
-			goto Nomount;
-		sprint(buf, "%d", i);
-		f->f = file(psmpt, buf);
-		if(f->f == nil)
-			goto Nomount;
-		sprint(buf, "/mnt/exportfs/%d", i);
-		nfd = dup(srvfd, -1);
-		if(amount(nfd, buf, MREPL|MCREATE, t->work.aname) < 0){
-			errstr(buf, sizeof buf);
-			reply(&t->work, &rhdr, buf);
-			t->busy = 0;
-			freefid(t->work.fid);
-			close(nfd);
-			return;
-		}
-		psmap[i] = 1;
-		f->mid = i;
-*/
-	}else{
-		f->f = root;
-		f->f->ref++;
-	}
+	f->f = root;
+	f->f->ref++;
 
 	rhdr.qid = f->f->qid;
 	reply(&t->work, &rhdr, 0);
-	t->busy = 0;
+	putsbuf(t);
 }
 
 Fid*
@@ -140,15 +104,15 @@
 	Fid *n;
 
 	n = newfid(new);
-	if(n == 0) {
+	if(n == nil) {
 		n = getfid(new);
-		if(n == 0)
+		if(n == nil)
 			fatal("inconsistent fids");
 		if(n->fid >= 0)
 			close(n->fid);
 		freefid(new);
 		n = newfid(new);
-		if(n == 0)
+		if(n == nil)
 			fatal("inconsistent fids2");
 	}
 	n->f = f->f;
@@ -166,9 +130,9 @@
 	int i;
 
 	f = getfid(t->work.fid);
-	if(f == 0) {
+	if(f == nil) {
 		reply(&t->work, &rhdr, Ebadfid);
-		t->busy = 0;
+		putsbuf(t);
 		return;
 	}
 
@@ -197,7 +161,7 @@
 		}
 	
 		wf = file(f->f, t->work.wname[i]);
-		if(wf == 0){
+		if(wf == nil){
 			errstr(err, sizeof err);
 			e = err;
 			break;
@@ -214,7 +178,7 @@
 	if(rhdr.nwqid > 0)
 		e = nil;
 	reply(&t->work, &rhdr, e);
-	t->busy = 0;
+	putsbuf(t);
 }
 
 void
@@ -224,9 +188,9 @@
 	Fid *f;
 
 	f = getfid(t->work.fid);
-	if(f == 0) {
+	if(f == nil) {
 		reply(&t->work, &rhdr, Ebadfid);
-		t->busy = 0;
+		putsbuf(t);
 		return;
 	}
 
@@ -235,7 +199,7 @@
 
 	freefid(t->work.fid);
 	reply(&t->work, &rhdr, 0);
-	t->busy = 0;
+	putsbuf(t);
 }
 
 void
@@ -249,9 +213,9 @@
 	uchar *statbuf;
 
 	f = getfid(t->work.fid);
-	if(f == 0) {
+	if(f == nil) {
 		reply(&t->work, &rhdr, Ebadfid);
-		t->busy = 0;
+		putsbuf(t);
 		return;
 	}
 	if(f->fid >= 0)
@@ -265,7 +229,7 @@
 	if(d == nil) {
 		errstr(err, sizeof err);
 		reply(&t->work, &rhdr, err);
-		t->busy = 0;
+		putsbuf(t);
 		return;
 	}
 
@@ -278,7 +242,7 @@
 	rhdr.stat = statbuf;
 	reply(&t->work, &rhdr, 0);
 	free(statbuf);
-	t->busy = 0;
+	putsbuf(t);
 }
 
 static int
@@ -300,10 +264,15 @@
 	Fid *f;
 	File *nf;
 
+	if(readonly) {
+		reply(&t->work, &rhdr, Ereadonly);
+		putsbuf(t);
+		return;
+	}
 	f = getfid(t->work.fid);
-	if(f == 0) {
+	if(f == nil) {
 		reply(&t->work, &rhdr, Ebadfid);
-		t->busy = 0;
+		putsbuf(t);
 		return;
 	}
 	
@@ -314,15 +283,15 @@
 	if(f->fid < 0) {
 		errstr(err, sizeof err);
 		reply(&t->work, &rhdr, err);
-		t->busy = 0;
+		putsbuf(t);
 		return;
 	}
 
 	nf = file(f->f, t->work.name);
-	if(nf == 0) {
+	if(nf == nil) {
 		errstr(err, sizeof err);
 		reply(&t->work, &rhdr, err);
-		t->busy = 0;
+		putsbuf(t);
 		return;
 	}
 
@@ -332,7 +301,7 @@
 	rhdr.qid = f->f->qid;
 	rhdr.iounit = getiounit(f->fid);
 	reply(&t->work, &rhdr, 0);
-	t->busy = 0;
+	putsbuf(t);
 }
 
 void
@@ -342,20 +311,25 @@
 	Fcall rhdr;
 	Fid *f;
 
+	if(readonly) {
+		reply(&t->work, &rhdr, Ereadonly);
+		putsbuf(t);
+		return;
+	}
 	f = getfid(t->work.fid);
-	if(f == 0) {
+	if(f == nil) {
 		reply(&t->work, &rhdr, Ebadfid);
-		t->busy = 0;
+		putsbuf(t);
 		return;
 	}
 
 	path = makepath(f->f, "");
-	DEBUG(DFD, "\tremove: %s\n", path);
+	DEBUG("\tremove: %s\n", path);
 	if(remove(path) < 0) {
 		free(path);
 		errstr(err, sizeof err);
 		reply(&t->work, &rhdr, err);
-		t->busy = 0;
+		putsbuf(t);
 		return;
 	}
 	free(path);
@@ -366,7 +340,7 @@
 	freefid(t->work.fid);
 
 	reply(&t->work, &rhdr, 0);
-	t->busy = 0;
+	putsbuf(t);
 }
 
 void
@@ -379,10 +353,15 @@
 	char *strings;
 	Dir d;
 
+	if(readonly) {
+		reply(&t->work, &rhdr, Ereadonly);
+		putsbuf(t);
+		return;
+	}
 	f = getfid(t->work.fid);
-	if(f == 0) {
+	if(f == nil) {
 		reply(&t->work, &rhdr, Ebadfid);
-		t->busy = 0;
+		putsbuf(t);
 		return;
 	}
 	strings = emallocz(t->work.nstat);	/* ample */
@@ -389,7 +368,7 @@
 	if(convM2D(t->work.stat, t->work.nstat, &d, strings) <= BIT16SZ){
 		rerrstr(err, sizeof err);
 		reply(&t->work, &rhdr, err);
-		t->busy = 0;
+		putsbuf(t);
 		free(strings);
 		return;
 	}
@@ -414,73 +393,83 @@
 		reply(&t->work, &rhdr, 0);
 	}
 	free(strings);
-	t->busy = 0;
+	putsbuf(t);
 }
 
 void
 slave(Fsrpc *f)
 {
-	Proc *p;
-	void *kp;
 	static int nproc;
+	Proc *m, **l;
+	Fcall rhdr;
 
-	for(;;) {
-		for(p = Proclist; p; p = p->next) {
-			if(p->busy == 0) {
-				f->kp = p->kp;
-				p->busy = 1;
-				kp = rendezvous(p->kp, f);
-				if(kp != p->kp)
-					fatal("rendezvous sync fail");
+	if(readonly){
+		switch(f->work.type){
+		case Twrite:
+			reply(&f->work, &rhdr, Ereadonly);
+			putsbuf(f);
+			return;
+		case Topen:
+		  	if((f->work.mode&3) == OWRITE || (f->work.mode&(OTRUNC|ORCLOSE))){
+				reply(&f->work, &rhdr, Ereadonly);
+				putsbuf(f);
 				return;
 			}
 		}
+	}
+	for(;;) {
+		for(l = &Proclist; (m = *l) != nil; l = &m->next) {
+			if(m->busy != nil)
+				continue;
 
-		if(++nproc > MAXPROC)
-			fatal("too many procs");
+			m->busy = f;
+			while(rendezvous(m, f) == (void*)~0)
+				;
 
-		kp = kproc("slave", blockingslave, nil);
-		DEBUG(DFD, "slave kp %p\n", kp);
+			/* swept a slave proc */
+			if(f == nil){
+				*l = m->next;
+				free(m);
+				nproc--;
+				break;
+			}
+			f = nil;
 
-		p = malloc(sizeof(Proc));
-		if(p == 0)
-			fatal("out of memory");
+			/*
+			 * as long as the number of slave procs
+			 * is small, dont bother sweeping.
+			 */
+			if(nproc < 16)
+				break;
+		}
+		if(f == nil)
+			return;
 
-		p->busy = 0;
-		p->kp = kp;
-		p->next = Proclist;
-		Proclist = p;
-
-DEBUG(DFD, "parent %p rendez\n", kp);
-		rendezvous(kp, p);
-DEBUG(DFD, "parent %p went\n", kp);
+		m = emallocz(sizeof(Proc));
+		m->kp = kproc("slave", blockingslave, m);
+		m->next = Proclist;
+		Proclist = m;
+		nproc++;
 	}
 }
 
 void
-blockingslave(void *x)
+blockingslave(void *arg)
 {
+	Proc *m;
 	Fsrpc *p;
 	Fcall rhdr;
-	Proc *m;
-	void *kp;
 
-	USED(x);
+	m = (Proc*)arg;
 
-	kp = getkproc();
-
-	notify(flushaction);
-
-DEBUG(DFD, "blockingslave %p rendez\n", kp);
-	m = rendezvous(kp, 0);
-DEBUG(DFD, "blockingslave %p rendez got %p\n", kp, m);
-	
 	for(;;) {
-		p = rendezvous(kp, kp);
-		if((uintptr)p == ~(uintptr)0)			/* Interrupted */
+		p = rendezvous(m, nil);
+		if(p == (void*)~0)	/* Interrupted */
 			continue;
+		if(p == nil)		/* Swept */
+			break;
 
-		DEBUG(DFD, "\tslave: %p %F b %d p %p\n", kp, &p->work, p->busy, p->kp);
+		DEBUG("\tslave: %p %F\n", m->kp, &p->work);
 		if(p->flushtag != NOTAG)
 			goto flushme;
 
@@ -500,14 +489,18 @@
 		default:
 			reply(&p->work, &rhdr, "exportfs: slave type error");
 		}
-		if(p->flushtag != NOTAG) {
 flushme:
+		lock(&m->lock);
+		m->busy = nil;
+		unlock(&m->lock);
+
+		/* no more flushes can come in now */
+		if(p->flushtag != NOTAG) {
 			p->work.type = Tflush;
 			p->work.tag = p->flushtag;
 			reply(&p->work, &rhdr, 0);
 		}
-		p->busy = 0;
-		m->busy = 0;
+		putsbuf(p);
 	}
 }
 
@@ -529,7 +522,7 @@
 	work = &p->work;
 
 	f = getfid(work->fid);
-	if(f == 0) {
+	if(f == nil) {
 		reply(work, &rhdr, Ebadfid);
 		return;
 	}
@@ -539,17 +532,9 @@
 	}
 	
 	path = makepath(f->f, "");
-	DEBUG(DFD, "\topen: %s %d\n", path, work->mode);
-
-	p->canint = 1;
-	if(p->flushtag != NOTAG){
-		free(path);
-		return;
-	}
-	/* There is a race here I ignore because there are no locks */
+	DEBUG("\topen: %s %d\n", path, work->mode);
 	f->fid = open(path, work->mode);
 	free(path);
-	p->canint = 0;
 	if(f->fid < 0 || (d = dirfstat(f->fid)) == nil) {
 	Error:
 		errstr(err, sizeof err);
@@ -564,8 +549,9 @@
 			goto Error;
 	}
 
-	DEBUG(DFD, "\topen: fd %d\n", f->fid);
+	DEBUG("\topen: fd %d\n", f->fid);
 	f->mode = work->mode;
+	f->offset = 0;
 	rhdr.iounit = getiounit(f->fid);
 	rhdr.qid = f->f->qid;
 	reply(work, &rhdr, 0);
@@ -582,22 +568,20 @@
 	work = &p->work;
 
 	f = getfid(work->fid);
-	if(f == 0) {
+	if(f == nil) {
 		reply(work, &rhdr, Ebadfid);
 		return;
 	}
 
 	n = (work->count > messagesize-IOHDRSZ) ? messagesize-IOHDRSZ : work->count;
-	p->canint = 1;
-	if(p->flushtag != NOTAG)
-		return;
 	data = malloc(n);
-	if(data == nil)
-		fatal(Enomem);
+	if(data == nil) {
+		reply(work, &rhdr, Enomem);
+		return;
+	}
 
 	/* can't just call pread, since directories must update the offset */
 	r = pread(f->fid, data, n, work->offset);
-	p->canint = 0;
 	if(r < 0) {
 		free(data);
 		errstr(err, sizeof err);
@@ -604,9 +588,8 @@
 		reply(work, &rhdr, err);
 		return;
 	}
+	DEBUG("\tread: fd=%d %d bytes\n", f->fid, r);
 
-	DEBUG(DFD, "\tread: fd=%d %d bytes\n", f->fid, r);
-
 	rhdr.data = data;
 	rhdr.count = r;
 	reply(work, &rhdr, 0);
@@ -624,17 +607,13 @@
 	work = &p->work;
 
 	f = getfid(work->fid);
-	if(f == 0) {
+	if(f == nil) {
 		reply(work, &rhdr, Ebadfid);
 		return;
 	}
 
 	n = (work->count > messagesize-IOHDRSZ) ? messagesize-IOHDRSZ : work->count;
-	p->canint = 1;
-	if(p->flushtag != NOTAG)
-		return;
 	n = pwrite(f->fid, work->data, n, work->offset);
-	p->canint = 0;
 	if(n < 0) {
 		errstr(err, sizeof err);
 		reply(work, &rhdr, err);
@@ -641,7 +620,7 @@
 		return;
 	}
 
-	DEBUG(DFD, "\twrite: %d bytes fd=%d\n", n, f->fid);
+	DEBUG("\twrite: %d bytes fd=%d\n", n, f->fid);
 
 	rhdr.count = n;
 	reply(work, &rhdr, 0);
@@ -652,18 +631,4 @@
 {
 	USED(f);
 	fatal("reopen");
-}
-
-void
-flushaction(void *a, char *cause)
-{
-	USED(a);
-	if(strncmp(cause, "sys:", 4) == 0 && !strstr(cause, "pipe")) {
-		fprint(2, "exportsrv: note: %s\n", cause);
-		exits("noted");
-	}
-	if(strncmp(cause, "kill", 4) == 0)
-		noted(NDFLT);
-
-	noted(NCONT);
 }
--- /dev/null
+++ b/exportfs/io.c
@@ -1,0 +1,490 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#define Extern
+#include "exportfs.h"
+
+#define QIDPATH	((1LL<<48)-1)
+vlong newqid = 0;
+
+void (*fcalls[])(Fsrpc*) =
+{
+	[Tversion]	Xversion,
+	[Tauth]	Xauth,
+	[Tflush]	Xflush,
+	[Tattach]	Xattach,
+	[Twalk]		Xwalk,
+	[Topen]		slave,
+	[Tcreate]	Xcreate,
+	[Tclunk]	Xclunk,
+	[Tread]		slave,
+	[Twrite]	slave,
+	[Tremove]	Xremove,
+	[Tstat]		Xstat,
+	[Twstat]	Xwstat,
+};
+
+/* accounting and debugging counters */
+int	filecnt;
+int	freecnt;
+int	qidcnt;
+int	qfreecnt;
+int	ncollision;
+
+static int netfd[2];
+
+/*
+ * Start serving file requests from the network
+ */
+void
+io(int rfd, int wfd)
+{
+	Fsrpc *r;
+	int n;
+
+	netfd[0] = rfd;
+	netfd[1] = wfd;
+
+	for(;;) {
+		r = getsbuf();
+		n = read9pmsg(netfd[0], r->buf, messagesize);
+		if(n <= 0)
+			fatal(nil);
+		if(convM2S(r->buf, n, &r->work) != n)
+			fatal("convM2S format error");
+
+		DEBUG("%F\n", &r->work);
+		(fcalls[r->work.type])(r);
+	}
+}
+
+void
+reply(Fcall *r, Fcall *t, char *err)
+{
+	uchar *data;
+	int n;
+
+	t->tag = r->tag;
+	t->fid = r->fid;
+	if(err != nil) {
+		t->type = Rerror;
+		t->ename = err;
+	}
+	else 
+		t->type = r->type + 1;
+
+	DEBUG("\t%F\n", t);
+
+	data = malloc(messagesize);	/* not mallocz; no need to clear */
+	if(data == nil)
+		fatal(Enomem);
+	n = convS2M(t, data, messagesize);
+	if(write(netfd[1], data, n) != n){
+		/* not fatal, might have got a note due to flush */
+		fprint(2, "exportfs: short write in reply: %r\n");
+	}
+	free(data);
+}
+
+void
+mounterror(char *err)
+{
+	Fsrpc *r;
+	int n;
+
+	r = getsbuf();
+	r->work.tag = NOTAG;
+	r->work.fid = NOFID;
+	r->work.type = Rerror;
+	r->work.ename = err;
+	n = convS2M(&r->work, r->buf, messagesize);
+	write(netfd[1], r->buf, n);
+	exits(err);
+}
+
+Fid *
+getfid(int nr)
+{
+	Fid *f;
+
+	for(f = fidhash(nr); f != nil; f = f->next)
+		if(f->nr == nr)
+			return f;
+
+	return nil;
+}
+
+int
+freefid(int nr)
+{
+	Fid *f, **l;
+	char buf[128];
+
+	l = &fidhash(nr);
+	for(f = *l; f != nil; f = f->next) {
+		if(f->nr == nr) {
+			if(f->mid != -1) {
+				snprint(buf, sizeof(buf), "/mnt/exportfs/%d", f->mid);
+				unmount(0, buf);
+			}
+			if(f->f != nil) {
+				freefile(f->f);
+				f->f = nil;
+			}
+			if(f->dir != nil){
+				free(f->dir);
+				f->dir = nil;
+			}
+			*l = f->next;
+			f->next = fidfree;
+			fidfree = f;
+			return 1;
+		}
+		l = &f->next;
+	}
+
+	return 0;	
+}
+
+Fid *
+newfid(int nr)
+{
+	Fid *new, **l;
+	int i;
+
+	l = &fidhash(nr);
+	for(new = *l; new != nil; new = new->next)
+		if(new->nr == nr)
+			return nil;
+
+	if(fidfree == nil) {
+		fidfree = emallocz(sizeof(Fid) * Fidchunk);
+
+		for(i = 0; i < Fidchunk-1; i++)
+			fidfree[i].next = &fidfree[i+1];
+
+		fidfree[Fidchunk-1].next = nil;
+	}
+
+	new = fidfree;
+	fidfree = new->next;
+
+	memset(new, 0, sizeof(Fid));
+	new->next = *l;
+	*l = new;
+	new->nr = nr;
+	new->fid = -1;
+	new->mid = -1;
+
+	return new;	
+}
+
+static struct {
+	Lock	lock;
+	Fsrpc	*free;
+
+	/* statistics */
+	int	nalloc;
+	int	nfree;
+}	sbufalloc;
+
+Fsrpc *
+getsbuf(void)
+{
+	Fsrpc *w;
+
+	lock(&sbufalloc.lock);
+	w = sbufalloc.free;
+	if(w != nil){
+		sbufalloc.free = w->next;
+		w->next = nil;
+		sbufalloc.nfree--;
+		unlock(&sbufalloc.lock);
+	} else {
+		sbufalloc.nalloc++;
+		unlock(&sbufalloc.lock);
+		w = emallocz(sizeof(*w) + messagesize);
+	}
+	w->flushtag = NOTAG;
+	return w;
+}
+
+void
+putsbuf(Fsrpc *w)
+{
+	w->flushtag = NOTAG;
+	lock(&sbufalloc.lock);
+	w->next = sbufalloc.free;
+	sbufalloc.free = w;
+	sbufalloc.nfree++;
+	unlock(&sbufalloc.lock);
+}
+
+void
+freefile(File *f)
+{
+	File *parent, *child;
+
+	while(--f->ref == 0){
+		freecnt++;
+		DEBUG("free %s\n", f->name);
+		/* delete from parent */
+		parent = f->parent;
+		if(parent->child == f)
+			parent->child = f->childlist;
+		else{
+			for(child = parent->child; child->childlist != f; child = child->childlist) {
+				if(child->childlist == nil)
+					fatal("bad child list");
+			}
+			child->childlist = f->childlist;
+		}
+		freeqid(f->qidt);
+		free(f->name);
+		free(f);
+		f = parent;
+	}
+}
+
+File *
+file(File *parent, char *name)
+{
+	Dir *dir;
+	char *path;
+	File *f;
+
+	DEBUG("\tfile: 0x%p %s name %s\n", parent, parent->name, name);
+
+	path = makepath(parent, name);
+	dir = dirstat(path);
+	free(path);
+	if(dir == nil)
+		return nil;
+
+	for(f = parent->child; f != nil; f = f->childlist)
+		if(strcmp(name, f->name) == 0)
+			break;
+
+	if(f == nil){
+		f = emallocz(sizeof(File));
+		f->name = estrdup(name);
+
+		f->parent = parent;
+		f->childlist = parent->child;
+		parent->child = f;
+		parent->ref++;
+		f->ref = 0;
+		filecnt++;
+	}
+	f->ref++;
+	f->qid.type = dir->qid.type;
+	f->qid.vers = dir->qid.vers;
+	f->qidt = uniqueqid(dir);
+	f->qid.path = f->qidt->uniqpath;
+
+	f->inval = 0;
+
+	free(dir);
+
+	return f;
+}
+
+void
+initroot(void)
+{
+	Dir *dir;
+
+	root = emallocz(sizeof(File));
+	root->name = estrdup(".");
+
+	dir = dirstat(root->name);
+	if(dir == nil)
+		fatal("root stat");
+
+	root->ref = 1;
+	root->qid.vers = dir->qid.vers;
+	root->qidt = uniqueqid(dir);
+	root->qid.path = root->qidt->uniqpath;
+	root->qid.type = QTDIR;
+	free(dir);
+
+	psmpt = emallocz(sizeof(File));
+	psmpt->name = estrdup("/");
+
+	dir = dirstat(psmpt->name);
+	if(dir == nil)
+		return;
+
+	psmpt->ref = 1;
+	psmpt->qid.vers = dir->qid.vers;
+	psmpt->qidt = uniqueqid(dir);
+	psmpt->qid.path = psmpt->qidt->uniqpath;
+	free(dir);
+
+	psmpt = file(psmpt, "mnt");
+	if(psmpt == nil)
+		return;
+	psmpt = file(psmpt, "exportfs");
+}
+
+char*
+makepath(File *p, char *name)
+{
+	int i, n;
+	char *c, *s, *path, *seg[256];
+
+	seg[0] = name;
+	n = strlen(name)+2;
+	for(i = 1; i < 256 && p; i++, p = p->parent){
+		seg[i] = p->name;
+		n += strlen(p->name)+1;
+	}
+	path = emallocz(n);
+	s = path;
+
+	while(i--) {
+		for(c = seg[i]; *c; c++)
+			*s++ = *c;
+		*s++ = '/';
+	}
+	while(s[-1] == '/')
+		s--;
+	*s = '\0';
+
+	return path;
+}
+
+int
+qidhash(vlong path)
+{
+	int h, n;
+
+	h = 0;
+	for(n=0; n<64; n+=Nqidbits){
+		h ^= path;
+		path >>= Nqidbits;
+	}
+	return h & (Nqidtab-1);
+}
+
+void
+freeqid(Qidtab *q)
+{
+	ulong h;
+	Qidtab *l;
+
+	if(--q->ref)
+		return;
+	qfreecnt++;
+	h = qidhash(q->path);
+	if(qidtab[h] == q)
+		qidtab[h] = q->next;
+	else{
+		for(l=qidtab[h]; l->next!=q; l=l->next)
+			if(l->next == nil)
+				fatal("bad qid list");
+		l->next = q->next;
+	}
+	free(q);
+}
+
+Qidtab*
+qidlookup(Dir *d)
+{
+	ulong h;
+	Qidtab *q;
+
+	h = qidhash(d->qid.path);
+	for(q=qidtab[h]; q!=nil; q=q->next)
+		if(q->type==d->type && q->dev==d->dev && q->path==d->qid.path)
+			return q;
+	return nil;
+}
+
+int
+qidexists(vlong path)
+{
+	int h;
+	Qidtab *q;
+
+	for(h=0; h<Nqidtab; h++)
+		for(q=qidtab[h]; q!=nil; q=q->next)
+			if(q->uniqpath == path)
+				return 1;
+	return 0;
+}
+
+Qidtab*
+uniqueqid(Dir *d)
+{
+	ulong h;
+	vlong path;
+	Qidtab *q;
+
+	q = qidlookup(d);
+	if(q != nil){
+		q->ref++;
+		return q;
+	}
+	path = d->qid.path;
+	while(qidexists(path)){
+		DEBUG("collision on %s\n", d->name);
+		/* collision: find a new one */
+		ncollision++;
+		path &= QIDPATH;
+		++newqid;
+		if(newqid >= (1<<16)){
+			DEBUG("collision wraparound\n");
+			newqid = 1;
+		}
+		path |= newqid<<48;
+		DEBUG("assign qid %.16llux\n", path);
+	}
+	qidcnt++;
+	q = emallocz(sizeof(Qidtab));
+	q->ref = 1;
+	q->type = d->type;
+	q->dev = d->dev;
+	q->path = d->qid.path;
+	q->uniqpath = path;
+	h = qidhash(d->qid.path);
+	q->next = qidtab[h];
+	qidtab[h] = q;
+	return q;
+}
+
+void
+fatal(char *s, ...)
+{
+	char buf[ERRMAX];
+	va_list arg;
+	Proc *m;
+
+	if(s != nil) {
+		va_start(arg, s);
+		vsnprint(buf, ERRMAX, s, arg);
+		va_end(arg);
+	}
+
+	/* Clear away the slave children */
+	for(m = Proclist; m != nil; m = m->next)
+		kprocint(m->kp);
+
+	if(s != nil) {
+		DEBUG("%s\n", buf);
+		sysfatal("%s", buf);	/* caution: buf could contain '%' */
+	} else
+		exits(nil);
+}
+
+void*
+emallocz(uint n)
+{
+	void *p;
+
+	p = mallocz(n, 1);
+	if(p == nil)
+		fatal(Enomem);
+	setmalloctag(p, getcallerpc(&n));
+	return p;
+}
--