xref: /plan9/sys/src/cmd/plumb/fsys.c (revision da091934ad5a991038edbfec621ba4743b1dcd18)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <bio.h>
47dd7cddfSDavid du Colombier #include <regexp.h>
57dd7cddfSDavid du Colombier #include <thread.h>
67dd7cddfSDavid du Colombier #include <auth.h>
77dd7cddfSDavid du Colombier #include <fcall.h>
87dd7cddfSDavid du Colombier #include <plumb.h>
97dd7cddfSDavid du Colombier #include "plumber.h"
107dd7cddfSDavid du Colombier 
117dd7cddfSDavid du Colombier enum
127dd7cddfSDavid du Colombier {
13*da091934SDavid du Colombier 	Stack = 16*1024
147dd7cddfSDavid du Colombier };
157dd7cddfSDavid du Colombier 
167dd7cddfSDavid du Colombier typedef struct Dirtab Dirtab;
177dd7cddfSDavid du Colombier typedef struct Fid Fid;
187dd7cddfSDavid du Colombier typedef struct Holdq Holdq;
197dd7cddfSDavid du Colombier typedef struct Readreq Readreq;
207dd7cddfSDavid du Colombier typedef struct Sendreq Sendreq;
217dd7cddfSDavid du Colombier 
227dd7cddfSDavid du Colombier struct Dirtab
237dd7cddfSDavid du Colombier {
247dd7cddfSDavid du Colombier 	char		*name;
259a747e4fSDavid du Colombier 	uchar	type;
267dd7cddfSDavid du Colombier 	uint		qid;
277dd7cddfSDavid du Colombier 	uint		perm;
287dd7cddfSDavid du Colombier 	int		nopen;		/* #fids open on this port */
297dd7cddfSDavid du Colombier 	Fid		*fopen;
307dd7cddfSDavid du Colombier 	Holdq	*holdq;
317dd7cddfSDavid du Colombier 	Readreq	*readq;
327dd7cddfSDavid du Colombier 	Sendreq	*sendq;
337dd7cddfSDavid du Colombier };
347dd7cddfSDavid du Colombier 
357dd7cddfSDavid du Colombier struct Fid
367dd7cddfSDavid du Colombier {
377dd7cddfSDavid du Colombier 	int		fid;
387dd7cddfSDavid du Colombier 	int		busy;
397dd7cddfSDavid du Colombier 	int		open;
407dd7cddfSDavid du Colombier 	int		mode;
417dd7cddfSDavid du Colombier 	Qid		qid;
427dd7cddfSDavid du Colombier 	Dirtab	*dir;
437dd7cddfSDavid du Colombier 	long		offset;		/* zeroed at beginning of each message, read or write */
447dd7cddfSDavid du Colombier 	char		*writebuf;		/* partial message written so far; offset tells how much */
457dd7cddfSDavid du Colombier 	Fid		*next;
467dd7cddfSDavid du Colombier 	Fid		*nextopen;
477dd7cddfSDavid du Colombier };
487dd7cddfSDavid du Colombier 
497dd7cddfSDavid du Colombier struct Readreq
507dd7cddfSDavid du Colombier {
517dd7cddfSDavid du Colombier 	Fid		*fid;
527dd7cddfSDavid du Colombier 	Fcall		*fcall;
539a747e4fSDavid du Colombier 	uchar	*buf;
547dd7cddfSDavid du Colombier 	Readreq	*next;
557dd7cddfSDavid du Colombier };
567dd7cddfSDavid du Colombier 
577dd7cddfSDavid du Colombier struct Sendreq
587dd7cddfSDavid du Colombier {
597dd7cddfSDavid du Colombier 	int			nfid;		/* number of fids that should receive this message */
607dd7cddfSDavid du Colombier 	int			nleft;		/* number left that haven't received it */
617dd7cddfSDavid du Colombier 	Fid			**fid;	/* fid[nfid] */
627dd7cddfSDavid du Colombier 	Plumbmsg	*msg;
637dd7cddfSDavid du Colombier 	char			*pack;	/* plumbpack()ed message */
647dd7cddfSDavid du Colombier 	int			npack;	/* length of pack */
657dd7cddfSDavid du Colombier 	Sendreq		*next;
667dd7cddfSDavid du Colombier };
677dd7cddfSDavid du Colombier 
687dd7cddfSDavid du Colombier struct Holdq
697dd7cddfSDavid du Colombier {
707dd7cddfSDavid du Colombier 	Plumbmsg	*msg;
717dd7cddfSDavid du Colombier 	Holdq		*next;
727dd7cddfSDavid du Colombier };
737dd7cddfSDavid du Colombier 
747dd7cddfSDavid du Colombier struct	/* needed because incref() doesn't return value */
757dd7cddfSDavid du Colombier {
767dd7cddfSDavid du Colombier 	Lock;
777dd7cddfSDavid du Colombier 	int			ref;
787dd7cddfSDavid du Colombier } rulesref;
797dd7cddfSDavid du Colombier 
807dd7cddfSDavid du Colombier enum
817dd7cddfSDavid du Colombier {
827dd7cddfSDavid du Colombier 	DEBUG	= 0,
837dd7cddfSDavid du Colombier 	NDIR	= 50,
847dd7cddfSDavid du Colombier 	Nhash	= 16,
857dd7cddfSDavid du Colombier 
867dd7cddfSDavid du Colombier 	Qdir		= 0,
877dd7cddfSDavid du Colombier 	Qrules	= 1,
887dd7cddfSDavid du Colombier 	Qsend	= 2,
897dd7cddfSDavid du Colombier 	Qport	= 3,
907dd7cddfSDavid du Colombier 	NQID	= Qport
917dd7cddfSDavid du Colombier };
927dd7cddfSDavid du Colombier 
937dd7cddfSDavid du Colombier static Dirtab dir[NDIR] =
947dd7cddfSDavid du Colombier {
959a747e4fSDavid du Colombier 	{ ".",			QTDIR,	Qdir,			0500|DMDIR },
969a747e4fSDavid du Colombier 	{ "rules",		QTFILE,	Qrules,		0600 },
979a747e4fSDavid du Colombier 	{ "send",		QTFILE,	Qsend,		0200 },
987dd7cddfSDavid du Colombier };
997dd7cddfSDavid du Colombier static int	ndir = NQID;
1007dd7cddfSDavid du Colombier 
1017dd7cddfSDavid du Colombier static int		srvfd;
1029a747e4fSDavid du Colombier static int		srvclosefd;			/* rock for end of pipe to close */
1037dd7cddfSDavid du Colombier static int		clockfd;
1047dd7cddfSDavid du Colombier static int		clock;
1057dd7cddfSDavid du Colombier static Fid		*fids[Nhash];
1067dd7cddfSDavid du Colombier static QLock	readlock;
1077dd7cddfSDavid du Colombier static QLock	queue;
1087dd7cddfSDavid du Colombier static char	srvfile[128];
1099a747e4fSDavid du Colombier static int		messagesize = 8192+IOHDRSZ;	/* good start */
1107dd7cddfSDavid du Colombier 
1117dd7cddfSDavid du Colombier static void	fsysproc(void*);
1129a747e4fSDavid du Colombier static void fsysrespond(Fcall*, uchar*, char*);
1137dd7cddfSDavid du Colombier static Fid*	newfid(int);
1147dd7cddfSDavid du Colombier 
1159a747e4fSDavid du Colombier static Fcall* fsysflush(Fcall*, uchar*, Fid*);
1169a747e4fSDavid du Colombier static Fcall* fsysversion(Fcall*, uchar*, Fid*);
1179a747e4fSDavid du Colombier static Fcall* fsysauth(Fcall*, uchar*, Fid*);
1189a747e4fSDavid du Colombier static Fcall* fsysattach(Fcall*, uchar*, Fid*);
1199a747e4fSDavid du Colombier static Fcall* fsyswalk(Fcall*, uchar*, Fid*);
1209a747e4fSDavid du Colombier static Fcall* fsysopen(Fcall*, uchar*, Fid*);
1219a747e4fSDavid du Colombier static Fcall* fsyscreate(Fcall*, uchar*, Fid*);
1229a747e4fSDavid du Colombier static Fcall* fsysread(Fcall*, uchar*, Fid*);
1239a747e4fSDavid du Colombier static Fcall* fsyswrite(Fcall*, uchar*, Fid*);
1249a747e4fSDavid du Colombier static Fcall* fsysclunk(Fcall*, uchar*, Fid*);
1259a747e4fSDavid du Colombier static Fcall* fsysremove(Fcall*, uchar*, Fid*);
1269a747e4fSDavid du Colombier static Fcall* fsysstat(Fcall*, uchar*, Fid*);
1279a747e4fSDavid du Colombier static Fcall* fsyswstat(Fcall*, uchar*, Fid*);
1287dd7cddfSDavid du Colombier 
1299a747e4fSDavid du Colombier Fcall* 	(*fcall[Tmax])(Fcall*, uchar*, Fid*) =
1307dd7cddfSDavid du Colombier {
1317dd7cddfSDavid du Colombier 	[Tflush]	= fsysflush,
1329a747e4fSDavid du Colombier 	[Tversion]	= fsysversion,
1339a747e4fSDavid du Colombier 	[Tauth]	= fsysauth,
1347dd7cddfSDavid du Colombier 	[Tattach]	= fsysattach,
1357dd7cddfSDavid du Colombier 	[Twalk]	= fsyswalk,
1367dd7cddfSDavid du Colombier 	[Topen]	= fsysopen,
1377dd7cddfSDavid du Colombier 	[Tcreate]	= fsyscreate,
1387dd7cddfSDavid du Colombier 	[Tread]	= fsysread,
1397dd7cddfSDavid du Colombier 	[Twrite]	= fsyswrite,
1407dd7cddfSDavid du Colombier 	[Tclunk]	= fsysclunk,
1417dd7cddfSDavid du Colombier 	[Tremove]= fsysremove,
1427dd7cddfSDavid du Colombier 	[Tstat]	= fsysstat,
1437dd7cddfSDavid du Colombier 	[Twstat]	= fsyswstat,
1447dd7cddfSDavid du Colombier };
1457dd7cddfSDavid du Colombier 
1467dd7cddfSDavid du Colombier char	Ebadfcall[] =	"bad fcall type";
1477dd7cddfSDavid du Colombier char	Eperm[] = 	"permission denied";
1487dd7cddfSDavid du Colombier char	Enomem[] =	"malloc failed for buffer";
1497dd7cddfSDavid du Colombier char	Enotdir[] =	"not a directory";
1507dd7cddfSDavid du Colombier char	Enoexist[] =	"plumb file does not exist";
1517dd7cddfSDavid du Colombier char	Eisdir[] =		"file is a directory";
1527dd7cddfSDavid du Colombier char	Ebadmsg[] =	"bad plumb message format";
1537dd7cddfSDavid du Colombier char Enosuchport[] ="no such plumb port";
1547dd7cddfSDavid du Colombier char Enoport[] =	"couldn't find destination for message";
1557dd7cddfSDavid du Colombier char	Einuse[] = 	"file already open";
1567dd7cddfSDavid du Colombier 
1577dd7cddfSDavid du Colombier /*
1587dd7cddfSDavid du Colombier  * Add new port.  A no-op if port already exists or is the null string
1597dd7cddfSDavid du Colombier  */
1607dd7cddfSDavid du Colombier void
addport(char * port)1617dd7cddfSDavid du Colombier addport(char *port)
1627dd7cddfSDavid du Colombier {
1637dd7cddfSDavid du Colombier 	int i;
1647dd7cddfSDavid du Colombier 
1657dd7cddfSDavid du Colombier 	if(port == nil)
1667dd7cddfSDavid du Colombier 		return;
1677dd7cddfSDavid du Colombier 	for(i=NQID; i<ndir; i++)
1687dd7cddfSDavid du Colombier 		if(strcmp(port, dir[i].name) == 0)
1697dd7cddfSDavid du Colombier 			return;
1707dd7cddfSDavid du Colombier 	if(i == NDIR){
1717dd7cddfSDavid du Colombier 		fprint(2, "plumb: too many ports; max %d\n", NDIR);
1727dd7cddfSDavid du Colombier 		return;
1737dd7cddfSDavid du Colombier 	}
1747dd7cddfSDavid du Colombier 	ndir++;
1757dd7cddfSDavid du Colombier 	dir[i].name = estrdup(port);
1767dd7cddfSDavid du Colombier 	dir[i].qid = i;
1777dd7cddfSDavid du Colombier 	dir[i].perm = 0400;
1787dd7cddfSDavid du Colombier 	nports++;
1797dd7cddfSDavid du Colombier 	ports = erealloc(ports, nports*sizeof(char*));
1807dd7cddfSDavid du Colombier 	ports[nports-1] = dir[i].name;
1817dd7cddfSDavid du Colombier }
1827dd7cddfSDavid du Colombier 
1837dd7cddfSDavid du Colombier static ulong
getclock(void)1847dd7cddfSDavid du Colombier getclock(void)
1857dd7cddfSDavid du Colombier {
1867dd7cddfSDavid du Colombier 	char buf[32];
1877dd7cddfSDavid du Colombier 
1887dd7cddfSDavid du Colombier 	seek(clockfd, 0, 0);
1897dd7cddfSDavid du Colombier 	read(clockfd, buf, sizeof buf);
1907dd7cddfSDavid du Colombier 	return atoi(buf);
1917dd7cddfSDavid du Colombier }
1927dd7cddfSDavid du Colombier 
1937dd7cddfSDavid du Colombier void
startfsys(void)1949a747e4fSDavid du Colombier startfsys(void)
1957dd7cddfSDavid du Colombier {
1967dd7cddfSDavid du Colombier 	int p[2], fd;
1977dd7cddfSDavid du Colombier 
1989a747e4fSDavid du Colombier 	fmtinstall('F', fcallfmt);
1997dd7cddfSDavid du Colombier 	clockfd = open("/dev/time", OREAD|OCEXEC);
2007dd7cddfSDavid du Colombier 	clock = getclock();
2017dd7cddfSDavid du Colombier 	if(pipe(p) < 0)
2027dd7cddfSDavid du Colombier 		error("can't create pipe: %r");
2037dd7cddfSDavid du Colombier 	/* 0 will be server end, 1 will be client end */
2047dd7cddfSDavid du Colombier 	srvfd = p[0];
2059a747e4fSDavid du Colombier 	srvclosefd = p[1];
2067dd7cddfSDavid du Colombier 	sprint(srvfile, "/srv/plumb.%s.%d", user, getpid());
2077dd7cddfSDavid du Colombier 	if(putenv("plumbsrv", srvfile) < 0)
2087dd7cddfSDavid du Colombier 		error("can't write $plumbsrv: %r");
2097dd7cddfSDavid du Colombier 	fd = create(srvfile, OWRITE|OCEXEC|ORCLOSE, 0600);
2107dd7cddfSDavid du Colombier 	if(fd < 0)
2117dd7cddfSDavid du Colombier 		error("can't create /srv file: %r");
2129a747e4fSDavid du Colombier 	if(fprint(fd, "%d", p[1]) <= 0)
2137dd7cddfSDavid du Colombier 		error("can't write /srv/file: %r");
2147dd7cddfSDavid du Colombier 	/* leave fd open; ORCLOSE will take care of it */
2157dd7cddfSDavid du Colombier 
2169a747e4fSDavid du Colombier 	procrfork(fsysproc, nil, Stack, RFFDG);
2177dd7cddfSDavid du Colombier 
2187dd7cddfSDavid du Colombier 	close(p[0]);
2199a747e4fSDavid du Colombier 	if(mount(p[1], -1, "/mnt/plumb", MREPL, "") < 0)
2207dd7cddfSDavid du Colombier 		error("can't mount /mnt/plumb: %r");
2217dd7cddfSDavid du Colombier 	close(p[1]);
2227dd7cddfSDavid du Colombier }
2237dd7cddfSDavid du Colombier 
2247dd7cddfSDavid du Colombier static void
fsysproc(void *)2257dd7cddfSDavid du Colombier fsysproc(void*)
2267dd7cddfSDavid du Colombier {
2277dd7cddfSDavid du Colombier 	int n;
2287dd7cddfSDavid du Colombier 	Fcall *t;
2297dd7cddfSDavid du Colombier 	Fid *f;
2309a747e4fSDavid du Colombier 	uchar *buf;
2317dd7cddfSDavid du Colombier 
2329a747e4fSDavid du Colombier 	close(srvclosefd);
2339a747e4fSDavid du Colombier 	srvclosefd = -1;
2347dd7cddfSDavid du Colombier 	t = nil;
2357dd7cddfSDavid du Colombier 	for(;;){
2369a747e4fSDavid du Colombier 		buf = malloc(messagesize);	/* avoid memset of emalloc */
2377dd7cddfSDavid du Colombier 		if(buf == nil)
2387dd7cddfSDavid du Colombier 			error("malloc failed: %r");
2397dd7cddfSDavid du Colombier 		qlock(&readlock);
2409a747e4fSDavid du Colombier 		n = read9pmsg(srvfd, buf, messagesize);
2419a747e4fSDavid du Colombier 		if(n <= 0){
2429a747e4fSDavid du Colombier 			if(n < 0)
2437dd7cddfSDavid du Colombier 				error("i/o error on server channel");
2449a747e4fSDavid du Colombier 			threadexitsall("unmounted");
2459a747e4fSDavid du Colombier 		}
2467dd7cddfSDavid du Colombier 		if(readlock.head == nil)	/* no other processes waiting to read; start one */
2477dd7cddfSDavid du Colombier 			proccreate(fsysproc, nil, Stack);
2487dd7cddfSDavid du Colombier 		qunlock(&readlock);
2497dd7cddfSDavid du Colombier 		if(t == nil)
2507dd7cddfSDavid du Colombier 			t = emalloc(sizeof(Fcall));
2519a747e4fSDavid du Colombier 		if(convM2S(buf, n, t) != n)
2527dd7cddfSDavid du Colombier 			error("convert error in convM2S");
2537dd7cddfSDavid du Colombier 		if(DEBUG)
2549a747e4fSDavid du Colombier 			fprint(2, "<= %F\n", t);
2557dd7cddfSDavid du Colombier 		if(fcall[t->type] == nil)
2567dd7cddfSDavid du Colombier 			fsysrespond(t, buf, Ebadfcall);
2577dd7cddfSDavid du Colombier 		else{
2589a747e4fSDavid du Colombier 			if(t->type==Tversion || t->type==Tauth)
2597dd7cddfSDavid du Colombier 				f = nil;
2607dd7cddfSDavid du Colombier 			else
2617dd7cddfSDavid du Colombier 				f = newfid(t->fid);
2627dd7cddfSDavid du Colombier 			t = (*fcall[t->type])(t, buf, f);
2637dd7cddfSDavid du Colombier 		}
2647dd7cddfSDavid du Colombier 	}
2657dd7cddfSDavid du Colombier }
2667dd7cddfSDavid du Colombier 
2677dd7cddfSDavid du Colombier static void
fsysrespond(Fcall * t,uchar * buf,char * err)2689a747e4fSDavid du Colombier fsysrespond(Fcall *t, uchar *buf, char *err)
2697dd7cddfSDavid du Colombier {
2707dd7cddfSDavid du Colombier 	int n;
2717dd7cddfSDavid du Colombier 
2727dd7cddfSDavid du Colombier 	if(err){
2737dd7cddfSDavid du Colombier 		t->type = Rerror;
2749a747e4fSDavid du Colombier 		t->ename = err;
2757dd7cddfSDavid du Colombier 	}else
2767dd7cddfSDavid du Colombier 		t->type++;
2777dd7cddfSDavid du Colombier 	if(buf == nil)
2789a747e4fSDavid du Colombier 		buf = emalloc(messagesize);
2799a747e4fSDavid du Colombier 	n = convS2M(t, buf, messagesize);
2807dd7cddfSDavid du Colombier 	if(n < 0)
2817dd7cddfSDavid du Colombier 		error("convert error in convS2M");
2827dd7cddfSDavid du Colombier 	if(write(srvfd, buf, n) != n)
2837dd7cddfSDavid du Colombier 		error("write error in respond");
2847dd7cddfSDavid du Colombier 	if(DEBUG)
2859a747e4fSDavid du Colombier 		fprint(2, "=> %F\n", t);
2867dd7cddfSDavid du Colombier 	free(buf);
2877dd7cddfSDavid du Colombier }
2887dd7cddfSDavid du Colombier 
2897dd7cddfSDavid du Colombier static
2907dd7cddfSDavid du Colombier Fid*
newfid(int fid)2917dd7cddfSDavid du Colombier newfid(int fid)
2927dd7cddfSDavid du Colombier {
2937dd7cddfSDavid du Colombier 	Fid *f, *ff, **fh;
2947dd7cddfSDavid du Colombier 
2957dd7cddfSDavid du Colombier 	qlock(&queue);
2967dd7cddfSDavid du Colombier 	ff = nil;
2977dd7cddfSDavid du Colombier 	fh = &fids[fid&(Nhash-1)];
2987dd7cddfSDavid du Colombier 	for(f=*fh; f; f=f->next)
2997dd7cddfSDavid du Colombier 		if(f->fid == fid)
3007dd7cddfSDavid du Colombier 			goto Return;
3017dd7cddfSDavid du Colombier 		else if(ff==nil && !f->busy)
3027dd7cddfSDavid du Colombier 			ff = f;
3037dd7cddfSDavid du Colombier 	if(ff){
3047dd7cddfSDavid du Colombier 		ff->fid = fid;
3057dd7cddfSDavid du Colombier 		f = ff;
3067dd7cddfSDavid du Colombier 		goto Return;
3077dd7cddfSDavid du Colombier 	}
3087dd7cddfSDavid du Colombier 	f = emalloc(sizeof *f);
3097dd7cddfSDavid du Colombier 	f->fid = fid;
3107dd7cddfSDavid du Colombier 	f->next = *fh;
3117dd7cddfSDavid du Colombier 	*fh = f;
3127dd7cddfSDavid du Colombier     Return:
3137dd7cddfSDavid du Colombier 	qunlock(&queue);
3147dd7cddfSDavid du Colombier 	return f;
3157dd7cddfSDavid du Colombier }
3167dd7cddfSDavid du Colombier 
3179a747e4fSDavid du Colombier static uint
dostat(Dirtab * dir,uchar * buf,uint nbuf,uint clock)3189a747e4fSDavid du Colombier dostat(Dirtab *dir, uchar *buf, uint nbuf, uint clock)
3197dd7cddfSDavid du Colombier {
3207dd7cddfSDavid du Colombier 	Dir d;
3217dd7cddfSDavid du Colombier 
3229a747e4fSDavid du Colombier 	d.qid.type = dir->type;
3237dd7cddfSDavid du Colombier 	d.qid.path = dir->qid;
3247dd7cddfSDavid du Colombier 	d.qid.vers = 0;
3257dd7cddfSDavid du Colombier 	d.mode = dir->perm;
3267dd7cddfSDavid du Colombier 	d.length = 0;	/* would be nice to do better */
3279a747e4fSDavid du Colombier 	d.name = dir->name;
3289a747e4fSDavid du Colombier 	d.uid = user;
3299a747e4fSDavid du Colombier 	d.gid = user;
3309a747e4fSDavid du Colombier 	d.muid = user;
3317dd7cddfSDavid du Colombier 	d.atime = clock;
3327dd7cddfSDavid du Colombier 	d.mtime = clock;
3339a747e4fSDavid du Colombier 	return convD2M(&d, buf, nbuf);
3347dd7cddfSDavid du Colombier }
3357dd7cddfSDavid du Colombier 
3367dd7cddfSDavid du Colombier static void
queuesend(Dirtab * d,Plumbmsg * m)3377dd7cddfSDavid du Colombier queuesend(Dirtab *d, Plumbmsg *m)
3387dd7cddfSDavid du Colombier {
3397dd7cddfSDavid du Colombier 	Sendreq *s, *t;
3407dd7cddfSDavid du Colombier 	Fid *f;
3417dd7cddfSDavid du Colombier 	int i;
3427dd7cddfSDavid du Colombier 
3437dd7cddfSDavid du Colombier 	s = emalloc(sizeof(Sendreq));
3447dd7cddfSDavid du Colombier 	s->nfid = d->nopen;
3457dd7cddfSDavid du Colombier 	s->nleft = s->nfid;
3467dd7cddfSDavid du Colombier 	s->fid = emalloc(s->nfid*sizeof(Fid*));
3477dd7cddfSDavid du Colombier 	i = 0;
3487dd7cddfSDavid du Colombier 	/* build array of fids open on this channel */
3497dd7cddfSDavid du Colombier 	for(f=d->fopen; f!=nil; f=f->nextopen)
3507dd7cddfSDavid du Colombier 		s->fid[i++] = f;
3517dd7cddfSDavid du Colombier 	s->msg = m;
3527dd7cddfSDavid du Colombier 	s->next = nil;
3537dd7cddfSDavid du Colombier 	/* link to end of queue; drainqueue() searches in sender order so this implements a FIFO */
3547dd7cddfSDavid du Colombier 	for(t=d->sendq; t!=nil; t=t->next)
3557dd7cddfSDavid du Colombier 		if(t->next == nil)
3567dd7cddfSDavid du Colombier 			break;
3577dd7cddfSDavid du Colombier 	if(t == nil)
3587dd7cddfSDavid du Colombier 		d->sendq = s;
3597dd7cddfSDavid du Colombier 	else
3607dd7cddfSDavid du Colombier 		t->next = s;
3617dd7cddfSDavid du Colombier }
3627dd7cddfSDavid du Colombier 
3637dd7cddfSDavid du Colombier static void
queueread(Dirtab * d,Fcall * t,uchar * buf,Fid * f)3649a747e4fSDavid du Colombier queueread(Dirtab *d, Fcall *t, uchar *buf, Fid *f)
3657dd7cddfSDavid du Colombier {
3667dd7cddfSDavid du Colombier 	Readreq *r;
3677dd7cddfSDavid du Colombier 
3687dd7cddfSDavid du Colombier 	r = emalloc(sizeof(Readreq));
3697dd7cddfSDavid du Colombier 	r->fcall = t;
3707dd7cddfSDavid du Colombier 	r->buf = buf;
3717dd7cddfSDavid du Colombier 	r->fid = f;
3727dd7cddfSDavid du Colombier 	r->next = d->readq;
3737dd7cddfSDavid du Colombier 	d->readq = r;
3747dd7cddfSDavid du Colombier }
3757dd7cddfSDavid du Colombier 
3767dd7cddfSDavid du Colombier static void
drainqueue(Dirtab * d)3777dd7cddfSDavid du Colombier drainqueue(Dirtab *d)
3787dd7cddfSDavid du Colombier {
3797dd7cddfSDavid du Colombier 	Readreq *r, *nextr, *prevr;
3807dd7cddfSDavid du Colombier 	Sendreq *s, *nexts, *prevs;
3817dd7cddfSDavid du Colombier 	int i, n;
3827dd7cddfSDavid du Colombier 
3837dd7cddfSDavid du Colombier 	prevs = nil;
3847dd7cddfSDavid du Colombier 	for(s=d->sendq; s!=nil; s=nexts){
3857dd7cddfSDavid du Colombier 		nexts = s->next;
3867dd7cddfSDavid du Colombier 		for(i=0; i<s->nfid; i++){
3877dd7cddfSDavid du Colombier 			prevr = nil;
3887dd7cddfSDavid du Colombier 			for(r=d->readq; r!=nil; r=nextr){
3897dd7cddfSDavid du Colombier 				nextr = r->next;
3907dd7cddfSDavid du Colombier 				if(r->fid == s->fid[i]){
3917dd7cddfSDavid du Colombier 					/* pack the message if necessary */
3927dd7cddfSDavid du Colombier 					if(s->pack == nil)
3937dd7cddfSDavid du Colombier 						s->pack = plumbpack(s->msg, &s->npack);
3947dd7cddfSDavid du Colombier 					/* exchange the stuff... */
3957dd7cddfSDavid du Colombier 					r->fcall->data = s->pack+r->fid->offset;
3967dd7cddfSDavid du Colombier 					n = s->npack - r->fid->offset;
3979a747e4fSDavid du Colombier 					if(n > messagesize-IOHDRSZ)
3989a747e4fSDavid du Colombier 						n = messagesize-IOHDRSZ;
3997dd7cddfSDavid du Colombier 					if(n > r->fcall->count)
4007dd7cddfSDavid du Colombier 						n = r->fcall->count;
4017dd7cddfSDavid du Colombier 					r->fcall->count = n;
4027dd7cddfSDavid du Colombier 					fsysrespond(r->fcall, r->buf, nil);
4037dd7cddfSDavid du Colombier 					r->fid->offset += n;
4047dd7cddfSDavid du Colombier 					if(r->fid->offset >= s->npack){
4057dd7cddfSDavid du Colombier 						/* message transferred; delete this fid from send queue */
4067dd7cddfSDavid du Colombier 						r->fid->offset = 0;
4077dd7cddfSDavid du Colombier 						s->fid[i] = nil;
4087dd7cddfSDavid du Colombier 						s->nleft--;
4097dd7cddfSDavid du Colombier 					}
4107dd7cddfSDavid du Colombier 					/* delete read request from queue */
4117dd7cddfSDavid du Colombier 					if(prevr)
4127dd7cddfSDavid du Colombier 						prevr->next = r->next;
4137dd7cddfSDavid du Colombier 					else
4147dd7cddfSDavid du Colombier 						d->readq = r->next;
4157dd7cddfSDavid du Colombier 					free(r->fcall);
4167dd7cddfSDavid du Colombier 					free(r);
4177dd7cddfSDavid du Colombier 					break;
4187dd7cddfSDavid du Colombier 				}else
4197dd7cddfSDavid du Colombier 					prevr = r;
4207dd7cddfSDavid du Colombier 			}
4217dd7cddfSDavid du Colombier 		}
4227dd7cddfSDavid du Colombier 		/* if no fids left, delete this send from queue */
4237dd7cddfSDavid du Colombier 		if(s->nleft == 0){
4247dd7cddfSDavid du Colombier 			free(s->fid);
4257dd7cddfSDavid du Colombier 			plumbfree(s->msg);
4267dd7cddfSDavid du Colombier 			free(s->pack);
4277dd7cddfSDavid du Colombier 			if(prevs)
4287dd7cddfSDavid du Colombier 				prevs->next = s->next;
4297dd7cddfSDavid du Colombier 			else
4307dd7cddfSDavid du Colombier 				d->sendq = s->next;
4317dd7cddfSDavid du Colombier 			free(s);
4327dd7cddfSDavid du Colombier 		}else
4337dd7cddfSDavid du Colombier 			prevs = s;
4347dd7cddfSDavid du Colombier 	}
4357dd7cddfSDavid du Colombier }
4367dd7cddfSDavid du Colombier 
4377dd7cddfSDavid du Colombier /* can't flush a send because they are always answered synchronously */
4387dd7cddfSDavid du Colombier static void
flushqueue(Dirtab * d,int oldtag)4397dd7cddfSDavid du Colombier flushqueue(Dirtab *d, int oldtag)
4407dd7cddfSDavid du Colombier {
4417dd7cddfSDavid du Colombier 	Readreq *r, *prevr;
4427dd7cddfSDavid du Colombier 
4437dd7cddfSDavid du Colombier 	prevr = nil;
4447dd7cddfSDavid du Colombier 	for(r=d->readq; r!=nil; r=r->next){
4457dd7cddfSDavid du Colombier 		if(oldtag == r->fcall->tag){
4467dd7cddfSDavid du Colombier 			/* delete read request from queue */
4477dd7cddfSDavid du Colombier 			if(prevr)
4487dd7cddfSDavid du Colombier 				prevr->next = r->next;
4497dd7cddfSDavid du Colombier 			else
4507dd7cddfSDavid du Colombier 				d->readq = r->next;
4517dd7cddfSDavid du Colombier 			free(r->fcall);
45280ee5cbfSDavid du Colombier 			free(r->buf);
4537dd7cddfSDavid du Colombier 			free(r);
4547dd7cddfSDavid du Colombier 			return;
4557dd7cddfSDavid du Colombier 		}
4567dd7cddfSDavid du Colombier 		prevr = r;
4577dd7cddfSDavid du Colombier 	}
4587dd7cddfSDavid du Colombier }
4597dd7cddfSDavid du Colombier 
4607dd7cddfSDavid du Colombier /* remove messages awaiting delivery to now-closing fid */
4617dd7cddfSDavid du Colombier static void
removesenders(Dirtab * d,Fid * fid)4627dd7cddfSDavid du Colombier removesenders(Dirtab *d, Fid *fid)
4637dd7cddfSDavid du Colombier {
4647dd7cddfSDavid du Colombier 	Sendreq *s, *nexts, *prevs;
4657dd7cddfSDavid du Colombier 	int i;
4667dd7cddfSDavid du Colombier 
4677dd7cddfSDavid du Colombier 	prevs = nil;
4687dd7cddfSDavid du Colombier 	for(s=d->sendq; s!=nil; s=nexts){
4697dd7cddfSDavid du Colombier 		nexts = s->next;
4707dd7cddfSDavid du Colombier 		for(i=0; i<s->nfid; i++)
4717dd7cddfSDavid du Colombier 			if(fid == s->fid[i]){
4727dd7cddfSDavid du Colombier 				/* delete this fid from send queue */
4737dd7cddfSDavid du Colombier 				s->fid[i] = nil;
4747dd7cddfSDavid du Colombier 				s->nleft--;
4757dd7cddfSDavid du Colombier 				break;
4767dd7cddfSDavid du Colombier 			}
4777dd7cddfSDavid du Colombier 		/* if no fids left, delete this send from queue */
4787dd7cddfSDavid du Colombier 		if(s->nleft == 0){
4797dd7cddfSDavid du Colombier 			free(s->fid);
4807dd7cddfSDavid du Colombier 			plumbfree(s->msg);
4817dd7cddfSDavid du Colombier 			free(s->pack);
4827dd7cddfSDavid du Colombier 			if(prevs)
4837dd7cddfSDavid du Colombier 				prevs->next = s->next;
4847dd7cddfSDavid du Colombier 			else
4857dd7cddfSDavid du Colombier 				d->sendq = s->next;
4867dd7cddfSDavid du Colombier 			free(s);
4877dd7cddfSDavid du Colombier 		}else
4887dd7cddfSDavid du Colombier 			prevs = s;
4897dd7cddfSDavid du Colombier 	}
4907dd7cddfSDavid du Colombier }
4917dd7cddfSDavid du Colombier 
4927dd7cddfSDavid du Colombier static void
hold(Plumbmsg * m,Dirtab * d)4937dd7cddfSDavid du Colombier hold(Plumbmsg *m, Dirtab *d)
4947dd7cddfSDavid du Colombier {
4957dd7cddfSDavid du Colombier 	Holdq *h, *q;
4967dd7cddfSDavid du Colombier 
4977dd7cddfSDavid du Colombier 	h = emalloc(sizeof(Holdq));
4987dd7cddfSDavid du Colombier 	h->msg = m;
4997dd7cddfSDavid du Colombier 	/* add to end of queue */
5007dd7cddfSDavid du Colombier 	if(d->holdq == nil)
5017dd7cddfSDavid du Colombier 		d->holdq = h;
5027dd7cddfSDavid du Colombier 	else{
5037dd7cddfSDavid du Colombier 		for(q=d->holdq; q->next!=nil; q=q->next)
5047dd7cddfSDavid du Colombier 			;
5057dd7cddfSDavid du Colombier 		q->next = h;
5067dd7cddfSDavid du Colombier 	}
5077dd7cddfSDavid du Colombier }
5087dd7cddfSDavid du Colombier 
5097dd7cddfSDavid du Colombier static void
queueheld(Dirtab * d)5107dd7cddfSDavid du Colombier queueheld(Dirtab *d)
5117dd7cddfSDavid du Colombier {
5127dd7cddfSDavid du Colombier 	Holdq *h;
5137dd7cddfSDavid du Colombier 
5147dd7cddfSDavid du Colombier 	while(d->holdq != nil){
5157dd7cddfSDavid du Colombier 		h = d->holdq;
5167dd7cddfSDavid du Colombier 		d->holdq = h->next;
5177dd7cddfSDavid du Colombier 		queuesend(d, h->msg);
5187dd7cddfSDavid du Colombier 		/* no need to drain queue because we know no-one is reading yet */
5197dd7cddfSDavid du Colombier 		free(h);
5207dd7cddfSDavid du Colombier 	}
5217dd7cddfSDavid du Colombier }
5227dd7cddfSDavid du Colombier 
5237dd7cddfSDavid du Colombier static void
dispose(Fcall * t,uchar * buf,Plumbmsg * m,Ruleset * rs,Exec * e)5249a747e4fSDavid du Colombier dispose(Fcall *t, uchar *buf, Plumbmsg *m, Ruleset *rs, Exec *e)
5257dd7cddfSDavid du Colombier {
5267dd7cddfSDavid du Colombier 	int i;
5277dd7cddfSDavid du Colombier 	char *err;
5287dd7cddfSDavid du Colombier 
5297dd7cddfSDavid du Colombier 	qlock(&queue);
5307dd7cddfSDavid du Colombier 	err = nil;
5317dd7cddfSDavid du Colombier 	if(m->dst==nil || m->dst[0]=='\0'){
5327dd7cddfSDavid du Colombier 		err = Enoport;
5337dd7cddfSDavid du Colombier 		if(rs != nil)
5347dd7cddfSDavid du Colombier 			err = startup(rs, e);
5357dd7cddfSDavid du Colombier 		plumbfree(m);
5367dd7cddfSDavid du Colombier 	}else
5377dd7cddfSDavid du Colombier 		for(i=NQID; i<ndir; i++)
5387dd7cddfSDavid du Colombier 			if(strcmp(m->dst, dir[i].name) == 0){
5397dd7cddfSDavid du Colombier 				if(dir[i].nopen == 0){
5407dd7cddfSDavid du Colombier 					err = startup(rs, e);
5417dd7cddfSDavid du Colombier 					if(e!=nil && e->holdforclient)
5427dd7cddfSDavid du Colombier 						hold(m, &dir[i]);
5437dd7cddfSDavid du Colombier 					else
5447dd7cddfSDavid du Colombier 						plumbfree(m);
5457dd7cddfSDavid du Colombier 				}else{
5467dd7cddfSDavid du Colombier 					queuesend(&dir[i], m);
5477dd7cddfSDavid du Colombier 					drainqueue(&dir[i]);
5487dd7cddfSDavid du Colombier 				}
5497dd7cddfSDavid du Colombier 				break;
5507dd7cddfSDavid du Colombier 			}
5517dd7cddfSDavid du Colombier 	freeexec(e);
5527dd7cddfSDavid du Colombier 	qunlock(&queue);
5537dd7cddfSDavid du Colombier 	fsysrespond(t, buf, err);
5547dd7cddfSDavid du Colombier 	free(t);
5557dd7cddfSDavid du Colombier }
5567dd7cddfSDavid du Colombier 
5577dd7cddfSDavid du Colombier static Fcall*
fsysversion(Fcall * t,uchar * buf,Fid *)5589a747e4fSDavid du Colombier fsysversion(Fcall *t, uchar *buf, Fid*)
5597dd7cddfSDavid du Colombier {
5609a747e4fSDavid du Colombier 	if(t->msize < 256){
5619a747e4fSDavid du Colombier 		fsysrespond(t, buf, "version: message size too small");
5629a747e4fSDavid du Colombier 		return t;
5639a747e4fSDavid du Colombier 	}
5649a747e4fSDavid du Colombier 	if(t->msize < messagesize)
5659a747e4fSDavid du Colombier 		messagesize = t->msize;
5669a747e4fSDavid du Colombier 	t->msize = messagesize;
5679a747e4fSDavid du Colombier 	if(strncmp(t->version, "9P2000", 6) != 0){
5689a747e4fSDavid du Colombier 		fsysrespond(t, buf, "unrecognized 9P version");
5699a747e4fSDavid du Colombier 		return t;
5709a747e4fSDavid du Colombier 	}
5719a747e4fSDavid du Colombier 	t->version = "9P2000";
5727dd7cddfSDavid du Colombier 	fsysrespond(t, buf, nil);
5737dd7cddfSDavid du Colombier 	return t;
5747dd7cddfSDavid du Colombier }
5757dd7cddfSDavid du Colombier 
5767dd7cddfSDavid du Colombier static Fcall*
fsysauth(Fcall * t,uchar * buf,Fid *)5779a747e4fSDavid du Colombier fsysauth(Fcall *t, uchar *buf, Fid*)
5787dd7cddfSDavid du Colombier {
5799a747e4fSDavid du Colombier 	fsysrespond(t, buf, "plumber: authentication not required");
5807dd7cddfSDavid du Colombier 	return t;
5817dd7cddfSDavid du Colombier }
5827dd7cddfSDavid du Colombier 
5837dd7cddfSDavid du Colombier static Fcall*
fsysattach(Fcall * t,uchar * buf,Fid * f)5849a747e4fSDavid du Colombier fsysattach(Fcall *t, uchar *buf, Fid *f)
5857dd7cddfSDavid du Colombier {
5867dd7cddfSDavid du Colombier 	Fcall out;
5877dd7cddfSDavid du Colombier 
5887dd7cddfSDavid du Colombier 	if(strcmp(t->uname, user) != 0){
5897dd7cddfSDavid du Colombier 		fsysrespond(&out, buf, Eperm);
5907dd7cddfSDavid du Colombier 		return t;
5917dd7cddfSDavid du Colombier 	}
5927dd7cddfSDavid du Colombier 	f->busy = 1;
5937dd7cddfSDavid du Colombier 	f->open = 0;
5949a747e4fSDavid du Colombier 	f->qid.type = QTDIR;
5959a747e4fSDavid du Colombier 	f->qid.path = Qdir;
5969a747e4fSDavid du Colombier 	f->qid.vers = 0;
5977dd7cddfSDavid du Colombier 	f->dir = dir;
5987dd7cddfSDavid du Colombier 	memset(&out, 0, sizeof(Fcall));
5997dd7cddfSDavid du Colombier 	out.type = t->type;
6007dd7cddfSDavid du Colombier 	out.tag = t->tag;
6017dd7cddfSDavid du Colombier 	out.fid = f->fid;
6027dd7cddfSDavid du Colombier 	out.qid = f->qid;
6037dd7cddfSDavid du Colombier 	fsysrespond(&out, buf, nil);
6047dd7cddfSDavid du Colombier 	return t;
6057dd7cddfSDavid du Colombier }
6067dd7cddfSDavid du Colombier 
6077dd7cddfSDavid du Colombier static Fcall*
fsysflush(Fcall * t,uchar * buf,Fid *)6089a747e4fSDavid du Colombier fsysflush(Fcall *t, uchar *buf, Fid*)
6097dd7cddfSDavid du Colombier {
6107dd7cddfSDavid du Colombier 	int i;
6117dd7cddfSDavid du Colombier 
6127dd7cddfSDavid du Colombier 	qlock(&queue);
6137dd7cddfSDavid du Colombier 	for(i=NQID; i<ndir; i++)
6147dd7cddfSDavid du Colombier 		flushqueue(&dir[i], t->oldtag);
6157dd7cddfSDavid du Colombier 	qunlock(&queue);
6167dd7cddfSDavid du Colombier 	fsysrespond(t, buf, nil);
6177dd7cddfSDavid du Colombier 	return t;
6187dd7cddfSDavid du Colombier }
6197dd7cddfSDavid du Colombier 
6207dd7cddfSDavid du Colombier static Fcall*
fsyswalk(Fcall * t,uchar * buf,Fid * f)6219a747e4fSDavid du Colombier fsyswalk(Fcall *t, uchar *buf, Fid *f)
6227dd7cddfSDavid du Colombier {
6239a747e4fSDavid du Colombier 	Fcall out;
6247dd7cddfSDavid du Colombier 	Fid *nf;
6259a747e4fSDavid du Colombier 	ulong path;
6269a747e4fSDavid du Colombier 	Dirtab *d, *dir;
6279a747e4fSDavid du Colombier 	Qid q;
6289a747e4fSDavid du Colombier 	int i;
6299a747e4fSDavid du Colombier 	uchar type;
6309a747e4fSDavid du Colombier 	char *err;
6317dd7cddfSDavid du Colombier 
6327dd7cddfSDavid du Colombier 	if(f->open){
6339a747e4fSDavid du Colombier 		fsysrespond(t, buf, "clone of an open fid");
6347dd7cddfSDavid du Colombier 		return t;
6357dd7cddfSDavid du Colombier 	}
6369a747e4fSDavid du Colombier 
6379a747e4fSDavid du Colombier 	nf = nil;
6389a747e4fSDavid du Colombier 	if(t->fid  != t->newfid){
6397dd7cddfSDavid du Colombier 		nf = newfid(t->newfid);
6409a747e4fSDavid du Colombier 		if(nf->busy){
6419a747e4fSDavid du Colombier 			fsysrespond(t, buf, "clone to a busy fid");
6429a747e4fSDavid du Colombier 			return t;
6439a747e4fSDavid du Colombier 		}
6447dd7cddfSDavid du Colombier 		nf->busy = 1;
6457dd7cddfSDavid du Colombier 		nf->open = 0;
6467dd7cddfSDavid du Colombier 		nf->dir = f->dir;
6477dd7cddfSDavid du Colombier 		nf->qid = f->qid;
6489a747e4fSDavid du Colombier 		f = nf;	/* walk f */
6497dd7cddfSDavid du Colombier 	}
6507dd7cddfSDavid du Colombier 
6519a747e4fSDavid du Colombier 	out.nwqid = 0;
6529a747e4fSDavid du Colombier 	err = nil;
6539a747e4fSDavid du Colombier 	dir = f->dir;
6549a747e4fSDavid du Colombier 	q = f->qid;
6557dd7cddfSDavid du Colombier 
6569a747e4fSDavid du Colombier 	if(t->nwname > 0){
6579a747e4fSDavid du Colombier 		for(i=0; i<t->nwname; i++){
6589a747e4fSDavid du Colombier 			if((q.type & QTDIR) == 0){
6599a747e4fSDavid du Colombier 				err = Enotdir;
6609a747e4fSDavid du Colombier 				break;
6617dd7cddfSDavid du Colombier 			}
6629a747e4fSDavid du Colombier 			if(strcmp(t->wname[i], "..") == 0){
6639a747e4fSDavid du Colombier 				type = QTDIR;
6649a747e4fSDavid du Colombier 				path = Qdir;
6659a747e4fSDavid du Colombier 	Accept:
6669a747e4fSDavid du Colombier 				q.type = type;
6679a747e4fSDavid du Colombier 				q.vers = 0;
6689a747e4fSDavid du Colombier 				q.path = path;
6699a747e4fSDavid du Colombier 				out.wqid[out.nwqid++] = q;
6709a747e4fSDavid du Colombier 				continue;
6717dd7cddfSDavid du Colombier 			}
6727dd7cddfSDavid du Colombier 			d = dir;
6737dd7cddfSDavid du Colombier 			d++;	/* skip '.' */
6747dd7cddfSDavid du Colombier 			for(; d->name; d++)
6759a747e4fSDavid du Colombier 				if(strcmp(t->wname[i], d->name) == 0){
6769a747e4fSDavid du Colombier 					type = d->type;
6779a747e4fSDavid du Colombier 					path = d->qid;
6789a747e4fSDavid du Colombier 					dir = d;
6799a747e4fSDavid du Colombier 					goto Accept;
6809a747e4fSDavid du Colombier 				}
6819a747e4fSDavid du Colombier 			err = Enoexist;
6829a747e4fSDavid du Colombier 			break;
6839a747e4fSDavid du Colombier 		}
6847dd7cddfSDavid du Colombier 	}
6857dd7cddfSDavid du Colombier 
6869a747e4fSDavid du Colombier 	out.type = t->type;
6879a747e4fSDavid du Colombier 	out.tag = t->tag;
6889a747e4fSDavid du Colombier 	if(err!=nil || out.nwqid<t->nwname){
6899a747e4fSDavid du Colombier 		if(nf)
6909a747e4fSDavid du Colombier 			nf->busy = 0;
6919a747e4fSDavid du Colombier 	}else if(out.nwqid == t->nwname){
6929a747e4fSDavid du Colombier 		f->qid = q;
6939a747e4fSDavid du Colombier 		f->dir = dir;
6949a747e4fSDavid du Colombier 	}
6957dd7cddfSDavid du Colombier 
6969a747e4fSDavid du Colombier 	fsysrespond(&out, buf, err);
6977dd7cddfSDavid du Colombier 	return t;
6987dd7cddfSDavid du Colombier }
6997dd7cddfSDavid du Colombier 
7007dd7cddfSDavid du Colombier static Fcall*
fsysopen(Fcall * t,uchar * buf,Fid * f)7019a747e4fSDavid du Colombier fsysopen(Fcall *t, uchar *buf, Fid *f)
7027dd7cddfSDavid du Colombier {
7039a747e4fSDavid du Colombier 	int m, clearrules, mode;
7047dd7cddfSDavid du Colombier 
7057dd7cddfSDavid du Colombier 	clearrules = 0;
7067dd7cddfSDavid du Colombier 	if(t->mode & OTRUNC){
7077dd7cddfSDavid du Colombier 		if(f->qid.path != Qrules)
7087dd7cddfSDavid du Colombier 			goto Deny;
7097dd7cddfSDavid du Colombier 		clearrules = 1;
7107dd7cddfSDavid du Colombier 	}
7117dd7cddfSDavid du Colombier 	/* can't truncate anything, so just disregard */
7129a747e4fSDavid du Colombier 	mode = t->mode & ~(OTRUNC|OCEXEC);
7137dd7cddfSDavid du Colombier 	/* can't execute or remove anything */
7149a747e4fSDavid du Colombier 	if(mode==OEXEC || (mode&ORCLOSE))
7157dd7cddfSDavid du Colombier 		goto Deny;
7169a747e4fSDavid du Colombier 	switch(mode){
7177dd7cddfSDavid du Colombier 	default:
7187dd7cddfSDavid du Colombier 		goto Deny;
7197dd7cddfSDavid du Colombier 	case OREAD:
7207dd7cddfSDavid du Colombier 		m = 0400;
7217dd7cddfSDavid du Colombier 		break;
7227dd7cddfSDavid du Colombier 	case OWRITE:
7237dd7cddfSDavid du Colombier 		m = 0200;
7247dd7cddfSDavid du Colombier 		break;
7257dd7cddfSDavid du Colombier 	case ORDWR:
7267dd7cddfSDavid du Colombier 		m = 0600;
7277dd7cddfSDavid du Colombier 		break;
7287dd7cddfSDavid du Colombier 	}
7299a747e4fSDavid du Colombier 	if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
7307dd7cddfSDavid du Colombier 		goto Deny;
7319a747e4fSDavid du Colombier 	if(f->qid.path==Qrules && (mode==OWRITE || mode==ORDWR)){
7327dd7cddfSDavid du Colombier 		lock(&rulesref);
7337dd7cddfSDavid du Colombier 		if(rulesref.ref++ != 0){
7347dd7cddfSDavid du Colombier 			rulesref.ref--;
7357dd7cddfSDavid du Colombier 			unlock(&rulesref);
7367dd7cddfSDavid du Colombier 			fsysrespond(t, buf, Einuse);
7377dd7cddfSDavid du Colombier 			return t;
7387dd7cddfSDavid du Colombier 		}
7397dd7cddfSDavid du Colombier 		unlock(&rulesref);
7407dd7cddfSDavid du Colombier 	}
7417dd7cddfSDavid du Colombier 	if(clearrules){
7427dd7cddfSDavid du Colombier 		writerules(nil, 0);
7437dd7cddfSDavid du Colombier 		rules[0] = nil;
7447dd7cddfSDavid du Colombier 	}
7457dd7cddfSDavid du Colombier 	t->qid = f->qid;
7469a747e4fSDavid du Colombier 	t->iounit = 0;
7477dd7cddfSDavid du Colombier 	qlock(&queue);
7489a747e4fSDavid du Colombier 	f->mode = mode;
7497dd7cddfSDavid du Colombier 	f->open = 1;
7507dd7cddfSDavid du Colombier 	f->dir->nopen++;
7517dd7cddfSDavid du Colombier 	f->nextopen = f->dir->fopen;
7527dd7cddfSDavid du Colombier 	f->dir->fopen = f;
7537dd7cddfSDavid du Colombier 	queueheld(f->dir);
7547dd7cddfSDavid du Colombier 	qunlock(&queue);
7557dd7cddfSDavid du Colombier 	fsysrespond(t, buf, nil);
7567dd7cddfSDavid du Colombier 	return t;
7577dd7cddfSDavid du Colombier 
7587dd7cddfSDavid du Colombier     Deny:
7597dd7cddfSDavid du Colombier 	fsysrespond(t, buf, Eperm);
7607dd7cddfSDavid du Colombier 	return t;
7617dd7cddfSDavid du Colombier }
7627dd7cddfSDavid du Colombier 
7637dd7cddfSDavid du Colombier static Fcall*
fsyscreate(Fcall * t,uchar * buf,Fid *)7649a747e4fSDavid du Colombier fsyscreate(Fcall *t, uchar *buf, Fid*)
7657dd7cddfSDavid du Colombier {
7667dd7cddfSDavid du Colombier 	fsysrespond(t, buf, Eperm);
7677dd7cddfSDavid du Colombier 	return t;
7687dd7cddfSDavid du Colombier }
7697dd7cddfSDavid du Colombier 
7707dd7cddfSDavid du Colombier static Fcall*
fsysreadrules(Fcall * t,uchar * buf)7719a747e4fSDavid du Colombier fsysreadrules(Fcall *t, uchar *buf)
7727dd7cddfSDavid du Colombier {
7737dd7cddfSDavid du Colombier 	char *p;
7747dd7cddfSDavid du Colombier 	int n;
7757dd7cddfSDavid du Colombier 
7767dd7cddfSDavid du Colombier 	p = printrules();
7777dd7cddfSDavid du Colombier 	n = strlen(p);
7787dd7cddfSDavid du Colombier 	t->data = p;
7797dd7cddfSDavid du Colombier 	if(t->offset >= n)
7807dd7cddfSDavid du Colombier 		t->count = 0;
7817dd7cddfSDavid du Colombier 	else{
7827dd7cddfSDavid du Colombier 		t->data = p+t->offset;
7837dd7cddfSDavid du Colombier 		if(t->offset+t->count > n)
7847dd7cddfSDavid du Colombier 			t->count = n-t->offset;
7857dd7cddfSDavid du Colombier 	}
7867dd7cddfSDavid du Colombier 	fsysrespond(t, buf, nil);
7877dd7cddfSDavid du Colombier 	free(p);
7887dd7cddfSDavid du Colombier 	return t;
7897dd7cddfSDavid du Colombier }
7907dd7cddfSDavid du Colombier 
7917dd7cddfSDavid du Colombier static Fcall*
fsysread(Fcall * t,uchar * buf,Fid * f)7929a747e4fSDavid du Colombier fsysread(Fcall *t, uchar *buf, Fid *f)
7937dd7cddfSDavid du Colombier {
7949a747e4fSDavid du Colombier 	uchar *b;
7957dd7cddfSDavid du Colombier 	int i, n, o, e;
7969a747e4fSDavid du Colombier 	uint len;
7977dd7cddfSDavid du Colombier 	Dirtab *d;
7989a747e4fSDavid du Colombier 	uint clock;
7997dd7cddfSDavid du Colombier 
8009a747e4fSDavid du Colombier 	if(f->qid.path != Qdir){
8017dd7cddfSDavid du Colombier 		if(f->qid.path == Qrules)
8027dd7cddfSDavid du Colombier 			return fsysreadrules(t, buf);
8037dd7cddfSDavid du Colombier 		/* read from port */
8047dd7cddfSDavid du Colombier 		if(f->qid.path < NQID){
8057dd7cddfSDavid du Colombier 			fsysrespond(t, buf, "internal error: unknown read port");
8067dd7cddfSDavid du Colombier 			return t;
8077dd7cddfSDavid du Colombier 		}
8087dd7cddfSDavid du Colombier 		qlock(&queue);
8097dd7cddfSDavid du Colombier 		queueread(f->dir, t, buf, f);
8107dd7cddfSDavid du Colombier 		drainqueue(f->dir);
8117dd7cddfSDavid du Colombier 		qunlock(&queue);
8127dd7cddfSDavid du Colombier 		return nil;
8137dd7cddfSDavid du Colombier 	}
8147dd7cddfSDavid du Colombier 	o = t->offset;
8157dd7cddfSDavid du Colombier 	e = t->offset+t->count;
8169a747e4fSDavid du Colombier 	clock = getclock();
8179a747e4fSDavid du Colombier 	b = malloc(messagesize-IOHDRSZ);
8187dd7cddfSDavid du Colombier 	if(b == nil){
8197dd7cddfSDavid du Colombier 		fsysrespond(t, buf, Enomem);
8207dd7cddfSDavid du Colombier 		return t;
8217dd7cddfSDavid du Colombier 	}
8227dd7cddfSDavid du Colombier 	n = 0;
8237dd7cddfSDavid du Colombier 	d = dir;
8247dd7cddfSDavid du Colombier 	d++;	/* first entry is '.' */
8259a747e4fSDavid du Colombier 	for(i=0; d->name!=nil && i<e; i+=len){
8269a747e4fSDavid du Colombier 		len = dostat(d, b+n, messagesize-IOHDRSZ-n, clock);
8279a747e4fSDavid du Colombier 		if(len <= BIT16SZ)
8289a747e4fSDavid du Colombier 			break;
8299a747e4fSDavid du Colombier 		if(i >= o)
8309a747e4fSDavid du Colombier 			n += len;
8317dd7cddfSDavid du Colombier 		d++;
8327dd7cddfSDavid du Colombier 	}
8339a747e4fSDavid du Colombier 	t->data = (char*)b;
8347dd7cddfSDavid du Colombier 	t->count = n;
8357dd7cddfSDavid du Colombier 	fsysrespond(t, buf, nil);
8367dd7cddfSDavid du Colombier 	free(b);
8377dd7cddfSDavid du Colombier 	return t;
8387dd7cddfSDavid du Colombier }
8397dd7cddfSDavid du Colombier 
8407dd7cddfSDavid du Colombier static Fcall*
fsyswrite(Fcall * t,uchar * buf,Fid * f)8419a747e4fSDavid du Colombier fsyswrite(Fcall *t, uchar *buf, Fid *f)
8427dd7cddfSDavid du Colombier {
8437dd7cddfSDavid du Colombier 	Plumbmsg *m;
8447dd7cddfSDavid du Colombier 	int i, n;
8457dd7cddfSDavid du Colombier 	long count;
8467dd7cddfSDavid du Colombier 	char *data;
8477dd7cddfSDavid du Colombier 	Exec *e;
8487dd7cddfSDavid du Colombier 
8499a747e4fSDavid du Colombier 	switch((int)f->qid.path){
8509a747e4fSDavid du Colombier 	case Qdir:
8517dd7cddfSDavid du Colombier 		fsysrespond(t, buf, Eisdir);
8527dd7cddfSDavid du Colombier 		return t;
8537dd7cddfSDavid du Colombier 	case Qrules:
8547dd7cddfSDavid du Colombier 		clock = getclock();
8557dd7cddfSDavid du Colombier 		fsysrespond(t, buf, writerules(t->data, t->count));
8567dd7cddfSDavid du Colombier 		return t;
8577dd7cddfSDavid du Colombier 	case Qsend:
8587dd7cddfSDavid du Colombier 		if(f->offset == 0){
8597dd7cddfSDavid du Colombier 			data = t->data;
8607dd7cddfSDavid du Colombier 			count = t->count;
8617dd7cddfSDavid du Colombier 		}else{
8627dd7cddfSDavid du Colombier 			/* partial message already assembled */
8637dd7cddfSDavid du Colombier 			f->writebuf = erealloc(f->writebuf, f->offset + t->count);
8647dd7cddfSDavid du Colombier 			memmove(f->writebuf+f->offset, t->data, t->count);
8657dd7cddfSDavid du Colombier 			data = f->writebuf;
8667dd7cddfSDavid du Colombier 			count = f->offset+t->count;
8677dd7cddfSDavid du Colombier 		}
8687dd7cddfSDavid du Colombier 		m = plumbunpackpartial(data, count, &n);
8697dd7cddfSDavid du Colombier 		if(m == nil){
8707dd7cddfSDavid du Colombier 			if(n == 0){
8717dd7cddfSDavid du Colombier 				f->offset = 0;
8727dd7cddfSDavid du Colombier 				free(f->writebuf);
8737dd7cddfSDavid du Colombier 				f->writebuf = nil;
8747dd7cddfSDavid du Colombier 				fsysrespond(t, buf, Ebadmsg);
8757dd7cddfSDavid du Colombier 				return t;
8767dd7cddfSDavid du Colombier 			}
8777dd7cddfSDavid du Colombier 			/* can read more... */
8787dd7cddfSDavid du Colombier 			if(f->offset == 0){
8797dd7cddfSDavid du Colombier 				f->writebuf = emalloc(t->count);
8807dd7cddfSDavid du Colombier 				memmove(f->writebuf, t->data, t->count);
8817dd7cddfSDavid du Colombier 			}
8827dd7cddfSDavid du Colombier 			/* else buffer has already been grown */
8837dd7cddfSDavid du Colombier 			f->offset += t->count;
8847dd7cddfSDavid du Colombier 			fsysrespond(t, buf, nil);
8857dd7cddfSDavid du Colombier 			return t;
8867dd7cddfSDavid du Colombier 		}
8877dd7cddfSDavid du Colombier 		/* release partial buffer */
8887dd7cddfSDavid du Colombier 		f->offset = 0;
8897dd7cddfSDavid du Colombier 		free(f->writebuf);
8907dd7cddfSDavid du Colombier 		f->writebuf = nil;
8917dd7cddfSDavid du Colombier 		for(i=0; rules[i]; i++)
8927dd7cddfSDavid du Colombier 			if((e=matchruleset(m, rules[i])) != nil){
8937dd7cddfSDavid du Colombier 				dispose(t, buf, m, rules[i], e);
8947dd7cddfSDavid du Colombier 				return nil;
8957dd7cddfSDavid du Colombier 			}
8967dd7cddfSDavid du Colombier 		if(m->dst != nil){
8977dd7cddfSDavid du Colombier 			dispose(t, buf, m, nil, nil);
8987dd7cddfSDavid du Colombier 			return nil;
8997dd7cddfSDavid du Colombier 		}
9007dd7cddfSDavid du Colombier 		fsysrespond(t, buf, "no matching plumb rule");
9017dd7cddfSDavid du Colombier 		return t;
9027dd7cddfSDavid du Colombier 	}
9037dd7cddfSDavid du Colombier 	fsysrespond(t, buf, "internal error: write to unknown file");
9047dd7cddfSDavid du Colombier 	return t;
9057dd7cddfSDavid du Colombier }
9067dd7cddfSDavid du Colombier 
9077dd7cddfSDavid du Colombier static Fcall*
fsysstat(Fcall * t,uchar * buf,Fid * f)9089a747e4fSDavid du Colombier fsysstat(Fcall *t, uchar *buf, Fid *f)
9097dd7cddfSDavid du Colombier {
9109a747e4fSDavid du Colombier 	t->stat = emalloc(messagesize-IOHDRSZ);
9119a747e4fSDavid du Colombier 	t->nstat = dostat(f->dir, t->stat, messagesize-IOHDRSZ, clock);
9127dd7cddfSDavid du Colombier 	fsysrespond(t, buf, nil);
9139a747e4fSDavid du Colombier 	free(t->stat);
9149a747e4fSDavid du Colombier 	t->stat = nil;
9157dd7cddfSDavid du Colombier 	return t;
9167dd7cddfSDavid du Colombier }
9177dd7cddfSDavid du Colombier 
9187dd7cddfSDavid du Colombier static Fcall*
fsyswstat(Fcall * t,uchar * buf,Fid *)9199a747e4fSDavid du Colombier fsyswstat(Fcall *t, uchar *buf, Fid*)
9207dd7cddfSDavid du Colombier {
9217dd7cddfSDavid du Colombier 	fsysrespond(t, buf, Eperm);
9227dd7cddfSDavid du Colombier 	return t;
9237dd7cddfSDavid du Colombier }
9247dd7cddfSDavid du Colombier 
9257dd7cddfSDavid du Colombier static Fcall*
fsysremove(Fcall * t,uchar * buf,Fid *)9269a747e4fSDavid du Colombier fsysremove(Fcall *t, uchar *buf, Fid*)
9277dd7cddfSDavid du Colombier {
9287dd7cddfSDavid du Colombier 	fsysrespond(t, buf, Eperm);
9297dd7cddfSDavid du Colombier 	return t;
9307dd7cddfSDavid du Colombier }
9317dd7cddfSDavid du Colombier 
9327dd7cddfSDavid du Colombier static Fcall*
fsysclunk(Fcall * t,uchar * buf,Fid * f)9339a747e4fSDavid du Colombier fsysclunk(Fcall *t, uchar *buf, Fid *f)
9347dd7cddfSDavid du Colombier {
9357dd7cddfSDavid du Colombier 	Fid *prev, *p;
9367dd7cddfSDavid du Colombier 	Dirtab *d;
9377dd7cddfSDavid du Colombier 
9387dd7cddfSDavid du Colombier 	qlock(&queue);
9397dd7cddfSDavid du Colombier 	if(f->open){
9407dd7cddfSDavid du Colombier 		d = f->dir;
9417dd7cddfSDavid du Colombier 		d->nopen--;
9427dd7cddfSDavid du Colombier 		if(d->qid==Qrules && (f->mode==OWRITE || f->mode==ORDWR)){
9437dd7cddfSDavid du Colombier 			/*
9447dd7cddfSDavid du Colombier 			 * just to be sure last rule is parsed; error messages will be lost, though,
9457dd7cddfSDavid du Colombier 			 * unless last write ended with a blank line
9467dd7cddfSDavid du Colombier 			 */
9477dd7cddfSDavid du Colombier 			writerules(nil, 0);
9487dd7cddfSDavid du Colombier 			lock(&rulesref);
9497dd7cddfSDavid du Colombier 			rulesref.ref--;
9507dd7cddfSDavid du Colombier 			unlock(&rulesref);
9517dd7cddfSDavid du Colombier 		}
9527dd7cddfSDavid du Colombier 		prev = nil;
9537dd7cddfSDavid du Colombier 		for(p=d->fopen; p; p=p->nextopen){
9547dd7cddfSDavid du Colombier 			if(p == f){
9557dd7cddfSDavid du Colombier 				if(prev)
9567dd7cddfSDavid du Colombier 					prev->nextopen = f->nextopen;
9577dd7cddfSDavid du Colombier 				else
9587dd7cddfSDavid du Colombier 					d->fopen = f->nextopen;
9597dd7cddfSDavid du Colombier 				removesenders(d, f);
9607dd7cddfSDavid du Colombier 				break;
9617dd7cddfSDavid du Colombier 			}
9627dd7cddfSDavid du Colombier 			prev = p;
9637dd7cddfSDavid du Colombier 		}
9647dd7cddfSDavid du Colombier 	}
9657dd7cddfSDavid du Colombier 	f->busy = 0;
9667dd7cddfSDavid du Colombier 	f->open = 0;
9677dd7cddfSDavid du Colombier 	f->offset = 0;
9687dd7cddfSDavid du Colombier 	if(f->writebuf != nil){
9697dd7cddfSDavid du Colombier 		free(f->writebuf);
9707dd7cddfSDavid du Colombier 		f->writebuf = nil;
9717dd7cddfSDavid du Colombier 	}
9727dd7cddfSDavid du Colombier 	qunlock(&queue);
9737dd7cddfSDavid du Colombier 	fsysrespond(t, buf, nil);
9747dd7cddfSDavid du Colombier 	return t;
9757dd7cddfSDavid du Colombier }
976