xref: /plan9-contrib/sys/src/cmd/fscfs/cfs.c (revision 518acb8505541d27c37065969099debc28466c8f)
14366bb71SDavid du Colombier #include <u.h>
24366bb71SDavid du Colombier #include <libc.h>
34366bb71SDavid du Colombier #include <auth.h>
44366bb71SDavid du Colombier #include <fcall.h>
54366bb71SDavid du Colombier #include <thread.h>
64366bb71SDavid du Colombier 
74366bb71SDavid du Colombier /*
84366bb71SDavid du Colombier  * to do:
94366bb71SDavid du Colombier  *	- concurrency?
104366bb71SDavid du Colombier  *		- only worthwhile with several connections, perhaps to several file servers
114366bb71SDavid du Colombier  *	- cache directories
124366bb71SDavid du Colombier  *	- message size (dynamic buffers)
134366bb71SDavid du Colombier  *	- more useful stats
144366bb71SDavid du Colombier  *	- notes
154366bb71SDavid du Colombier  *	- lru for sfids in paths (also correct ref counts for paths)
164366bb71SDavid du Colombier  */
174366bb71SDavid du Colombier 
184366bb71SDavid du Colombier #include "dat.h"
194366bb71SDavid du Colombier #include "fns.h"
204366bb71SDavid du Colombier #include "stats.h"
214366bb71SDavid du Colombier 
224366bb71SDavid du Colombier #define DPRINT if(debug)fprint
234366bb71SDavid du Colombier 
244366bb71SDavid du Colombier /* maximum length of a file (used for unknown lengths) */
254366bb71SDavid du Colombier enum { MAXLEN = ((uvlong)1<<63)-1 };
264366bb71SDavid du Colombier 
274366bb71SDavid du Colombier int	debug, statson, noauth, openserver;
284366bb71SDavid du Colombier 
294366bb71SDavid du Colombier #define	MAXFDATA	8192	/* i/o size for read/write */
304366bb71SDavid du Colombier 
314366bb71SDavid du Colombier struct P9fs
324366bb71SDavid du Colombier {
334366bb71SDavid du Colombier 	int	fd[2];
344366bb71SDavid du Colombier 	Fcall	thdr;
354366bb71SDavid du Colombier 	Fcall	rhdr;
364366bb71SDavid du Colombier 	uchar	sndbuf[MAXFDATA+IOHDRSZ];	/* TO DO: dynamic */
374366bb71SDavid du Colombier 	uchar	rcvbuf[2][MAXFDATA + IOHDRSZ];	/* extra buffer to protect client data over server calls */
384366bb71SDavid du Colombier 	int	cb;	/* current buffer */
394366bb71SDavid du Colombier 	long	len;
404366bb71SDavid du Colombier 	char	*name;
414366bb71SDavid du Colombier };
424366bb71SDavid du Colombier 
434366bb71SDavid du Colombier P9fs	c;	/* client conversation */
444366bb71SDavid du Colombier P9fs	s;	/* server conversation */
454366bb71SDavid du Colombier 
464366bb71SDavid du Colombier Host	host[1];	/* clients, just the one for now */
474366bb71SDavid du Colombier 
484366bb71SDavid du Colombier Cfsstat  cfsstat, cfsprev;
494366bb71SDavid du Colombier char	statbuf[2048];
504366bb71SDavid du Colombier int	statlen;
514366bb71SDavid du Colombier 
524366bb71SDavid du Colombier int		messagesize = MAXFDATA+IOHDRSZ;	/* must be the same for all connections */
534366bb71SDavid du Colombier 
544366bb71SDavid du Colombier Qid	rootqid;
554366bb71SDavid du Colombier Qid	ctlqid = {0x5555555555555555LL, 0, 0};
564366bb71SDavid du Colombier 
574366bb71SDavid du Colombier uint	cachesize = 16 * 1024 * 1024;
584366bb71SDavid du Colombier 
594366bb71SDavid du Colombier /*
604366bb71SDavid du Colombier  * clients directly access directories, auth files, append-only files, exclusive-use files, and mount points
614366bb71SDavid du Colombier  */
624366bb71SDavid du Colombier #define	isdirect(qid)	(((qid).type & ~QTTMP) != QTFILE)
634366bb71SDavid du Colombier 
644366bb71SDavid du Colombier static char Efidinuse[] = "fid in use";
654366bb71SDavid du Colombier static char Ebadfid[] = "invalid fid";
664366bb71SDavid du Colombier static char Enotdir[] = "not a directory";
674366bb71SDavid du Colombier static char Enonexist[] = "file does not exist";
684366bb71SDavid du Colombier static char Eexist[] = "file already exists";
694366bb71SDavid du Colombier static char Eopened[] = "opened for I/O";
704366bb71SDavid du Colombier static char Emode[] = "bad open mode";
714366bb71SDavid du Colombier static char Eremoved[] = "file has been removed";
724366bb71SDavid du Colombier 
734366bb71SDavid du Colombier static SFid*	freefids;
744366bb71SDavid du Colombier static u32int fidgen;
754366bb71SDavid du Colombier 
764366bb71SDavid du Colombier static Data	datalist;
774366bb71SDavid du Colombier 
784366bb71SDavid du Colombier static Auth*	authlist;
794366bb71SDavid du Colombier 
804366bb71SDavid du Colombier void
usage(void)814366bb71SDavid du Colombier usage(void)
824366bb71SDavid du Colombier {
83*518acb85SDavid du Colombier 	fprint(2, "usage: %s [-a netaddr | -F srv] [-dknS] [mntpt] [spec]\n", argv0);
844366bb71SDavid du Colombier 	threadexitsall("usage");
854366bb71SDavid du Colombier }
864366bb71SDavid du Colombier 
874366bb71SDavid du Colombier void
threadmain(int argc,char * argv[])884366bb71SDavid du Colombier threadmain(int argc, char *argv[])
894366bb71SDavid du Colombier {
904366bb71SDavid du Colombier 	int std;
91*518acb85SDavid du Colombier 	char *server, *mtpt, *aname;
924366bb71SDavid du Colombier 
934366bb71SDavid du Colombier 	std = 0;
944366bb71SDavid du Colombier 	server = "net!$server";
954366bb71SDavid du Colombier 	mtpt = "/n/remote";
96*518acb85SDavid du Colombier 	aname = "";
974366bb71SDavid du Colombier 
984366bb71SDavid du Colombier 	ARGBEGIN{
994366bb71SDavid du Colombier 	case 'a':
1004366bb71SDavid du Colombier 		server = EARGF(usage());
1014366bb71SDavid du Colombier 		break;
1024366bb71SDavid du Colombier 	case 'd':
1034366bb71SDavid du Colombier 		debug = 1;
1044366bb71SDavid du Colombier 		break;
1054366bb71SDavid du Colombier 	case 'F':
1064366bb71SDavid du Colombier 		server = EARGF(usage());
1074366bb71SDavid du Colombier 		openserver = 1;
1084366bb71SDavid du Colombier 		break;
1094366bb71SDavid du Colombier 	case 'm':
1104366bb71SDavid du Colombier 		cachesize = atoi(EARGF(usage())) * 1024 * 1024;
1114366bb71SDavid du Colombier 		if(cachesize < 8 * 1024 * 1024 ||
1124366bb71SDavid du Colombier 		    cachesize > 3750UL * 1024 * 1024)
1134366bb71SDavid du Colombier 			sysfatal("implausible cache size %ud", cachesize);
1144366bb71SDavid du Colombier 		break;
1154366bb71SDavid du Colombier 	case 'n':
1164366bb71SDavid du Colombier 		noauth = 1;
1174366bb71SDavid du Colombier 		break;
1184366bb71SDavid du Colombier 	case 'S':
1194366bb71SDavid du Colombier 		statson = 1;
1204366bb71SDavid du Colombier 		break;
1214366bb71SDavid du Colombier 	case 's':
1224366bb71SDavid du Colombier 		std = 1;
1234366bb71SDavid du Colombier 		break;
1244366bb71SDavid du Colombier 	default:
1254366bb71SDavid du Colombier 		usage();
1264366bb71SDavid du Colombier 	}ARGEND
1274366bb71SDavid du Colombier 	if(argc && *argv)
1284366bb71SDavid du Colombier 		mtpt = *argv;
129*518acb85SDavid du Colombier 	if(argc > 1 && argv[1])
130*518acb85SDavid du Colombier 		aname = argv[1];
1314366bb71SDavid du Colombier 
1324366bb71SDavid du Colombier 	quotefmtinstall();
1334366bb71SDavid du Colombier 	if(debug){
1344366bb71SDavid du Colombier 		fmtinstall('F', fcallfmt);
1354366bb71SDavid du Colombier 		fmtinstall('D', dirfmt);
1364366bb71SDavid du Colombier 		fmtinstall('M', dirmodefmt);
1374366bb71SDavid du Colombier 	}
1384366bb71SDavid du Colombier 
1394366bb71SDavid du Colombier 	c.name = "client";
1404366bb71SDavid du Colombier 	s.name = "server";
1414366bb71SDavid du Colombier 	if(std){
1424366bb71SDavid du Colombier 		c.fd[0] = c.fd[1] = 1;
1434366bb71SDavid du Colombier 		s.fd[0] = s.fd[1] = 0;
1444366bb71SDavid du Colombier 	}else
145*518acb85SDavid du Colombier 		mountinit(server, mtpt, aname);
1464366bb71SDavid du Colombier 
1474366bb71SDavid du Colombier 	switch(fork()){
1484366bb71SDavid du Colombier 	case 0:
1494366bb71SDavid du Colombier 		io();
1504366bb71SDavid du Colombier 		threadexits("");
1514366bb71SDavid du Colombier 	case -1:
1524366bb71SDavid du Colombier 		error("fork");
1534366bb71SDavid du Colombier 	default:
1544366bb71SDavid du Colombier 		_exits("");
1554366bb71SDavid du Colombier 	}
1564366bb71SDavid du Colombier }
1574366bb71SDavid du Colombier 
1584366bb71SDavid du Colombier /*
1594366bb71SDavid du Colombier  * TO DO: need to use libthread variants
1604366bb71SDavid du Colombier  */
1614366bb71SDavid du Colombier void
mountinit(char * server,char * mountpoint,char * aname)162*518acb85SDavid du Colombier mountinit(char *server, char *mountpoint, char *aname)
1634366bb71SDavid du Colombier {
1644366bb71SDavid du Colombier 	int err;
1654366bb71SDavid du Colombier 	int p[2];
1664366bb71SDavid du Colombier 
1674366bb71SDavid du Colombier 	/*
1684366bb71SDavid du Colombier 	 *  grab a channel and call up the file server
1694366bb71SDavid du Colombier 	 */
1704366bb71SDavid du Colombier 	if(openserver){
1714366bb71SDavid du Colombier 		s.fd[0] = open(server, ORDWR);
1724366bb71SDavid du Colombier 		if(s.fd[0] < 0)
1734366bb71SDavid du Colombier 			error("opening srv file %s: %r", server);
1744366bb71SDavid du Colombier 	} else {
1754366bb71SDavid du Colombier 		s.fd[0] = dial(netmkaddr(server, 0, "9fs"), 0, 0, 0);
1764366bb71SDavid du Colombier 		if(s.fd[0] < 0)
1774366bb71SDavid du Colombier 			error("dialing %s: %r", server);
1784366bb71SDavid du Colombier 	}
1794366bb71SDavid du Colombier 	s.fd[1] = s.fd[0];
1804366bb71SDavid du Colombier 
1814366bb71SDavid du Colombier 	/*
1824366bb71SDavid du Colombier  	 *  mount onto name space
1834366bb71SDavid du Colombier 	 */
1844366bb71SDavid du Colombier 	if(pipe(p) < 0)
1854366bb71SDavid du Colombier 		error("pipe failed");
1864366bb71SDavid du Colombier 	switch(fork()){
1874366bb71SDavid du Colombier 	case 0:
1884366bb71SDavid du Colombier 		break;
1894366bb71SDavid du Colombier 	default:
1904366bb71SDavid du Colombier 		rfork(RFFDG);
1914366bb71SDavid du Colombier 		close(p[0]);
1924366bb71SDavid du Colombier 		if(noauth)
193*518acb85SDavid du Colombier 			err = mount(p[1], -1, mountpoint, MREPL|MCREATE|MCACHE, aname);
1944366bb71SDavid du Colombier 		else
195*518acb85SDavid du Colombier 			err = amount(p[1], mountpoint, MREPL|MCREATE|MCACHE, aname);
1964366bb71SDavid du Colombier 		if(err < 0)
1974366bb71SDavid du Colombier 			error("mount failed: %r");
1984366bb71SDavid du Colombier 		_exits(0);
1994366bb71SDavid du Colombier 	case -1:
2004366bb71SDavid du Colombier 		error("fork failed\n");
2014366bb71SDavid du Colombier /*BUG: no wait!*/
2024366bb71SDavid du Colombier 	}
2034366bb71SDavid du Colombier 	close(p[1]);
2044366bb71SDavid du Colombier 	c.fd[0] = c.fd[1] = p[0];
2054366bb71SDavid du Colombier }
2064366bb71SDavid du Colombier 
2074366bb71SDavid du Colombier void
io(void)2084366bb71SDavid du Colombier io(void)
2094366bb71SDavid du Colombier {
2104366bb71SDavid du Colombier 	Fcall *t;
2114366bb71SDavid du Colombier 
2124366bb71SDavid du Colombier 	datainit();
2134366bb71SDavid du Colombier 	t = &c.thdr;
2144366bb71SDavid du Colombier 
2154366bb71SDavid du Colombier     loop:
2164366bb71SDavid du Colombier 	rcvmsg(&c, t);
2174366bb71SDavid du Colombier 
2184366bb71SDavid du Colombier 	if(statson){
2194366bb71SDavid du Colombier 		cfsstat.cm[t->type].n++;
2204366bb71SDavid du Colombier 		cfsstat.cm[t->type].s = nsec();
2214366bb71SDavid du Colombier 	}
2224366bb71SDavid du Colombier 	switch(t->type){
2234366bb71SDavid du Colombier 	default:
2244366bb71SDavid du Colombier 		sendreply("invalid 9p operation");
2254366bb71SDavid du Colombier 		break;
2264366bb71SDavid du Colombier 	case Tversion:
2274366bb71SDavid du Colombier 		rversion(t);
2284366bb71SDavid du Colombier 		break;
2294366bb71SDavid du Colombier 	case Tauth:
2304366bb71SDavid du Colombier 		rauth(t);
2314366bb71SDavid du Colombier 		break;
2324366bb71SDavid du Colombier 	case Tflush:
2334366bb71SDavid du Colombier 		rflush(t);
2344366bb71SDavid du Colombier 		break;
2354366bb71SDavid du Colombier 	case Tattach:
2364366bb71SDavid du Colombier 		rattach(t);
2374366bb71SDavid du Colombier 		break;
2384366bb71SDavid du Colombier 	case Twalk:
2394366bb71SDavid du Colombier 		rwalk(t);
2404366bb71SDavid du Colombier 		break;
2414366bb71SDavid du Colombier 	case Topen:
2424366bb71SDavid du Colombier 		ropen(t);
2434366bb71SDavid du Colombier 		break;
2444366bb71SDavid du Colombier 	case Tcreate:
2454366bb71SDavid du Colombier 		rcreate(t);
2464366bb71SDavid du Colombier 		break;
2474366bb71SDavid du Colombier 	case Tread:
2484366bb71SDavid du Colombier 		rread(t);
2494366bb71SDavid du Colombier 		break;
2504366bb71SDavid du Colombier 	case Twrite:
2514366bb71SDavid du Colombier 		rwrite(t);
2524366bb71SDavid du Colombier 		break;
2534366bb71SDavid du Colombier 	case Tclunk:
2544366bb71SDavid du Colombier 		rclunk(t);
2554366bb71SDavid du Colombier 		break;
2564366bb71SDavid du Colombier 	case Tremove:
2574366bb71SDavid du Colombier 		rremove(t);
2584366bb71SDavid du Colombier 		break;
2594366bb71SDavid du Colombier 	case Tstat:
2604366bb71SDavid du Colombier 		rstat(t);
2614366bb71SDavid du Colombier 		break;
2624366bb71SDavid du Colombier 	case Twstat:
2634366bb71SDavid du Colombier 		rwstat(t);
2644366bb71SDavid du Colombier 		break;
2654366bb71SDavid du Colombier 	}
2664366bb71SDavid du Colombier 	if(statson){
2674366bb71SDavid du Colombier 		cfsstat.cm[t->type].t += nsec() -cfsstat.cm[t->type].s;
2684366bb71SDavid du Colombier 	}
2694366bb71SDavid du Colombier 	goto loop;
2704366bb71SDavid du Colombier }
2714366bb71SDavid du Colombier 
2724366bb71SDavid du Colombier void
rversion(Fcall * t)2734366bb71SDavid du Colombier rversion(Fcall *t)
2744366bb71SDavid du Colombier {
2754366bb71SDavid du Colombier 	if(messagesize > t->msize)
2764366bb71SDavid du Colombier 		messagesize = t->msize;
2774366bb71SDavid du Colombier 	t->msize = messagesize;	/* set downstream size */
2784366bb71SDavid du Colombier 	delegate(t, nil, nil);
2794366bb71SDavid du Colombier }
2804366bb71SDavid du Colombier 
2814366bb71SDavid du Colombier void
rauth(Fcall * t)2824366bb71SDavid du Colombier rauth(Fcall *t)
2834366bb71SDavid du Colombier {
2844366bb71SDavid du Colombier 	Fid *mf;
2854366bb71SDavid du Colombier 
2864366bb71SDavid du Colombier 	mf = findfid(host, t->afid, 1);
2874366bb71SDavid du Colombier 	if(mf == nil)
2884366bb71SDavid du Colombier 		return;
2894366bb71SDavid du Colombier 	mf->path = newpath(nil, "#auth", (Qid){0, 0, 0});	/* path name is never used */
2904366bb71SDavid du Colombier 	mf->opened = allocsfid();	/* newly allocated */
2914366bb71SDavid du Colombier 	if(delegate(t, mf, nil) == 0){
2924366bb71SDavid du Colombier 		mf->qid = s.rhdr.aqid;
2934366bb71SDavid du Colombier 		mf->mode = ORDWR;
2944366bb71SDavid du Colombier 	}else{
2954366bb71SDavid du Colombier 		freesfid(mf->opened);	/* free, don't clunk: failed to establish */
2964366bb71SDavid du Colombier 		mf->opened = nil;
2974366bb71SDavid du Colombier 		putfid(mf);
2984366bb71SDavid du Colombier 	}
2994366bb71SDavid du Colombier }
3004366bb71SDavid du Colombier 
3014366bb71SDavid du Colombier void
rflush(Fcall *)3024366bb71SDavid du Colombier rflush(Fcall*)		/* synchronous so easy */
3034366bb71SDavid du Colombier {
3044366bb71SDavid du Colombier 	/*TO DO: need a tag hash to find requests, once auth is asynchronous */
3054366bb71SDavid du Colombier 	sendreply(0);
3064366bb71SDavid du Colombier }
3074366bb71SDavid du Colombier 
3084366bb71SDavid du Colombier void
rattach(Fcall * t)3094366bb71SDavid du Colombier rattach(Fcall *t)
3104366bb71SDavid du Colombier {
3114366bb71SDavid du Colombier 	Fid *mf, *afid;
3124366bb71SDavid du Colombier 	SFid *sfid;
3134366bb71SDavid du Colombier 
3144366bb71SDavid du Colombier 	mf = findfid(host, t->fid, 1);
3154366bb71SDavid du Colombier 	if(mf == nil)
3164366bb71SDavid du Colombier 		return;
3174366bb71SDavid du Colombier 	sfid = nil;
3184366bb71SDavid du Colombier 	if(t->afid != NOFID){
3194366bb71SDavid du Colombier 		afid = findfid(host, t->afid, 0);
3204366bb71SDavid du Colombier 		if(afid == nil)
3214366bb71SDavid du Colombier 			return;
3224366bb71SDavid du Colombier 		sfid = afid->opened;
3234366bb71SDavid du Colombier 		if((afid->qid.type & QTAUTH) == 0 || sfid == nil){
3244366bb71SDavid du Colombier 			sendreply("bad auth file");
3254366bb71SDavid du Colombier 			return;
3264366bb71SDavid du Colombier 		}
3274366bb71SDavid du Colombier 	}
3284366bb71SDavid du Colombier 	mf->path = newpath(nil, "", (Qid){0, 0, 0});
3294366bb71SDavid du Colombier 	mf->path->sfid = allocsfid();	/* newly allocated */
3304366bb71SDavid du Colombier 	if(delegate(t, mf, sfid) == 0){
3314366bb71SDavid du Colombier 		mf->qid = s.rhdr.qid;
3324366bb71SDavid du Colombier 		mf->path->qid = mf->qid;
3334366bb71SDavid du Colombier 		if(statson == 1){
3344366bb71SDavid du Colombier 			statson++;
3354366bb71SDavid du Colombier 			rootqid = mf->qid;
3364366bb71SDavid du Colombier 		}
3374366bb71SDavid du Colombier 	}else{
3384366bb71SDavid du Colombier 		freesfid(mf->path->sfid);	/* free, don't clunk: failed to establish */
3394366bb71SDavid du Colombier 		mf->path->sfid = nil;
3404366bb71SDavid du Colombier 		putfid(mf);
3414366bb71SDavid du Colombier 	}
3424366bb71SDavid du Colombier }
3434366bb71SDavid du Colombier 
3444366bb71SDavid du Colombier void
rwalk(Fcall * t)3454366bb71SDavid du Colombier rwalk(Fcall *t)
3464366bb71SDavid du Colombier {
3474366bb71SDavid du Colombier 	Fid *mf, *nmf;
3484366bb71SDavid du Colombier 	SFid *sfid;
3494366bb71SDavid du Colombier 	Path *p;
3504366bb71SDavid du Colombier 	char *n;
3514366bb71SDavid du Colombier 	int i;
3524366bb71SDavid du Colombier 
3534366bb71SDavid du Colombier 	mf = findfid(host, t->fid, 0);
3544366bb71SDavid du Colombier 	if(mf == nil)
3554366bb71SDavid du Colombier 		return;
3564366bb71SDavid du Colombier 	if(mf->path){
3574366bb71SDavid du Colombier 		DPRINT(2, "walk from  %p %q + %d\n", mf, pathstr(mf->path), t->nwname);
3584366bb71SDavid du Colombier 	}
359*518acb85SDavid du Colombier 	if(mf->path->inval){
360*518acb85SDavid du Colombier 		sendreply(mf->path->inval);
361*518acb85SDavid du Colombier 		return;
362*518acb85SDavid du Colombier 	}
3634366bb71SDavid du Colombier 	nmf = nil;
3644366bb71SDavid du Colombier 	if(t->newfid != t->fid){
3654366bb71SDavid du Colombier 		nmf = findfid(host, t->newfid, 1);
3664366bb71SDavid du Colombier 		if(nmf == nil)
3674366bb71SDavid du Colombier 			return;
3684366bb71SDavid du Colombier 		nmf->qid = mf->qid;
3694366bb71SDavid du Colombier 		if(nmf->path != nil)
3704366bb71SDavid du Colombier 			freepath(nmf->path);
3714366bb71SDavid du Colombier 		mf->path->ref++;
3724366bb71SDavid du Colombier 		nmf->path = mf->path;
3734366bb71SDavid du Colombier 		mf = nmf; /* Walk mf */
3744366bb71SDavid du Colombier 	}
3754366bb71SDavid du Colombier 
3764366bb71SDavid du Colombier 	if(statson &&
3774366bb71SDavid du Colombier 	   mf->qid.type == rootqid.type && mf->qid.path == rootqid.path &&
3784366bb71SDavid du Colombier 	   t->nwname == 1 && strcmp(t->wname[0], "cfsctl") == 0){
3794366bb71SDavid du Colombier 		/* This is the ctl file */
3804366bb71SDavid du Colombier 		mf->qid = ctlqid;
3814366bb71SDavid du Colombier 		c.rhdr.nwqid = 1;
3824366bb71SDavid du Colombier 		c.rhdr.wqid[0] = ctlqid;
3834366bb71SDavid du Colombier 		sendreply(0);
3844366bb71SDavid du Colombier 		return;
3854366bb71SDavid du Colombier 	}
3864366bb71SDavid du Colombier 
3874366bb71SDavid du Colombier 	/*
3884366bb71SDavid du Colombier 	 * need this as an invariant, and it should always be true, because
3894366bb71SDavid du Colombier 	 * Twalk.fid should exist and thus have sfid. could compensate by
3904366bb71SDavid du Colombier 	 * rewalking from nearest parent with sfid (in the limit, root from attach)
3914366bb71SDavid du Colombier 	 */
3924366bb71SDavid du Colombier 	assert(mf->path->sfid != nil);
3934366bb71SDavid du Colombier 
3944366bb71SDavid du Colombier 	if(t->nwname == 0){
3954366bb71SDavid du Colombier 		/* new local fid (if any) refers to existing path, shares its sfid via that path */
3964366bb71SDavid du Colombier 		c.rhdr.nwqid = 0;
3974366bb71SDavid du Colombier 		sendreply(0);
3984366bb71SDavid du Colombier 		return;
3994366bb71SDavid du Colombier 	}
4004366bb71SDavid du Colombier 
4014366bb71SDavid du Colombier 	/* t->nwname != 0 */
4024366bb71SDavid du Colombier 
4034366bb71SDavid du Colombier 	if(localwalk(mf, &c.thdr, &c.rhdr, &p)){
4044366bb71SDavid du Colombier 		/* it all worked or is known to fail (in all or in part) */
4054366bb71SDavid du Colombier 		if(c.rhdr.type == Rerror){
4064366bb71SDavid du Colombier 			DPRINT(2, "walk error: %q\n", c.rhdr.ename);
4074366bb71SDavid du Colombier 			sendreply(c.rhdr.ename);
4084366bb71SDavid du Colombier 			if(nmf != nil)
4094366bb71SDavid du Colombier 				putfid(nmf);
4104366bb71SDavid du Colombier 			return;
4114366bb71SDavid du Colombier 		}
4124366bb71SDavid du Colombier 		if(c.rhdr.nwqid != t->nwname){
4134366bb71SDavid du Colombier 			DPRINT(2, "partial walk, hit error\n");
4144366bb71SDavid du Colombier 			sendreply(0);
4154366bb71SDavid du Colombier 			if(nmf != nil)
4164366bb71SDavid du Colombier 				putfid(nmf);
4174366bb71SDavid du Colombier 			return;
4184366bb71SDavid du Colombier 		}
4194366bb71SDavid du Colombier 		DPRINT(2, "seen it: %q %q\n", pathstr(p), p->inval);
4204366bb71SDavid du Colombier 		if(p->sfid != nil){
4214366bb71SDavid du Colombier 			p->ref++;
4224366bb71SDavid du Colombier 			freepath(mf->path);
4234366bb71SDavid du Colombier 			mf->path = p;
4244366bb71SDavid du Colombier 			if(c.rhdr.nwqid > 0)
4254366bb71SDavid du Colombier 				mf->qid = c.rhdr.wqid[c.rhdr.nwqid-1];
4264366bb71SDavid du Colombier 			sendreply(0);
4274366bb71SDavid du Colombier 			return;
4284366bb71SDavid du Colombier 		}
4294366bb71SDavid du Colombier 		/* know the path, but need a fid on the server */
4304366bb71SDavid du Colombier 	}
4314366bb71SDavid du Colombier 
4324366bb71SDavid du Colombier 	/* walk to a new server fid (ie, server always sees fid != newfid) */
4334366bb71SDavid du Colombier 	sfid = allocsfid();	/* must release it, if walk doesn't work */
4344366bb71SDavid du Colombier 
4354366bb71SDavid du Colombier 	if(delegate(t, mf, sfid) < 0){	/* complete failure */
4364366bb71SDavid du Colombier 		if(t->nwname > 0){	/* cache first item's failure */
4374366bb71SDavid du Colombier 			DPRINT(2, "bad path: %q + %q -> %q\n", pathstr(mf->path), t->wname[0], s.rhdr.ename);
4384366bb71SDavid du Colombier 			badpath(mf->path, t->wname[0], s.rhdr.ename);
4394366bb71SDavid du Colombier 		}
4404366bb71SDavid du Colombier 		if(nmf != nil)
4414366bb71SDavid du Colombier 			putfid(nmf);
4424366bb71SDavid du Colombier 		freesfid(sfid);	/* no need to clunk it, since it hasn't been assigned */
4434366bb71SDavid du Colombier 		return;
4444366bb71SDavid du Colombier 	}
4454366bb71SDavid du Colombier 
4464366bb71SDavid du Colombier 	if(s.rhdr.nwqid == t->nwname){	/* complete success */
4474366bb71SDavid du Colombier 		for(i = 0; i < t->nwname; i++){
4484366bb71SDavid du Colombier 			n = t->wname[i];
4494366bb71SDavid du Colombier 			if(strcmp(n, "..") == 0){
4504366bb71SDavid du Colombier 				p = mf->path->parent;
4514366bb71SDavid du Colombier 				if(p != nil){	/* otherwise at root, no change */
4524366bb71SDavid du Colombier 					p->ref++;
4534366bb71SDavid du Colombier 					freepath(mf->path);
4544366bb71SDavid du Colombier 					mf->path = p;
4554366bb71SDavid du Colombier 				}
4564366bb71SDavid du Colombier 			}else
4574366bb71SDavid du Colombier 				mf->path = newpath(mf->path, n, s.rhdr.wqid[i]);
4584366bb71SDavid du Colombier 		}
4594366bb71SDavid du Colombier 		mf->qid = s.rhdr.wqid[s.rhdr.nwqid-1];	/* nwname != 0 (above) */
4604366bb71SDavid du Colombier 		if(mf->path->sfid != nil){
4614366bb71SDavid du Colombier 			/* shouldn't happen (otherwise we wouldn't walk, because localwalk would find it) */
4624366bb71SDavid du Colombier 			sysfatal("rwalk: unexpected sfid: %q -> %ud\n", pathstr(mf->path), mf->path->sfid->fid);
4634366bb71SDavid du Colombier 		}
4644366bb71SDavid du Colombier 		mf->path->sfid = sfid;
4654366bb71SDavid du Colombier 		return;
4664366bb71SDavid du Colombier 	}
4674366bb71SDavid du Colombier 
4684366bb71SDavid du Colombier 	/* partial success; note success and failure, and release fid */
4694366bb71SDavid du Colombier 	p = mf->path;
4704366bb71SDavid du Colombier 	for(i = 0; i < s.rhdr.nwqid; i++){
4714366bb71SDavid du Colombier 		n = t->wname[i];
4724366bb71SDavid du Colombier 		if(strcmp(n, "..") == 0){
4734366bb71SDavid du Colombier 			if(p->parent != nil)
4744366bb71SDavid du Colombier 				p = p->parent;
4754366bb71SDavid du Colombier 		}else
4764366bb71SDavid du Colombier 			p = newpath(p, n, s.rhdr.wqid[i]);
4774366bb71SDavid du Colombier 	}
4784366bb71SDavid du Colombier 	n = t->wname[i];
4794366bb71SDavid du Colombier 	if(i == 0 || s.rhdr.wqid[i-1].type & QTDIR)
4804366bb71SDavid du Colombier 		badpath(p, n, Enonexist);
4814366bb71SDavid du Colombier 	if(nmf != nil)
4824366bb71SDavid du Colombier 		putfid(nmf);
4834366bb71SDavid du Colombier 	freesfid(sfid);
4844366bb71SDavid du Colombier }
4854366bb71SDavid du Colombier 
4864366bb71SDavid du Colombier int
localwalk(Fid * mf,Fcall * t,Fcall * r,Path ** pp)4874366bb71SDavid du Colombier localwalk(Fid *mf, Fcall *t, Fcall *r, Path **pp)
4884366bb71SDavid du Colombier {
4894366bb71SDavid du Colombier 	Path *p, *q;
4904366bb71SDavid du Colombier 	char *n, *err;
4914366bb71SDavid du Colombier 	int i;
4924366bb71SDavid du Colombier 
4934366bb71SDavid du Colombier 	*pp = nil;
4944366bb71SDavid du Colombier 	if(t->nwname == 0)
4954366bb71SDavid du Colombier 		return 0;	/* clone */
4964366bb71SDavid du Colombier 	if(mf->path == nil)
4974366bb71SDavid du Colombier 		return 0;
4984366bb71SDavid du Colombier 	r->type = Rwalk;
4994366bb71SDavid du Colombier 	r->nwqid = 0;
5004366bb71SDavid du Colombier 	p = mf->path;
5014366bb71SDavid du Colombier 	for(i = 0; i < t->nwname; i++){
5024366bb71SDavid du Colombier 		n = t->wname[i];
5034366bb71SDavid du Colombier 		if((err = p->inval) != nil)
5044366bb71SDavid du Colombier 			goto Err;
5054366bb71SDavid du Colombier 		if((p->qid.type & QTDIR) == 0){
5064366bb71SDavid du Colombier 			err = Enotdir;
5074366bb71SDavid du Colombier 			goto Err;
5084366bb71SDavid du Colombier 		}
5094366bb71SDavid du Colombier 		if(strcmp(n, "..") == 0){
5104366bb71SDavid du Colombier 			if((q = p->parent) == nil)
5114366bb71SDavid du Colombier 				q = p;
5124366bb71SDavid du Colombier 		}else{
5134366bb71SDavid du Colombier 			for(q = p->child; q != nil; q = q->next){
5144366bb71SDavid du Colombier 				if(strcmp(n, q->name) == 0){
5154366bb71SDavid du Colombier 					if((err = q->inval) != nil)
5164366bb71SDavid du Colombier 						goto Err;
5174366bb71SDavid du Colombier 					break;
5184366bb71SDavid du Colombier 				}
5194366bb71SDavid du Colombier 			}
5204366bb71SDavid du Colombier 			if(q == nil)
5214366bb71SDavid du Colombier 				return 0;	/* not found in cache, must try server */
5224366bb71SDavid du Colombier 		}
5234366bb71SDavid du Colombier 		r->wqid[r->nwqid++] = q->qid;
5244366bb71SDavid du Colombier 		p = q;
5254366bb71SDavid du Colombier 	}
5264366bb71SDavid du Colombier 	/* found them all: if p's not locally NOFID, link incoming fid to it as local value */
5274366bb71SDavid du Colombier 	*pp = p;
5284366bb71SDavid du Colombier 	return 1;
5294366bb71SDavid du Colombier 
5304366bb71SDavid du Colombier Err:
5314366bb71SDavid du Colombier 	if(r->nwqid == 0){
5324366bb71SDavid du Colombier 		r->type = Rerror;
5334366bb71SDavid du Colombier 		r->ename = err;
5344366bb71SDavid du Colombier 	}
5354366bb71SDavid du Colombier 	return 1;
5364366bb71SDavid du Colombier }
5374366bb71SDavid du Colombier 
5384366bb71SDavid du Colombier void
ropen(Fcall * t)5394366bb71SDavid du Colombier ropen(Fcall *t)
5404366bb71SDavid du Colombier {
5414366bb71SDavid du Colombier 	Fid *mf;
5424366bb71SDavid du Colombier 	SFid *sfid;
5434366bb71SDavid du Colombier 	int omode;
5444366bb71SDavid du Colombier 	File *file;
5454366bb71SDavid du Colombier 
5464366bb71SDavid du Colombier 	mf = findfid(host, t->fid, 0);
5474366bb71SDavid du Colombier 	if(mf == nil)
5484366bb71SDavid du Colombier 		return;
5494366bb71SDavid du Colombier 	if(mf->opened != nil){
5504366bb71SDavid du Colombier 		sendreply(Eopened);
5514366bb71SDavid du Colombier 		return;
5524366bb71SDavid du Colombier 	}
5534366bb71SDavid du Colombier 	omode = openmode(t->mode);
5544366bb71SDavid du Colombier 	if(omode < 0){
5554366bb71SDavid du Colombier 		sendreply(Emode);
5564366bb71SDavid du Colombier 		return;
5574366bb71SDavid du Colombier 	}
5584366bb71SDavid du Colombier 	if(statson && ctltest(mf)){
5594366bb71SDavid du Colombier 		/* Opening ctl file */
5604366bb71SDavid du Colombier 		if(t->mode != OREAD){
5614366bb71SDavid du Colombier 			sendreply("permission denied");
5624366bb71SDavid du Colombier 			return;
5634366bb71SDavid du Colombier 		}
5644366bb71SDavid du Colombier 		mf->mode = OREAD;
5654366bb71SDavid du Colombier 		c.rhdr.qid = ctlqid;
5664366bb71SDavid du Colombier 		c.rhdr.iounit = 0;
5674366bb71SDavid du Colombier 		sendreply(0);
5684366bb71SDavid du Colombier 		genstats();
5694366bb71SDavid du Colombier 		return;
5704366bb71SDavid du Colombier 	}
5714366bb71SDavid du Colombier 	if(t->mode & (ORCLOSE|OTRUNC) || isdirect(mf->qid)){
5724366bb71SDavid du Colombier 		/* must have private sfid */
5734366bb71SDavid du Colombier 		DPRINT(2, "open dir/auth: %q\n", pathstr(mf->path));
5744366bb71SDavid du Colombier 		sfid = sfclone(mf->path->sfid);
5754366bb71SDavid du Colombier 		if(delegate(t, mf, sfid) < 0){
5764366bb71SDavid du Colombier 			sfclunk(sfid);
5774366bb71SDavid du Colombier 			return;
5784366bb71SDavid du Colombier 		}
5794366bb71SDavid du Colombier 		mf->qid = s.rhdr.qid;
5804366bb71SDavid du Colombier 		/* don't currently need iounit */
5814366bb71SDavid du Colombier 	}else if((sfid = alreadyopen(mf, omode)) == nil){
5824366bb71SDavid du Colombier 		sfid = sfclone(mf->path->sfid);
5834366bb71SDavid du Colombier 		DPRINT(2, "new open %q -> %ud\n", pathstr(mf->path), sfid->fid);
5844366bb71SDavid du Colombier 		if(delegate(t, mf, sfid) < 0){
5854366bb71SDavid du Colombier 			sfclunk(sfid);
5864366bb71SDavid du Colombier 			return;
5874366bb71SDavid du Colombier 		}
5884366bb71SDavid du Colombier 		if(mf->qid.type != s.rhdr.qid.type || mf->qid.path != s.rhdr.qid.path){
5894366bb71SDavid du Colombier 			/* file changed type or naming is volatile */
5904366bb71SDavid du Colombier 			print("file changed underfoot: %q %#ux %llud -> %#ux %llud\n",
5914366bb71SDavid du Colombier 				pathstr(mf->path), mf->qid.type, mf->qid.path, s.rhdr.qid.type, s.rhdr.qid.path);
5924366bb71SDavid du Colombier 			mf->path->qid = mf->qid;
5934366bb71SDavid du Colombier 			fileinval(mf->path);
5944366bb71SDavid du Colombier 		}
5954366bb71SDavid du Colombier 		mf->qid = s.rhdr.qid;	/* picks up vers */
5964366bb71SDavid du Colombier 		openfile(mf, omode, s.rhdr.iounit, sfid);
5974366bb71SDavid du Colombier 	}else{
5984366bb71SDavid du Colombier 		DPRINT(2, "cached open %q -> %ud\n", pathstr(mf->path), sfid->fid);
5994366bb71SDavid du Colombier 		c.rhdr.qid = mf->path->file->qid;
6004366bb71SDavid du Colombier 		c.rhdr.iounit = mf->path->file->iounit;
6014366bb71SDavid du Colombier 		sendreply(0);
6024366bb71SDavid du Colombier 	}
6034366bb71SDavid du Colombier 	mf->opened = sfid;
6044366bb71SDavid du Colombier 	mf->mode = omode;
6054366bb71SDavid du Colombier 	if(t->mode & OTRUNC && !(mf->qid.type & QTAPPEND)){
6064366bb71SDavid du Colombier 		file = mf->path->file;
6074366bb71SDavid du Colombier 		if(file != nil){
6084366bb71SDavid du Colombier 			cacheinval(file);	/* discard cached data */
6094366bb71SDavid du Colombier 			file->length = 0;
6104366bb71SDavid du Colombier 		}
6114366bb71SDavid du Colombier 	}
6124366bb71SDavid du Colombier }
6134366bb71SDavid du Colombier 
6144366bb71SDavid du Colombier void
rcreate(Fcall * t)6154366bb71SDavid du Colombier rcreate(Fcall *t)
6164366bb71SDavid du Colombier {
6174366bb71SDavid du Colombier 	Fid *mf;
6184366bb71SDavid du Colombier 	SFid *sfid;
6194366bb71SDavid du Colombier 	int omode;
6204366bb71SDavid du Colombier 
6214366bb71SDavid du Colombier 	mf = findfid(host, t->fid, 0);
6224366bb71SDavid du Colombier 	if(mf == nil)
6234366bb71SDavid du Colombier 		return;
6244366bb71SDavid du Colombier 	if(statson && ctltest(mf)){
6254366bb71SDavid du Colombier 		sendreply(Eexist);
6264366bb71SDavid du Colombier 		return;
6274366bb71SDavid du Colombier 	}
6284366bb71SDavid du Colombier 	omode = openmode(t->mode);
6294366bb71SDavid du Colombier 	if(omode < 0){
6304366bb71SDavid du Colombier 		sendreply(Emode);
6314366bb71SDavid du Colombier 		return;
6324366bb71SDavid du Colombier 	}
6334366bb71SDavid du Colombier 	/* always write-through, and by definition can't exist and be open */
6344366bb71SDavid du Colombier 	sfid = sfclone(mf->path->sfid);
6354366bb71SDavid du Colombier 	if(delegate(t, mf, sfid) == 0){
6364366bb71SDavid du Colombier 		mf->opened = sfid;
6374366bb71SDavid du Colombier 		mf->mode = omode;
6384366bb71SDavid du Colombier 		mf->qid = s.rhdr.qid;
6394366bb71SDavid du Colombier 		mf->path = newpath(mf->path, t->name, mf->qid);
6404366bb71SDavid du Colombier 		if(!(t->mode & ORCLOSE || isdirect(mf->qid))){
6414366bb71SDavid du Colombier 			/* can cache (if it's volatile, find out on subsequent open) */
6424366bb71SDavid du Colombier 			openfile(mf, omode, s.rhdr.iounit, sfid);
6434366bb71SDavid du Colombier 		}
6444366bb71SDavid du Colombier 		//mf->iounit = s.rhdr.iounit;
6454366bb71SDavid du Colombier 	}else
6464366bb71SDavid du Colombier 		sfclunk(sfid);
6474366bb71SDavid du Colombier }
6484366bb71SDavid du Colombier 
6494366bb71SDavid du Colombier void
rclunk(Fcall * t)6504366bb71SDavid du Colombier rclunk(Fcall *t)
6514366bb71SDavid du Colombier {
6524366bb71SDavid du Colombier 	Fid *mf;
6534366bb71SDavid du Colombier 
6544366bb71SDavid du Colombier 	mf = findfid(host, t->fid, 0);
6554366bb71SDavid du Colombier 	if(mf == nil)
6564366bb71SDavid du Colombier 		return;
6574366bb71SDavid du Colombier 	if(mf->opened != nil)
6584366bb71SDavid du Colombier 		closefile(mf, 1, 0);
6594366bb71SDavid du Colombier 	/* local clunk, not delegated */
6604366bb71SDavid du Colombier 	sendreply(0);
6614366bb71SDavid du Colombier 	putfid(mf);
6624366bb71SDavid du Colombier }
6634366bb71SDavid du Colombier 
6644366bb71SDavid du Colombier void
rremove(Fcall * t)6654366bb71SDavid du Colombier rremove(Fcall *t)
6664366bb71SDavid du Colombier {
6674366bb71SDavid du Colombier 	Fid *mf;
6684366bb71SDavid du Colombier 	Path *p;
6694366bb71SDavid du Colombier 	SFid *sfid;
6704366bb71SDavid du Colombier 	File *file;
6714366bb71SDavid du Colombier 
6724366bb71SDavid du Colombier 	mf = findfid(host, t->fid, 0);
6734366bb71SDavid du Colombier 	if(mf == nil)
6744366bb71SDavid du Colombier 		return;
6754366bb71SDavid du Colombier 	/* invalidate path entry; discard file; discard any local fids in use */
6764366bb71SDavid du Colombier 	if(statson && ctltest(mf)){
6774366bb71SDavid du Colombier 		sendreply("not removed");
6784366bb71SDavid du Colombier 		return;
6794366bb71SDavid du Colombier 	}
6804366bb71SDavid du Colombier 	file = nil;
6814366bb71SDavid du Colombier 	p = mf->path;
6824366bb71SDavid du Colombier 	if(delegate(t, mf, nil) == 0){
6834366bb71SDavid du Colombier 		setinval(p, Eremoved);
6844366bb71SDavid du Colombier 		file = p->file;
6854366bb71SDavid du Colombier 		p->file = nil;
6864366bb71SDavid du Colombier 	}
6874366bb71SDavid du Colombier 	/* in any case, the fid was clunked: free the sfid */
6884366bb71SDavid du Colombier 	if(mf->opened == nil){
6894366bb71SDavid du Colombier 		sfid = p->sfid;
6904366bb71SDavid du Colombier 		p->sfid = nil;
6914366bb71SDavid du Colombier 		freesfid(sfid);
6924366bb71SDavid du Colombier 	}else
6934366bb71SDavid du Colombier 		closefile(mf, 0, 1);
6944366bb71SDavid du Colombier 	putfid(mf);
6954366bb71SDavid du Colombier 	if(file != nil)
6964366bb71SDavid du Colombier 		putfile(file);
6974366bb71SDavid du Colombier }
6984366bb71SDavid du Colombier 
6994366bb71SDavid du Colombier void
rread(Fcall * t)7004366bb71SDavid du Colombier rread(Fcall *t)
7014366bb71SDavid du Colombier {
7024366bb71SDavid du Colombier 	Fid *mf;
7034366bb71SDavid du Colombier 	int cnt, done;
7044366bb71SDavid du Colombier 	long n;
7054366bb71SDavid du Colombier 	vlong off, first;
7064366bb71SDavid du Colombier 	char *cp;
7074366bb71SDavid du Colombier 	File *file;
7084366bb71SDavid du Colombier 	char data[MAXFDATA];
7094366bb71SDavid du Colombier 
7104366bb71SDavid du Colombier 	mf = findfid(host, t->fid, 0);
7114366bb71SDavid du Colombier 	if(mf == nil)
7124366bb71SDavid du Colombier 		return;
7134366bb71SDavid du Colombier 
7144366bb71SDavid du Colombier 	off = t->offset;
7154366bb71SDavid du Colombier 	cnt = t->count;
7164366bb71SDavid du Colombier 
7174366bb71SDavid du Colombier 	if(statson && ctltest(mf)){
7184366bb71SDavid du Colombier 		if(cnt > statlen-off)
7194366bb71SDavid du Colombier 			c.rhdr.count = statlen-off;
7204366bb71SDavid du Colombier 		else
7214366bb71SDavid du Colombier 			c.rhdr.count = cnt;
7224366bb71SDavid du Colombier 		if((int)c.rhdr.count < 0){
7234366bb71SDavid du Colombier 			c.rhdr.count = 0;
7244366bb71SDavid du Colombier 			sendreply(0);
7254366bb71SDavid du Colombier 			return;
7264366bb71SDavid du Colombier 		}
7274366bb71SDavid du Colombier 		c.rhdr.data = statbuf + off;
7284366bb71SDavid du Colombier 		sendreply(0);
7294366bb71SDavid du Colombier 		return;
7304366bb71SDavid du Colombier 	}
7314366bb71SDavid du Colombier 
7324366bb71SDavid du Colombier 	if(mf->opened == nil || mf->mode == OWRITE){
7334366bb71SDavid du Colombier 		sendreply("not open for reading");
7344366bb71SDavid du Colombier 		return;
7354366bb71SDavid du Colombier 	}
7364366bb71SDavid du Colombier 
7374366bb71SDavid du Colombier 	file = mf->path->file;
7384366bb71SDavid du Colombier 	if(isdirect(mf->qid) || file == nil){
7394366bb71SDavid du Colombier 		DPRINT(2, "delegating read\n");
7404366bb71SDavid du Colombier 		delegate(t, mf, nil);
7414366bb71SDavid du Colombier 		if(statson){
7424366bb71SDavid du Colombier 			if(mf->qid.type & QTDIR)
7434366bb71SDavid du Colombier 				cfsstat.ndirread++;
7444366bb71SDavid du Colombier 			else
7454366bb71SDavid du Colombier 				cfsstat.ndelegateread++;
7464366bb71SDavid du Colombier 			if(s.rhdr.count > 0){
7474366bb71SDavid du Colombier 				cfsstat.bytesread += s.rhdr.count;
7484366bb71SDavid du Colombier 				if(mf->qid.type & QTDIR)
7494366bb71SDavid du Colombier 					cfsstat.bytesfromdirs += s.rhdr.count;
7504366bb71SDavid du Colombier 				else
7514366bb71SDavid du Colombier 					cfsstat.bytesfromserver += s.rhdr.count;
7524366bb71SDavid du Colombier 			}
7534366bb71SDavid du Colombier 		}
7544366bb71SDavid du Colombier 		return;
7554366bb71SDavid du Colombier 	}
7564366bb71SDavid du Colombier 
7574366bb71SDavid du Colombier 	first = off;
7584366bb71SDavid du Colombier 	cp = data;
7594366bb71SDavid du Colombier 	done = 0;
7604366bb71SDavid du Colombier 	while(cnt>0 && !done){
7614366bb71SDavid du Colombier 		if(off >= file->clength){
7624366bb71SDavid du Colombier 			DPRINT(2, "offset %lld >= length %lld\n", off, file->clength);
7634366bb71SDavid du Colombier 			break;
7644366bb71SDavid du Colombier 		}
7654366bb71SDavid du Colombier 		n = cacheread(file, cp, off, cnt);
7664366bb71SDavid du Colombier 		if(n <= 0){
7674366bb71SDavid du Colombier 			n = -n;
7684366bb71SDavid du Colombier 			if(n==0 || n>cnt)
7694366bb71SDavid du Colombier 				n = cnt;
7704366bb71SDavid du Colombier 			DPRINT(2, "fetch %ld bytes of data from server at offset %lld\n", n, off);
7714366bb71SDavid du Colombier 			s.thdr.tag = t->tag;
7724366bb71SDavid du Colombier 			s.thdr.type = t->type;
7734366bb71SDavid du Colombier 			s.thdr.fid = t->fid;
7744366bb71SDavid du Colombier 			s.thdr.offset = off;
7754366bb71SDavid du Colombier 			s.thdr.count = n;
7764366bb71SDavid du Colombier 			if(statson)
7774366bb71SDavid du Colombier 				cfsstat.ndelegateread++;
7784366bb71SDavid du Colombier 			if(askserver(t, mf) < 0){
7794366bb71SDavid du Colombier 				sendreply(s.rhdr.ename);
7804366bb71SDavid du Colombier 				return;
7814366bb71SDavid du Colombier 			}
7824366bb71SDavid du Colombier 			if(s.rhdr.count != n)
7834366bb71SDavid du Colombier 				done = 1;
7844366bb71SDavid du Colombier 			n = s.rhdr.count;
7854366bb71SDavid du Colombier 			if(n == 0){
7864366bb71SDavid du Colombier 				/* end of file */
7874366bb71SDavid du Colombier 				if(file->clength > off){
7884366bb71SDavid du Colombier 					DPRINT(2, "file %llud.%ld, length %lld\n",
7894366bb71SDavid du Colombier 						file->qid.path,
7904366bb71SDavid du Colombier 						file->qid.vers, off);
7914366bb71SDavid du Colombier 					file->clength = off;
7924366bb71SDavid du Colombier 				}
7934366bb71SDavid du Colombier 				break;
7944366bb71SDavid du Colombier 			}
7954366bb71SDavid du Colombier 			memmove(cp, s.rhdr.data, n);
7964366bb71SDavid du Colombier 			cachewrite(file, cp, off, n);
7974366bb71SDavid du Colombier 			if(statson){
7984366bb71SDavid du Colombier 				cfsstat.bytestocache += n;
7994366bb71SDavid du Colombier 				cfsstat.bytesfromserver += n;
8004366bb71SDavid du Colombier 			}
8014366bb71SDavid du Colombier 		}else{
8024366bb71SDavid du Colombier 			DPRINT(2, "fetched %ld bytes from cache\n", n);
8034366bb71SDavid du Colombier 			if(statson)
8044366bb71SDavid du Colombier 				cfsstat.bytesfromcache += n;
8054366bb71SDavid du Colombier 		}
8064366bb71SDavid du Colombier 		cnt -= n;
8074366bb71SDavid du Colombier 		off += n;
8084366bb71SDavid du Colombier 		cp += n;
8094366bb71SDavid du Colombier 	}
8104366bb71SDavid du Colombier 	c.rhdr.data = data;
8114366bb71SDavid du Colombier 	c.rhdr.count = off - first;
8124366bb71SDavid du Colombier 	if(statson)
8134366bb71SDavid du Colombier 		cfsstat.bytesread += c.rhdr.count;
8144366bb71SDavid du Colombier 	sendreply(0);
8154366bb71SDavid du Colombier }
8164366bb71SDavid du Colombier 
8174366bb71SDavid du Colombier void
rwrite(Fcall * t)8184366bb71SDavid du Colombier rwrite(Fcall *t)
8194366bb71SDavid du Colombier {
8204366bb71SDavid du Colombier 	Fid *mf;
8214366bb71SDavid du Colombier 	File *file;
8224366bb71SDavid du Colombier 	u32int v1, v2, count;
8234366bb71SDavid du Colombier 
8244366bb71SDavid du Colombier 	mf = findfid(host, t->fid, 0);
8254366bb71SDavid du Colombier 	if(mf == nil)
8264366bb71SDavid du Colombier 		return;
8274366bb71SDavid du Colombier 	if(statson && ctltest(mf)){
8284366bb71SDavid du Colombier 		sendreply("read only");
8294366bb71SDavid du Colombier 		return;
8304366bb71SDavid du Colombier 	}
8314366bb71SDavid du Colombier 	if(mf->opened == nil || mf->mode == OREAD){
8324366bb71SDavid du Colombier 		sendreply("not open for writing");
8334366bb71SDavid du Colombier 		return;
8344366bb71SDavid du Colombier 	}
8354366bb71SDavid du Colombier 	file = mf->path->file;
8364366bb71SDavid du Colombier 	if(isdirect(mf->qid) || file == nil){
8374366bb71SDavid du Colombier 		delegate(t, mf, nil);
8384366bb71SDavid du Colombier 		if(statson && s.rhdr.count > 0)
8394366bb71SDavid du Colombier 			cfsstat.byteswritten += s.rhdr.count;
8404366bb71SDavid du Colombier 		return;
8414366bb71SDavid du Colombier 	}
8424366bb71SDavid du Colombier 
8434366bb71SDavid du Colombier 	/* add to local cache as write through */
8444366bb71SDavid du Colombier 	if(delegate(t, mf, nil) < 0)
8454366bb71SDavid du Colombier 		return;
8464366bb71SDavid du Colombier 
8474366bb71SDavid du Colombier 	count = s.rhdr.count;
8484366bb71SDavid du Colombier 	cfsstat.byteswritten += count;
8494366bb71SDavid du Colombier 
8504366bb71SDavid du Colombier 	v1 = mf->qid.vers + 1;
8514366bb71SDavid du Colombier 	v2 = mf->path->qid.vers + 1;
8524366bb71SDavid du Colombier 	if(v1 > v2)
8534366bb71SDavid du Colombier 		v1 = v2;
8544366bb71SDavid du Colombier 	mf->qid.vers = v1;
8554366bb71SDavid du Colombier 	mf->path->qid.vers = v1;
8564366bb71SDavid du Colombier 	file->qid.vers = v1;
8574366bb71SDavid du Colombier 	if(file->clength < t->offset + count)
8584366bb71SDavid du Colombier 		file->clength = t->offset + count;
8594366bb71SDavid du Colombier 	cachewrite(file, t->data, t->offset, count);
8604366bb71SDavid du Colombier }
8614366bb71SDavid du Colombier 
8624366bb71SDavid du Colombier void
rstat(Fcall * t)8634366bb71SDavid du Colombier rstat(Fcall *t)
8644366bb71SDavid du Colombier {
8654366bb71SDavid du Colombier 	Fid *mf;
8664366bb71SDavid du Colombier 	Dir d;
8674366bb71SDavid du Colombier 	uchar statbuf[256];
8684366bb71SDavid du Colombier 
8694366bb71SDavid du Colombier 	mf = findfid(host, t->fid, 0);
8704366bb71SDavid du Colombier 	if(mf == nil)
8714366bb71SDavid du Colombier 		return;
8724366bb71SDavid du Colombier 	if(statson && ctltest(mf)){
8734366bb71SDavid du Colombier 		genstats();
8744366bb71SDavid du Colombier 		d.qid = ctlqid;
8754366bb71SDavid du Colombier 		d.mode = 0444;
8764366bb71SDavid du Colombier 		d.length = statlen;
8774366bb71SDavid du Colombier 		d.name = "cfsctl";
8784366bb71SDavid du Colombier 		d.uid = "none";
8794366bb71SDavid du Colombier 		d.gid = "none";
8804366bb71SDavid du Colombier 		d.muid = "none";
8814366bb71SDavid du Colombier 		d.atime = time(nil);
8824366bb71SDavid du Colombier 		d.mtime = d.atime;
8834366bb71SDavid du Colombier 		c.rhdr.stat = statbuf;
8844366bb71SDavid du Colombier 		c.rhdr.nstat = convD2M(&d, statbuf, sizeof(statbuf));
8854366bb71SDavid du Colombier 		sendreply(0);
8864366bb71SDavid du Colombier 		return;
8874366bb71SDavid du Colombier 	}
8884366bb71SDavid du Colombier 	if(delegate(t, mf, nil) == 0){
8894366bb71SDavid du Colombier 		convM2D(s.rhdr.stat, s.rhdr.nstat , &d, nil);
8904366bb71SDavid du Colombier 		//mf->qid = d.qid;
8914366bb71SDavid du Colombier 		if(mf->path->file != nil)
8924366bb71SDavid du Colombier 			copystat(mf->path->file, &d, 0);
8934366bb71SDavid du Colombier 	}
8944366bb71SDavid du Colombier }
8954366bb71SDavid du Colombier 
8964366bb71SDavid du Colombier void
rwstat(Fcall * t)8974366bb71SDavid du Colombier rwstat(Fcall *t)
8984366bb71SDavid du Colombier {
8994366bb71SDavid du Colombier 	Fid *mf;
9004366bb71SDavid du Colombier 	Path *p;
9014366bb71SDavid du Colombier 	Dir *d;
9024366bb71SDavid du Colombier 	int qt;
9034366bb71SDavid du Colombier 
9044366bb71SDavid du Colombier 	mf = findfid(host, t->fid, 0);
9054366bb71SDavid du Colombier 	if(mf == nil)
9064366bb71SDavid du Colombier 		return;
9074366bb71SDavid du Colombier 	if(statson && ctltest(mf)){
9084366bb71SDavid du Colombier 		sendreply("read only");
9094366bb71SDavid du Colombier 		return;
9104366bb71SDavid du Colombier 	}
9114366bb71SDavid du Colombier 	if(delegate(t, mf, nil) == 0){
9124366bb71SDavid du Colombier 		d = malloc(sizeof(*d)+t->nstat);
9134366bb71SDavid du Colombier 		if(convM2D(t->stat, t->nstat, d, (char*)(d+1)) != 0){
9144366bb71SDavid du Colombier 			if(*d->name){	/* file renamed */
9154366bb71SDavid du Colombier 				p = mf->path;
9164366bb71SDavid du Colombier 				free(p->name);
9174366bb71SDavid du Colombier 				p->name = strdup(d->name);
918*518acb85SDavid du Colombier 				freeinval(p);
9194366bb71SDavid du Colombier 			}
9204366bb71SDavid du Colombier 			if(d->mode != ~0){
9214366bb71SDavid du Colombier 				qt = d->mode>>24;
9224366bb71SDavid du Colombier 				mf->qid.type = qt;
9234366bb71SDavid du Colombier 				mf->path->qid.type = qt;
9244366bb71SDavid du Colombier 			}
9254366bb71SDavid du Colombier 			if(mf->path->file != nil)
9264366bb71SDavid du Colombier 				copystat(mf->path->file, d, 1);
9274366bb71SDavid du Colombier 		}
9284366bb71SDavid du Colombier 		free(d);
9294366bb71SDavid du Colombier 	}
9304366bb71SDavid du Colombier }
9314366bb71SDavid du Colombier 
9324366bb71SDavid du Colombier int
openmode(uint o)9334366bb71SDavid du Colombier openmode(uint o)
9344366bb71SDavid du Colombier {
9354366bb71SDavid du Colombier 	uint m;
9364366bb71SDavid du Colombier 
9374366bb71SDavid du Colombier 	m = o & ~(OTRUNC|OCEXEC|ORCLOSE);
9384366bb71SDavid du Colombier 	if(m > OEXEC)
9394366bb71SDavid du Colombier 		return -1;
9404366bb71SDavid du Colombier 	if(m == OEXEC)
9414366bb71SDavid du Colombier 		m = OREAD;
9424366bb71SDavid du Colombier 	if(o&OTRUNC && m == OREAD)
9434366bb71SDavid du Colombier 		return -1;
9444366bb71SDavid du Colombier 	return m;
9454366bb71SDavid du Colombier }
9464366bb71SDavid du Colombier 
9474366bb71SDavid du Colombier void
error(char * fmt,...)9484366bb71SDavid du Colombier error(char *fmt, ...)
9494366bb71SDavid du Colombier {
9504366bb71SDavid du Colombier 	va_list arg;
9514366bb71SDavid du Colombier 	static char buf[2048];
9524366bb71SDavid du Colombier 
9534366bb71SDavid du Colombier 	va_start(arg, fmt);
9544366bb71SDavid du Colombier 	vseprint(buf, buf+sizeof(buf), fmt, arg);
9554366bb71SDavid du Colombier 	va_end(arg);
9564366bb71SDavid du Colombier 	fprint(2, "%s: %s\n", argv0, buf);
9574366bb71SDavid du Colombier 	threadexitsall("error");
9584366bb71SDavid du Colombier }
9594366bb71SDavid du Colombier 
9604366bb71SDavid du Colombier void
warning(char * s)9614366bb71SDavid du Colombier warning(char *s)
9624366bb71SDavid du Colombier {
9634366bb71SDavid du Colombier 	fprint(2, "%s: %s: %r\n", argv0, s);
9644366bb71SDavid du Colombier }
9654366bb71SDavid du Colombier 
9664366bb71SDavid du Colombier /*
9674366bb71SDavid du Colombier  *  send a reply to the client
9684366bb71SDavid du Colombier  */
9694366bb71SDavid du Colombier void
sendreply(char * err)9704366bb71SDavid du Colombier sendreply(char *err)
9714366bb71SDavid du Colombier {
9724366bb71SDavid du Colombier 	if(err){
9734366bb71SDavid du Colombier 		c.rhdr.type = Rerror;
9744366bb71SDavid du Colombier 		c.rhdr.ename = err;
9754366bb71SDavid du Colombier 	}else{
9764366bb71SDavid du Colombier 		c.rhdr.type = c.thdr.type+1;
9774366bb71SDavid du Colombier 		c.rhdr.fid = c.thdr.fid;
9784366bb71SDavid du Colombier 	}
9794366bb71SDavid du Colombier 	c.rhdr.tag = c.thdr.tag;
9804366bb71SDavid du Colombier 	sendmsg(&c, &c.rhdr);
9814366bb71SDavid du Colombier }
9824366bb71SDavid du Colombier 
9834366bb71SDavid du Colombier /*
9844366bb71SDavid du Colombier  * local fids
9854366bb71SDavid du Colombier  */
9864366bb71SDavid du Colombier Fid*
findfid(Host * host,u32int fid,int mk)9874366bb71SDavid du Colombier findfid(Host *host, u32int fid, int mk)
9884366bb71SDavid du Colombier {
9894366bb71SDavid du Colombier 	Fid *f, **l;
9904366bb71SDavid du Colombier 
9914366bb71SDavid du Colombier 	if(fid == NOFID){
9924366bb71SDavid du Colombier 		sendreply(Ebadfid);
9934366bb71SDavid du Colombier 		return nil;
9944366bb71SDavid du Colombier 	}
9954366bb71SDavid du Colombier 	l = &host->fids[fid & (FidHash-1)];
9964366bb71SDavid du Colombier 	for(f = *l; f != nil; f = f->next){
9974366bb71SDavid du Colombier 		if(f->fid == fid){
9984366bb71SDavid du Colombier 			if(mk){
9994366bb71SDavid du Colombier 				sendreply(Efidinuse);
10004366bb71SDavid du Colombier 				return nil;
10014366bb71SDavid du Colombier 			}
10024366bb71SDavid du Colombier 			return f;
10034366bb71SDavid du Colombier 		}
10044366bb71SDavid du Colombier 	}
10054366bb71SDavid du Colombier 	if(!mk){
10064366bb71SDavid du Colombier 		sendreply(Ebadfid);
10074366bb71SDavid du Colombier 		return nil;
10084366bb71SDavid du Colombier 	}
10094366bb71SDavid du Colombier 	f = mallocz(sizeof(*f), 1);
10104366bb71SDavid du Colombier 	f->fid = fid;
10114366bb71SDavid du Colombier 	f->next = *l;
10124366bb71SDavid du Colombier 	*l = f;
10134366bb71SDavid du Colombier 	return f;
10144366bb71SDavid du Colombier }
10154366bb71SDavid du Colombier 
10164366bb71SDavid du Colombier void
putfid(Fid * f)10174366bb71SDavid du Colombier putfid(Fid *f)
10184366bb71SDavid du Colombier {
10194366bb71SDavid du Colombier 	Fid **l;
10204366bb71SDavid du Colombier 	static Qid zeroqid;
10214366bb71SDavid du Colombier 
10224366bb71SDavid du Colombier 	for(l = &host->fids[f->fid & (FidHash-1)]; *l != nil; l = &(*l)->next){
10234366bb71SDavid du Colombier 		if(*l == f){
10244366bb71SDavid du Colombier 			*l = f->next;
10254366bb71SDavid du Colombier 			break;
10264366bb71SDavid du Colombier 		}
10274366bb71SDavid du Colombier 	}
10284366bb71SDavid du Colombier 	if(f->opened != nil){
10294366bb71SDavid du Colombier 		sfclunk(f->opened);
10304366bb71SDavid du Colombier 		f->opened = nil;
10314366bb71SDavid du Colombier 	}
10324366bb71SDavid du Colombier 	freepath(f->path);
10334366bb71SDavid du Colombier 	/* poison values just in case */
10344366bb71SDavid du Colombier 	f->path = nil;
10354366bb71SDavid du Colombier 	f->fid = NOFID;
10364366bb71SDavid du Colombier 	f->qid = zeroqid;
10374366bb71SDavid du Colombier 	free(f);
10384366bb71SDavid du Colombier }
10394366bb71SDavid du Colombier 
10404366bb71SDavid du Colombier /*
10414366bb71SDavid du Colombier  * return server fid for local fid
10424366bb71SDavid du Colombier  */
10434366bb71SDavid du Colombier u32int
mapfid(Fid * f)10444366bb71SDavid du Colombier mapfid(Fid *f)
10454366bb71SDavid du Colombier {
10464366bb71SDavid du Colombier 	if(f->opened != nil){
10474366bb71SDavid du Colombier 		DPRINT(2, "mapfid: use open fid %ud -> %ud\n", f->fid, f->opened->fid);
10484366bb71SDavid du Colombier 		return f->opened->fid;
10494366bb71SDavid du Colombier 	}
10504366bb71SDavid du Colombier 	if(f->path->sfid == nil)
10514366bb71SDavid du Colombier 		sysfatal("mapfid: missing sfid for path %q\n", pathstr(f->path));
10524366bb71SDavid du Colombier 	return f->path->sfid->fid;
10534366bb71SDavid du Colombier }
10544366bb71SDavid du Colombier 
10554366bb71SDavid du Colombier /*
10564366bb71SDavid du Colombier  *  send a request to the server, get the reply,
10574366bb71SDavid du Colombier  * and send that to the client
10584366bb71SDavid du Colombier  */
10594366bb71SDavid du Colombier int
delegate(Fcall * t,Fid * f1,SFid * f2)10604366bb71SDavid du Colombier delegate(Fcall *t, Fid *f1, SFid *f2)
10614366bb71SDavid du Colombier {
10624366bb71SDavid du Colombier 	int type;
10634366bb71SDavid du Colombier 
10644366bb71SDavid du Colombier 	type = t->type;
10654366bb71SDavid du Colombier 	if(statson){
10664366bb71SDavid du Colombier 		cfsstat.sm[type].n++;
10674366bb71SDavid du Colombier 		cfsstat.sm[type].s = nsec();
10684366bb71SDavid du Colombier 	}
10694366bb71SDavid du Colombier 
10704366bb71SDavid du Colombier 	switch(type){
10714366bb71SDavid du Colombier 	case Tversion:
10724366bb71SDavid du Colombier 	case Tflush:	/* no fid */
10734366bb71SDavid du Colombier 		break;
10744366bb71SDavid du Colombier 	case Tauth:	/* afid */
10754366bb71SDavid du Colombier 		t->afid = mapfid(f1);
10764366bb71SDavid du Colombier 		break;
10774366bb71SDavid du Colombier 	case Tattach:	/* fid, afid */
10784366bb71SDavid du Colombier 		t->fid = mapfid(f1);
10794366bb71SDavid du Colombier 		if(f2 != nil)
10804366bb71SDavid du Colombier 			t->afid = f2->fid;
10814366bb71SDavid du Colombier 		else
10824366bb71SDavid du Colombier 			t->afid = NOFID;
10834366bb71SDavid du Colombier 		break;
10844366bb71SDavid du Colombier 	case Twalk:	/* fid, newfid */
10854366bb71SDavid du Colombier 		t->fid = mapfid(f1);
10864366bb71SDavid du Colombier 		t->newfid = f2->fid;
10874366bb71SDavid du Colombier 		break;
10884366bb71SDavid du Colombier 	default:	/* fid */
10894366bb71SDavid du Colombier 		if(f2 != nil)
10904366bb71SDavid du Colombier 			t->fid = f2->fid;
10914366bb71SDavid du Colombier 		else
10924366bb71SDavid du Colombier 			t->fid = mapfid(f1);
10934366bb71SDavid du Colombier 		break;
10944366bb71SDavid du Colombier 	}
10954366bb71SDavid du Colombier 
10964366bb71SDavid du Colombier 	sendmsg(&s, t);
10974366bb71SDavid du Colombier 	rcvmsg(&s, &s.rhdr);
10984366bb71SDavid du Colombier 
10994366bb71SDavid du Colombier 	if(statson)
11004366bb71SDavid du Colombier 		cfsstat.sm[type].t += nsec() - cfsstat.sm[type].s;
11014366bb71SDavid du Colombier 
11024366bb71SDavid du Colombier 	sendmsg(&c, &s.rhdr);
11034366bb71SDavid du Colombier 	return t->type+1 == s.rhdr.type? 0: -1;
11044366bb71SDavid du Colombier }
11054366bb71SDavid du Colombier 
11064366bb71SDavid du Colombier /*
11074366bb71SDavid du Colombier  *  send an i/o request to the server and get a reply
11084366bb71SDavid du Colombier  */
11094366bb71SDavid du Colombier int
askserver(Fcall * t,Fid * f)11104366bb71SDavid du Colombier askserver(Fcall *t, Fid *f)
11114366bb71SDavid du Colombier {
11124366bb71SDavid du Colombier 	int type;
11134366bb71SDavid du Colombier 
11144366bb71SDavid du Colombier 	s.thdr.tag = t->tag;
11154366bb71SDavid du Colombier 
11164366bb71SDavid du Colombier 	type = s.thdr.type;
11174366bb71SDavid du Colombier 	if(statson){
11184366bb71SDavid du Colombier 		cfsstat.sm[type].n++;
11194366bb71SDavid du Colombier 		cfsstat.sm[type].s = nsec();
11204366bb71SDavid du Colombier 	}
11214366bb71SDavid du Colombier 
11224366bb71SDavid du Colombier 	s.thdr.fid = mapfid(f);
11234366bb71SDavid du Colombier 	sendmsg(&s, &s.thdr);
11244366bb71SDavid du Colombier 	rcvmsg(&s, &s.rhdr);
11254366bb71SDavid du Colombier 
11264366bb71SDavid du Colombier 	if(statson)
11274366bb71SDavid du Colombier 		cfsstat.sm[type].t += nsec() - cfsstat.sm[type].s;
11284366bb71SDavid du Colombier 
11294366bb71SDavid du Colombier 	return s.thdr.type+1 == s.rhdr.type ? 0 : -1;
11304366bb71SDavid du Colombier }
11314366bb71SDavid du Colombier 
11324366bb71SDavid du Colombier /*
11334366bb71SDavid du Colombier  *  send/receive messages with logging
11344366bb71SDavid du Colombier  */
11354366bb71SDavid du Colombier 
11364366bb71SDavid du Colombier void
sendmsg(P9fs * p,Fcall * f)11374366bb71SDavid du Colombier sendmsg(P9fs *p, Fcall *f)
11384366bb71SDavid du Colombier {
11394366bb71SDavid du Colombier 	DPRINT(2, "->%s: %F\n", p->name, f);
11404366bb71SDavid du Colombier 
11414366bb71SDavid du Colombier 	p->len = convS2M(f, p->sndbuf, messagesize);
11424366bb71SDavid du Colombier 	if(p->len <= 0)
11434366bb71SDavid du Colombier 		error("convS2M");
11444366bb71SDavid du Colombier 	if(write(p->fd[1], p->sndbuf, p->len)!=p->len)
11454366bb71SDavid du Colombier 		error("sendmsg");
11464366bb71SDavid du Colombier }
11474366bb71SDavid du Colombier 
11484366bb71SDavid du Colombier void
rcvmsg(P9fs * p,Fcall * f)11494366bb71SDavid du Colombier rcvmsg(P9fs *p, Fcall *f)
11504366bb71SDavid du Colombier {
11514366bb71SDavid du Colombier 	int rlen;
11524366bb71SDavid du Colombier 	char buf[128];
11534366bb71SDavid du Colombier 
11544366bb71SDavid du Colombier 	p->len = read9pmsg(p->fd[0], p->rcvbuf[p->cb], sizeof(p->rcvbuf[0]));
11554366bb71SDavid du Colombier 	if(p->len <= 0){
11564366bb71SDavid du Colombier 		snprint(buf, sizeof buf, "read9pmsg(%d)->%ld: %r",
11574366bb71SDavid du Colombier 			p->fd[0], p->len);
11584366bb71SDavid du Colombier 		error(buf);
11594366bb71SDavid du Colombier 	}
11604366bb71SDavid du Colombier 
11614366bb71SDavid du Colombier 	if((rlen = convM2S(p->rcvbuf[p->cb], p->len, f)) != p->len)
11624366bb71SDavid du Colombier 		error("rcvmsg format error, expected length %d, got %d",
11634366bb71SDavid du Colombier 			rlen, p->len);
11644366bb71SDavid du Colombier 	DPRINT(2, "<-%s: %F\n", p->name, f);
11654366bb71SDavid du Colombier 	p->cb = (p->cb+1)%nelem(p->rcvbuf);
11664366bb71SDavid du Colombier }
11674366bb71SDavid du Colombier 
11684366bb71SDavid du Colombier void
dump(void * a,int len)11694366bb71SDavid du Colombier dump(void *a, int len)
11704366bb71SDavid du Colombier {
11714366bb71SDavid du Colombier 	uchar *p;
11724366bb71SDavid du Colombier 
11734366bb71SDavid du Colombier 	p = a;
11744366bb71SDavid du Colombier 	fprint(2, "%d bytes", len);
11754366bb71SDavid du Colombier 	while(len-- > 0)
11764366bb71SDavid du Colombier 		fprint(2, " %.2ux", *p++);
11774366bb71SDavid du Colombier 	fprint(2, "\n");
11784366bb71SDavid du Colombier }
11794366bb71SDavid du Colombier 
11804366bb71SDavid du Colombier /*
11814366bb71SDavid du Colombier  * requests (later, unused now)
11824366bb71SDavid du Colombier  */
11834366bb71SDavid du Colombier 
11844366bb71SDavid du Colombier void
readtmsgproc(void * a)11854366bb71SDavid du Colombier readtmsgproc(void *a)
11864366bb71SDavid du Colombier {
11874366bb71SDavid du Colombier 	//uint messagesize;
11884366bb71SDavid du Colombier 	int n;
11894366bb71SDavid du Colombier 	Req *r;
11904366bb71SDavid du Colombier 	Channel *reqs;
11914366bb71SDavid du Colombier 
11924366bb71SDavid du Colombier 	reqs = a;
11934366bb71SDavid du Colombier 	for(;;){
11944366bb71SDavid du Colombier 		r = malloc(sizeof(*r)+messagesize);
11954366bb71SDavid du Colombier 		r->msize = messagesize;
11964366bb71SDavid du Colombier 		r->buf = (uchar*)(r+1);
11974366bb71SDavid du Colombier 		n = read9pmsg(c.fd[0], r->buf, r->msize);
11984366bb71SDavid du Colombier 		if(n <= 0){
11994366bb71SDavid du Colombier 			free(r);
12004366bb71SDavid du Colombier 			sendp(reqs, nil);
12014366bb71SDavid du Colombier 			threadexits(nil);
12024366bb71SDavid du Colombier 		}
12034366bb71SDavid du Colombier 		if(convM2S(r->buf, n, &r->t) != n){
12044366bb71SDavid du Colombier 			free(r);
12054366bb71SDavid du Colombier 			error("convM2S error in readtmsgproc");
12064366bb71SDavid du Colombier 		}
12074366bb71SDavid du Colombier 		sendp(reqs, r);
12084366bb71SDavid du Colombier 	}
12094366bb71SDavid du Colombier }
12104366bb71SDavid du Colombier 
12114366bb71SDavid du Colombier void
readrmsgproc(void * a)12124366bb71SDavid du Colombier readrmsgproc(void *a)
12134366bb71SDavid du Colombier {
12144366bb71SDavid du Colombier 	//uint messagesize;
12154366bb71SDavid du Colombier 	int n;
12164366bb71SDavid du Colombier 	Fcall *r;
12174366bb71SDavid du Colombier 	uchar *buf;
12184366bb71SDavid du Colombier 	Channel *reps;
12194366bb71SDavid du Colombier 
12204366bb71SDavid du Colombier 	reps = a;
12214366bb71SDavid du Colombier 	for(;;){
12224366bb71SDavid du Colombier 		r = malloc(sizeof(*r)+messagesize);
12234366bb71SDavid du Colombier 		buf = (uchar*)(r+1);
12244366bb71SDavid du Colombier 		n = read9pmsg(c.fd[0], buf, messagesize);
12254366bb71SDavid du Colombier 		if(n <= 0){
12264366bb71SDavid du Colombier 			free(r);
12274366bb71SDavid du Colombier 			sendp(reps, nil);
12284366bb71SDavid du Colombier 			threadexits(nil);
12294366bb71SDavid du Colombier 		}
12304366bb71SDavid du Colombier 		if(convM2S(buf, n, r) != n){
12314366bb71SDavid du Colombier 			free(r);
12324366bb71SDavid du Colombier 			error("convM2S error in readtmsgproc");
12334366bb71SDavid du Colombier 		}
12344366bb71SDavid du Colombier 		sendp(reps, r);
12354366bb71SDavid du Colombier 	}
12364366bb71SDavid du Colombier }
12374366bb71SDavid du Colombier 
12384366bb71SDavid du Colombier void
freereq(Req * r)12394366bb71SDavid du Colombier freereq(Req *r)
12404366bb71SDavid du Colombier {
12414366bb71SDavid du Colombier 	free(r);
12424366bb71SDavid du Colombier }
12434366bb71SDavid du Colombier 
12444366bb71SDavid du Colombier /*
12454366bb71SDavid du Colombier  * server fids
12464366bb71SDavid du Colombier */
12474366bb71SDavid du Colombier 
12484366bb71SDavid du Colombier SFid*
allocsfid(void)12494366bb71SDavid du Colombier allocsfid(void)
12504366bb71SDavid du Colombier {
12514366bb71SDavid du Colombier 	SFid *sf;
12524366bb71SDavid du Colombier 
12534366bb71SDavid du Colombier 	sf = freefids;
12544366bb71SDavid du Colombier 	if(sf != nil){
12554366bb71SDavid du Colombier 		freefids = sf->next;
12564366bb71SDavid du Colombier 		sf->ref = 1;
12574366bb71SDavid du Colombier 		return sf;
12584366bb71SDavid du Colombier 	}
12594366bb71SDavid du Colombier 	sf = mallocz(sizeof(*sf), 1);
12604366bb71SDavid du Colombier 	sf->ref = 1;
12614366bb71SDavid du Colombier 	sf->fid = ++fidgen;
12624366bb71SDavid du Colombier 	return sf;
12634366bb71SDavid du Colombier }
12644366bb71SDavid du Colombier 
12654366bb71SDavid du Colombier void
freesfid(SFid * sf)12664366bb71SDavid du Colombier freesfid(SFid *sf)
12674366bb71SDavid du Colombier {
12684366bb71SDavid du Colombier 	if(--sf->ref != 0)
12694366bb71SDavid du Colombier 		return;
12704366bb71SDavid du Colombier 	/* leave sf->fid alone */
12714366bb71SDavid du Colombier 	sf->next = freefids;
12724366bb71SDavid du Colombier 	freefids = sf;
12734366bb71SDavid du Colombier }
12744366bb71SDavid du Colombier 
12754366bb71SDavid du Colombier SFid*
sfclone(SFid * sf)12764366bb71SDavid du Colombier sfclone(SFid *sf)
12774366bb71SDavid du Colombier {
12784366bb71SDavid du Colombier 	Fcall t, r;
12794366bb71SDavid du Colombier 	SFid *cf;
12804366bb71SDavid du Colombier 
12814366bb71SDavid du Colombier 	cf = allocsfid();
12824366bb71SDavid du Colombier 	t.tag = c.thdr.tag;
12834366bb71SDavid du Colombier 	t.type = Twalk;
12844366bb71SDavid du Colombier 	t.fid = sf->fid;
12854366bb71SDavid du Colombier 	t.newfid = cf->fid;
12864366bb71SDavid du Colombier 	t.nwname = 0;
12874366bb71SDavid du Colombier 	sendmsg(&s, &t);
12884366bb71SDavid du Colombier 	rcvmsg(&s, &r);
12894366bb71SDavid du Colombier 	if(r.type == Rerror){
12904366bb71SDavid du Colombier 		werrstr("%s", r.ename);
12914366bb71SDavid du Colombier 		return nil;
12924366bb71SDavid du Colombier 	}
12934366bb71SDavid du Colombier 	assert(r.type == Rwalk);	/* TO DO: out of order */
12944366bb71SDavid du Colombier 	return cf;
12954366bb71SDavid du Colombier }
12964366bb71SDavid du Colombier 
12974366bb71SDavid du Colombier Dir*
sfstat(Path * p,u32int tag)12984366bb71SDavid du Colombier sfstat(Path *p, u32int tag)
12994366bb71SDavid du Colombier {
13004366bb71SDavid du Colombier 	Fcall t, r;
13014366bb71SDavid du Colombier 	Dir *d;
13024366bb71SDavid du Colombier 
13034366bb71SDavid du Colombier 	assert(p->sfid != nil);
13044366bb71SDavid du Colombier 	t.tag = tag;
13054366bb71SDavid du Colombier 	t.type = Tstat;
13064366bb71SDavid du Colombier 	t.fid = p->sfid->fid;
13074366bb71SDavid du Colombier 	sendmsg(&s, &t);
13084366bb71SDavid du Colombier 	rcvmsg(&s, &r);
13094366bb71SDavid du Colombier 	if(r.type == Rerror){
13104366bb71SDavid du Colombier 		werrstr("%s", r.ename);
13114366bb71SDavid du Colombier 		return nil;
13124366bb71SDavid du Colombier 	}
13134366bb71SDavid du Colombier 	d = malloc(sizeof(*d)+r.nstat);
13144366bb71SDavid du Colombier 	if(convM2D(r.stat, r.nstat, d, (char*)(d+1)) == 0){
13154366bb71SDavid du Colombier 		free(d);
13164366bb71SDavid du Colombier 		werrstr("invalid stat data");
13174366bb71SDavid du Colombier 		return nil;
13184366bb71SDavid du Colombier 	}
13194366bb71SDavid du Colombier 	return d;
13204366bb71SDavid du Colombier }
13214366bb71SDavid du Colombier 
13224366bb71SDavid du Colombier void
sfclunk(SFid * sf)13234366bb71SDavid du Colombier sfclunk(SFid *sf)
13244366bb71SDavid du Colombier {
13254366bb71SDavid du Colombier 	Fcall t, r;
13264366bb71SDavid du Colombier 
13274366bb71SDavid du Colombier 	if(sf->ref > 1)
13284366bb71SDavid du Colombier 		return;
13294366bb71SDavid du Colombier 	t.tag = c.thdr.tag;
13304366bb71SDavid du Colombier 	t.type = Tclunk;
13314366bb71SDavid du Colombier 	t.fid = sf->fid;
13324366bb71SDavid du Colombier 	sendmsg(&s, &t);
13334366bb71SDavid du Colombier 	rcvmsg(&s, &r);
13344366bb71SDavid du Colombier 	/* don't care about result */
13354366bb71SDavid du Colombier }
13364366bb71SDavid du Colombier 
13374366bb71SDavid du Colombier void
printpath(Path * p,int level)13384366bb71SDavid du Colombier printpath(Path *p, int level)
13394366bb71SDavid du Colombier {
13404366bb71SDavid du Colombier 	Path *q;
13414366bb71SDavid du Colombier 
13424366bb71SDavid du Colombier 	for(int i = 0; i < level; i++)
13434366bb71SDavid du Colombier 		print("\t");
13444366bb71SDavid du Colombier 	print("%q [%lud] %q\n", p->name, p->ref, p->inval?p->inval:"");
13454366bb71SDavid du Colombier 	for(q = p->child; q != nil; q = q->next)
13464366bb71SDavid du Colombier 		printpath(q, level+1);
13474366bb71SDavid du Colombier }
13484366bb71SDavid du Colombier 
13494366bb71SDavid du Colombier int
ctltest(Fid * mf)13504366bb71SDavid du Colombier ctltest(Fid *mf)
13514366bb71SDavid du Colombier {
13524366bb71SDavid du Colombier 	return mf->qid.type == ctlqid.type && mf->qid.path == ctlqid.path;
13534366bb71SDavid du Colombier }
13544366bb71SDavid du Colombier 
13554366bb71SDavid du Colombier char *mname[]={
13564366bb71SDavid du Colombier 	[Tversion]		"Tversion",
13574366bb71SDavid du Colombier 	[Tauth]	"Tauth",
13584366bb71SDavid du Colombier 	[Tflush]	"Tflush",
13594366bb71SDavid du Colombier 	[Tattach]	"Tattach",
13604366bb71SDavid du Colombier 	[Twalk]		"Twalk",
13614366bb71SDavid du Colombier 	[Topen]		"Topen",
13624366bb71SDavid du Colombier 	[Tcreate]	"Tcreate",
13634366bb71SDavid du Colombier 	[Tclunk]	"Tclunk",
13644366bb71SDavid du Colombier 	[Tread]		"Tread",
13654366bb71SDavid du Colombier 	[Twrite]	"Twrite",
13664366bb71SDavid du Colombier 	[Tremove]	"Tremove",
13674366bb71SDavid du Colombier 	[Tstat]		"Tstat",
13684366bb71SDavid du Colombier 	[Twstat]	"Twstat",
13694366bb71SDavid du Colombier 	[Rversion]	"Rversion",
13704366bb71SDavid du Colombier 	[Rauth]	"Rauth",
13714366bb71SDavid du Colombier 	[Rerror]	"Rerror",
13724366bb71SDavid du Colombier 	[Rflush]	"Rflush",
13734366bb71SDavid du Colombier 	[Rattach]	"Rattach",
13744366bb71SDavid du Colombier 	[Rwalk]		"Rwalk",
13754366bb71SDavid du Colombier 	[Ropen]		"Ropen",
13764366bb71SDavid du Colombier 	[Rcreate]	"Rcreate",
13774366bb71SDavid du Colombier 	[Rclunk]	"Rclunk",
13784366bb71SDavid du Colombier 	[Rread]		"Rread",
13794366bb71SDavid du Colombier 	[Rwrite]	"Rwrite",
13804366bb71SDavid du Colombier 	[Rremove]	"Rremove",
13814366bb71SDavid du Colombier 	[Rstat]		"Rstat",
13824366bb71SDavid du Colombier 	[Rwstat]	"Rwstat",
13834366bb71SDavid du Colombier 			0,
13844366bb71SDavid du Colombier };
13854366bb71SDavid du Colombier 
13864366bb71SDavid du Colombier void
genstats(void)13874366bb71SDavid du Colombier genstats(void)
13884366bb71SDavid du Colombier {
13894366bb71SDavid du Colombier 	int i;
13904366bb71SDavid du Colombier 	char *p;
13914366bb71SDavid du Colombier 
13924366bb71SDavid du Colombier 	p = statbuf;
13934366bb71SDavid du Colombier 
13944366bb71SDavid du Colombier 	p += snprint(p, sizeof statbuf+statbuf-p,
13954366bb71SDavid du Colombier 		"        Client                          Server\n");
13964366bb71SDavid du Colombier 	p += snprint(p, sizeof statbuf+statbuf-p,
13974366bb71SDavid du Colombier 	    "   #calls     Δ  ms/call    Δ      #calls     Δ  ms/call    Δ\n");
13984366bb71SDavid du Colombier 	for(i = 0; i < nelem(cfsstat.cm); i++)
13994366bb71SDavid du Colombier 		if(cfsstat.cm[i].n || cfsstat.sm[i].n){
14004366bb71SDavid du Colombier 			p += snprint(p, sizeof statbuf+statbuf-p,
14014366bb71SDavid du Colombier 				"%7lud %7lud ", cfsstat.cm[i].n,
14024366bb71SDavid du Colombier 				cfsstat.cm[i].n - cfsprev.cm[i].n);
14034366bb71SDavid du Colombier 			if(cfsstat.cm[i].n)
14044366bb71SDavid du Colombier 				p += snprint(p, sizeof statbuf+statbuf-p,
14054366bb71SDavid du Colombier 					"%7.3f ", 0.000001*cfsstat.cm[i].t/
14064366bb71SDavid du Colombier 					cfsstat.cm[i].n);
14074366bb71SDavid du Colombier 			else
14084366bb71SDavid du Colombier 				p += snprint(p, sizeof statbuf+statbuf-p,
14094366bb71SDavid du Colombier 					"        ");
14104366bb71SDavid du Colombier 			if(cfsstat.cm[i].n - cfsprev.cm[i].n)
14114366bb71SDavid du Colombier 				p += snprint(p, sizeof statbuf+statbuf-p,
14124366bb71SDavid du Colombier 					"%7.3f ", 0.000001*
14134366bb71SDavid du Colombier 					(cfsstat.cm[i].t - cfsprev.cm[i].t)/
14144366bb71SDavid du Colombier 					(cfsstat.cm[i].n - cfsprev.cm[i].n));
14154366bb71SDavid du Colombier 			else
14164366bb71SDavid du Colombier 				p += snprint(p, sizeof statbuf+statbuf-p,
14174366bb71SDavid du Colombier 					"        ");
14184366bb71SDavid du Colombier 			p += snprint(p, sizeof statbuf+statbuf-p,
14194366bb71SDavid du Colombier 				"%7lud %7lud ", cfsstat.sm[i].n,
14204366bb71SDavid du Colombier 				cfsstat.sm[i].n - cfsprev.sm[i].n);
14214366bb71SDavid du Colombier 			if(cfsstat.sm[i].n)
14224366bb71SDavid du Colombier 				p += snprint(p, sizeof statbuf+statbuf-p,
14234366bb71SDavid du Colombier 					"%7.3f ", 0.000001*cfsstat.sm[i].t/
14244366bb71SDavid du Colombier 					cfsstat.sm[i].n);
14254366bb71SDavid du Colombier 			else
14264366bb71SDavid du Colombier 				p += snprint(p, sizeof statbuf+statbuf-p,
14274366bb71SDavid du Colombier 					"        ");
14284366bb71SDavid du Colombier 			if(cfsstat.sm[i].n - cfsprev.sm[i].n)
14294366bb71SDavid du Colombier 				p += snprint(p, sizeof statbuf+statbuf-p,
14304366bb71SDavid du Colombier 					"%7.3f ", 0.000001*
14314366bb71SDavid du Colombier 					(cfsstat.sm[i].t - cfsprev.sm[i].t)/
14324366bb71SDavid du Colombier 					(cfsstat.sm[i].n - cfsprev.sm[i].n));
14334366bb71SDavid du Colombier 			else
14344366bb71SDavid du Colombier 				p += snprint(p, sizeof statbuf+statbuf-p,
14354366bb71SDavid du Colombier 					"        ");
14364366bb71SDavid du Colombier 			p += snprint(p, sizeof statbuf+statbuf-p, "%s\n",
14374366bb71SDavid du Colombier 				mname[i]);
14384366bb71SDavid du Colombier 		}
14394366bb71SDavid du Colombier 	p += snprint(p, sizeof statbuf+statbuf-p, "%7lud %7lud ndirread\n",
14404366bb71SDavid du Colombier 		cfsstat.ndirread, cfsstat.ndirread - cfsprev.ndirread);
14414366bb71SDavid du Colombier 	p += snprint(p, sizeof statbuf+statbuf-p, "%7lud %7lud ndelegateread\n",
14424366bb71SDavid du Colombier 		cfsstat.ndelegateread, cfsstat.ndelegateread -
14434366bb71SDavid du Colombier 		cfsprev.ndelegateread);
14444366bb71SDavid du Colombier 	p += snprint(p, sizeof statbuf+statbuf-p, "%7lud %7lud ninsert\n",
14454366bb71SDavid du Colombier 		cfsstat.ninsert, cfsstat.ninsert - cfsprev.ninsert);
14464366bb71SDavid du Colombier 	p += snprint(p, sizeof statbuf+statbuf-p, "%7lud %7lud ndelete\n",
14474366bb71SDavid du Colombier 		cfsstat.ndelete, cfsstat.ndelete - cfsprev.ndelete);
14484366bb71SDavid du Colombier 	p += snprint(p, sizeof statbuf+statbuf-p, "%7lud %7lud nupdate\n",
14494366bb71SDavid du Colombier 		cfsstat.nupdate, cfsstat.nupdate - cfsprev.nupdate);
14504366bb71SDavid du Colombier 
14514366bb71SDavid du Colombier 	p += snprint(p, sizeof statbuf+statbuf-p, "%7llud %7llud bytesread\n",
14524366bb71SDavid du Colombier 		cfsstat.bytesread, cfsstat.bytesread - cfsprev.bytesread);
14534366bb71SDavid du Colombier 	p += snprint(p, sizeof statbuf+statbuf-p, "%7llud %7llud byteswritten\n",
14544366bb71SDavid du Colombier 		cfsstat.byteswritten, cfsstat.byteswritten -
14554366bb71SDavid du Colombier 		cfsprev.byteswritten);
14564366bb71SDavid du Colombier 	p += snprint(p, sizeof statbuf+statbuf-p, "%7llud %7llud bytesfromserver\n",
14574366bb71SDavid du Colombier 		cfsstat.bytesfromserver, cfsstat.bytesfromserver -
14584366bb71SDavid du Colombier 		cfsprev.bytesfromserver);
14594366bb71SDavid du Colombier 	p += snprint(p, sizeof statbuf+statbuf-p, "%7llud %7llud bytesfromdirs\n",
14604366bb71SDavid du Colombier 		cfsstat.bytesfromdirs, cfsstat.bytesfromdirs -
14614366bb71SDavid du Colombier 		cfsprev.bytesfromdirs);
14624366bb71SDavid du Colombier 	p += snprint(p, sizeof statbuf+statbuf-p, "%7llud %7llud bytesfromcache\n",
14634366bb71SDavid du Colombier 		cfsstat.bytesfromcache, cfsstat.bytesfromcache -
14644366bb71SDavid du Colombier 		cfsprev.bytesfromcache);
14654366bb71SDavid du Colombier 	p += snprint(p, sizeof statbuf+statbuf-p, "%7llud %7llud bytestocache\n",
14664366bb71SDavid du Colombier 		cfsstat.bytestocache, cfsstat.bytestocache -
14674366bb71SDavid du Colombier 		cfsprev.bytestocache);
14684366bb71SDavid du Colombier 	statlen = p - statbuf;
14694366bb71SDavid du Colombier 	cfsprev = cfsstat;
14704366bb71SDavid du Colombier }
14714366bb71SDavid du Colombier 
14724366bb71SDavid du Colombier /*
14734366bb71SDavid du Colombier  * paths
14744366bb71SDavid du Colombier  */
14754366bb71SDavid du Colombier 
14764366bb71SDavid du Colombier Path*
newpath(Path * parent,char * name,Qid qid)14774366bb71SDavid du Colombier newpath(Path *parent, char *name, Qid qid)
14784366bb71SDavid du Colombier {
14794366bb71SDavid du Colombier 	Path *p;
14804366bb71SDavid du Colombier 
14814366bb71SDavid du Colombier 	if(parent != nil){
14824366bb71SDavid du Colombier 		for(p = parent->child; p != nil; p = p->next){
14834366bb71SDavid du Colombier 			if(strcmp(p->name, name) == 0){
14844366bb71SDavid du Colombier 				if(p->inval != nil){
14854366bb71SDavid du Colombier 					free(p->inval);
14864366bb71SDavid du Colombier 					p->inval = nil;
14874366bb71SDavid du Colombier 				}
14884366bb71SDavid du Colombier 				p->qid = qid;
14894366bb71SDavid du Colombier 				p->ref++;
14904366bb71SDavid du Colombier 				return p;
14914366bb71SDavid du Colombier 			}
14924366bb71SDavid du Colombier 		}
14934366bb71SDavid du Colombier 	}
14944366bb71SDavid du Colombier 	p = mallocz(sizeof(*p), 1);
14954366bb71SDavid du Colombier 	p->ref = 2;
14964366bb71SDavid du Colombier 	if(name != nil)
14974366bb71SDavid du Colombier 		p->name = strdup(name);
14984366bb71SDavid du Colombier 	p->qid = qid;
14994366bb71SDavid du Colombier 	p->parent = parent;
15004366bb71SDavid du Colombier 	if(parent != nil){
15014366bb71SDavid du Colombier 		parent->ref++;
15024366bb71SDavid du Colombier 		p->next = parent->child;
15034366bb71SDavid du Colombier 		parent->child = p;
15044366bb71SDavid du Colombier 	}
15054366bb71SDavid du Colombier 	return p;
15064366bb71SDavid du Colombier }
15074366bb71SDavid du Colombier 
15084366bb71SDavid du Colombier void
freeinval(Path * p)1509*518acb85SDavid du Colombier freeinval(Path *p)
1510*518acb85SDavid du Colombier {
1511*518acb85SDavid du Colombier 	Path *q, **r;
1512*518acb85SDavid du Colombier 
1513*518acb85SDavid du Colombier 	for(r = &p->parent->child; (q = *r) != nil; r = &q->next){
1514*518acb85SDavid du Colombier 		if(q == p)
1515*518acb85SDavid du Colombier 			continue;
1516*518acb85SDavid du Colombier 		if(strcmp(q->name, p->name) == 0 && q->inval != nil){
1517*518acb85SDavid du Colombier 			if(q->ref > 1){
1518*518acb85SDavid du Colombier 				*r = q->next;
1519*518acb85SDavid du Colombier 				q->next = p->next;
1520*518acb85SDavid du Colombier 				p->next = q;
1521*518acb85SDavid du Colombier 			}
1522*518acb85SDavid du Colombier 			freepath(q);
1523*518acb85SDavid du Colombier 			break;
1524*518acb85SDavid du Colombier 		}
1525*518acb85SDavid du Colombier 	}
1526*518acb85SDavid du Colombier }
1527*518acb85SDavid du Colombier 
1528*518acb85SDavid du Colombier void
setinval(Path * p,char * err)15294366bb71SDavid du Colombier setinval(Path *p, char *err)
15304366bb71SDavid du Colombier {
15314366bb71SDavid du Colombier 	if(p->inval != nil){
15324366bb71SDavid du Colombier 		free(p->inval);
15334366bb71SDavid du Colombier 		p->inval = nil;
15344366bb71SDavid du Colombier 	}
15354366bb71SDavid du Colombier 	if(err != nil)
15364366bb71SDavid du Colombier 		p->inval = strdup(err);
15374366bb71SDavid du Colombier }
15384366bb71SDavid du Colombier 
15394366bb71SDavid du Colombier void
badpath(Path * parent,char * name,char * err)15404366bb71SDavid du Colombier badpath(Path *parent, char *name, char *err)
15414366bb71SDavid du Colombier {
15424366bb71SDavid du Colombier 	Path *p;
15434366bb71SDavid du Colombier 
15444366bb71SDavid du Colombier 	for(p = parent->child; p != nil; p = p->next){
15454366bb71SDavid du Colombier 		if(strcmp(p->name, name) == 0){
15464366bb71SDavid du Colombier 			setinval(p, err);
15474366bb71SDavid du Colombier 			return;
15484366bb71SDavid du Colombier 		}
15494366bb71SDavid du Colombier 	}
15504366bb71SDavid du Colombier 	p = mallocz(sizeof(*p), 1);
15514366bb71SDavid du Colombier 	p->ref = 2;
15524366bb71SDavid du Colombier 	if(name != nil)
15534366bb71SDavid du Colombier 		p->name = strdup(name);
15544366bb71SDavid du Colombier 	p->inval = strdup(err);
15554366bb71SDavid du Colombier 	p->parent = parent;
15564366bb71SDavid du Colombier 	if(parent != nil){
15574366bb71SDavid du Colombier 		parent->ref++;
15584366bb71SDavid du Colombier 		p->next = parent->child;
15594366bb71SDavid du Colombier 		parent->child = p;
15604366bb71SDavid du Colombier 	}
15614366bb71SDavid du Colombier }
15624366bb71SDavid du Colombier 
15634366bb71SDavid du Colombier void
fileinval(Path * p)15644366bb71SDavid du Colombier fileinval(Path *p)
15654366bb71SDavid du Colombier {
15664366bb71SDavid du Colombier 	if(p->file != nil){
15674366bb71SDavid du Colombier 		putfile(p->file);
15684366bb71SDavid du Colombier 		p->file = nil;
15694366bb71SDavid du Colombier 	}
15704366bb71SDavid du Colombier }
15714366bb71SDavid du Colombier 
15724366bb71SDavid du Colombier void
freepath(Path * p)15734366bb71SDavid du Colombier freepath(Path *p)
15744366bb71SDavid du Colombier {
15754366bb71SDavid du Colombier 	Path *q, **r;
15764366bb71SDavid du Colombier 
15774366bb71SDavid du Colombier 	while(p != nil && --p->ref == 0){
15784366bb71SDavid du Colombier 		if(p->child != nil)
15794366bb71SDavid du Colombier 			error("freepath child");
15804366bb71SDavid du Colombier 		q = p->parent;
15814366bb71SDavid du Colombier 		if(q != nil){
15824366bb71SDavid du Colombier 			for(r = &q->child; *r != nil; r = &(*r)->next){
15834366bb71SDavid du Colombier 				if(*r == p){
15844366bb71SDavid du Colombier 					*r = p->next;
15854366bb71SDavid du Colombier 					break;
15864366bb71SDavid du Colombier 				}
15874366bb71SDavid du Colombier 			}
15884366bb71SDavid du Colombier 		}
15894366bb71SDavid du Colombier 		if(p->sfid != nil){
15904366bb71SDavid du Colombier 			/* TO DO: could queue these for a great clunking proc */
15914366bb71SDavid du Colombier 			sfclunk(p->sfid);
15924366bb71SDavid du Colombier 			p->sfid = nil;
15934366bb71SDavid du Colombier 		}
15944366bb71SDavid du Colombier 		if(p->inval != nil)
15954366bb71SDavid du Colombier 			free(p->inval);
15964366bb71SDavid du Colombier 		if(p->file != nil)
15974366bb71SDavid du Colombier 			putfile(p->file);
15984366bb71SDavid du Colombier 		free(p->name);
15994366bb71SDavid du Colombier 		free(p);
16004366bb71SDavid du Colombier 		p = q;
16014366bb71SDavid du Colombier 	}
16024366bb71SDavid du Colombier }
16034366bb71SDavid du Colombier 
16044366bb71SDavid du Colombier static char*
pathstr1(Path * p,char * ep)16054366bb71SDavid du Colombier pathstr1(Path *p, char *ep)
16064366bb71SDavid du Colombier {
16074366bb71SDavid du Colombier 	ep -= strlen(p->name);
16084366bb71SDavid du Colombier 	memmove(ep, p->name, strlen(p->name));
16094366bb71SDavid du Colombier 	if(p->parent != nil){
16104366bb71SDavid du Colombier 		*--ep = '/';
16114366bb71SDavid du Colombier 		ep = pathstr1(p->parent, ep);
16124366bb71SDavid du Colombier 	}
16134366bb71SDavid du Colombier 	return ep;
16144366bb71SDavid du Colombier }
16154366bb71SDavid du Colombier 
16164366bb71SDavid du Colombier char*
pathstr(Path * p)16174366bb71SDavid du Colombier pathstr(Path *p)
16184366bb71SDavid du Colombier {
16194366bb71SDavid du Colombier 	static char buf[1000];
16204366bb71SDavid du Colombier 	return pathstr1(p, buf+sizeof(buf)-1);
16214366bb71SDavid du Colombier }
16224366bb71SDavid du Colombier 
16234366bb71SDavid du Colombier /*
16244366bb71SDavid du Colombier  * files
16254366bb71SDavid du Colombier  */
16264366bb71SDavid du Colombier 
16274366bb71SDavid du Colombier void
openfile(Fid * mf,int omode,u32int iounit,SFid * sfid)16284366bb71SDavid du Colombier openfile(Fid *mf, int omode, u32int iounit, SFid *sfid)
16294366bb71SDavid du Colombier {
16304366bb71SDavid du Colombier 	/* open mf and cache open File at p */
16314366bb71SDavid du Colombier 	Path *p;
16324366bb71SDavid du Colombier 	File *file;
16334366bb71SDavid du Colombier 	SFid *osfid;
16344366bb71SDavid du Colombier 
16354366bb71SDavid du Colombier 	p = mf->path;
16364366bb71SDavid du Colombier 	file = p->file;
16374366bb71SDavid du Colombier 	if(file == nil){
16384366bb71SDavid du Colombier 		file = mallocz(sizeof(*file), 1);
16394366bb71SDavid du Colombier 		file->ref = 1;
16404366bb71SDavid du Colombier 		file->qid = mf->qid;	/* TO DO: check for clone files */
16414366bb71SDavid du Colombier 		file->clength = MAXLEN;
16424366bb71SDavid du Colombier 		p->file = file;
16434366bb71SDavid du Colombier 	}
16444366bb71SDavid du Colombier 	osfid = file->open[omode];
16454366bb71SDavid du Colombier 	if(osfid != nil){
16464366bb71SDavid du Colombier 		DPRINT(2, "openfile: existing sfid %ud open %d\n", osfid->fid, omode);
16474366bb71SDavid du Colombier 		return;
16484366bb71SDavid du Colombier 	}
16494366bb71SDavid du Colombier 	DPRINT(2, "openfile: cached %ud for %q mode %d\n", sfid->fid, pathstr(p), omode);
16504366bb71SDavid du Colombier 	sfid->ref++;
16514366bb71SDavid du Colombier 	file->open[omode] = sfid;
16524366bb71SDavid du Colombier 	if(file->iounit == 0)
16534366bb71SDavid du Colombier 		file->iounit = iounit;	/* BUG: might vary */
16544366bb71SDavid du Colombier }
16554366bb71SDavid du Colombier 
16564366bb71SDavid du Colombier void
closefile(Fid * mf,int mustclunk,int removed)16574366bb71SDavid du Colombier closefile(Fid *mf, int mustclunk, int removed)
16584366bb71SDavid du Colombier {
16594366bb71SDavid du Colombier 	Path *p;
16604366bb71SDavid du Colombier 	File *file;
16614366bb71SDavid du Colombier 	SFid *sfid, *osfid;
16624366bb71SDavid du Colombier 
16634366bb71SDavid du Colombier 	if((sfid = mf->opened) == nil)
16644366bb71SDavid du Colombier 		return;
16654366bb71SDavid du Colombier 	p = mf->path;
16664366bb71SDavid du Colombier 	file = p->file;
16674366bb71SDavid du Colombier 	if(file == nil){	/* TO DO: messy */
16684366bb71SDavid du Colombier 		mf->opened = nil;
16694366bb71SDavid du Colombier 		if(sfid->ref == 1 && mustclunk)
16704366bb71SDavid du Colombier 			sfclunk(sfid);
16714366bb71SDavid du Colombier 		freesfid(sfid);
16724366bb71SDavid du Colombier 		return;
16734366bb71SDavid du Colombier 	}
16744366bb71SDavid du Colombier 	osfid = file->open[mf->mode];
16754366bb71SDavid du Colombier 	if(osfid == sfid){
16764366bb71SDavid du Colombier 		if(removed || sfid->ref == 1)
16774366bb71SDavid du Colombier 			file->open[mf->mode] = nil;
16784366bb71SDavid du Colombier 		if(sfid->ref == 1){
16794366bb71SDavid du Colombier 			if(mustclunk)
16804366bb71SDavid du Colombier 				sfclunk(sfid);
16814366bb71SDavid du Colombier 			freesfid(sfid);
16824366bb71SDavid du Colombier 		}else
16834366bb71SDavid du Colombier 			sfid->ref--;
16844366bb71SDavid du Colombier 	}
16854366bb71SDavid du Colombier 	mf->opened = nil;
16864366bb71SDavid du Colombier }
16874366bb71SDavid du Colombier 
16884366bb71SDavid du Colombier SFid*
alreadyopen(Fid * mf,uint mode)16894366bb71SDavid du Colombier alreadyopen(Fid *mf, uint mode)
16904366bb71SDavid du Colombier {
16914366bb71SDavid du Colombier 	File *file;
16924366bb71SDavid du Colombier 	SFid *sfid;
16934366bb71SDavid du Colombier 
16944366bb71SDavid du Colombier 	file = mf->path->file;
16954366bb71SDavid du Colombier 	if(file == nil)
16964366bb71SDavid du Colombier 		return nil;
16974366bb71SDavid du Colombier 	sfid = file->open[mode&3];
16984366bb71SDavid du Colombier 	if(sfid == nil)
16994366bb71SDavid du Colombier 		return nil;
17004366bb71SDavid du Colombier 	DPRINT(2, "openfile: existing sfid %ud open %d\n", sfid->fid, mode&3);
17014366bb71SDavid du Colombier 	sfid->ref++;
17024366bb71SDavid du Colombier 	return sfid;
17034366bb71SDavid du Colombier }
17044366bb71SDavid du Colombier 
17054366bb71SDavid du Colombier void
copystat(File * file,Dir * d,int iswstat)17064366bb71SDavid du Colombier copystat(File *file, Dir *d, int iswstat)
17074366bb71SDavid du Colombier {
17084366bb71SDavid du Colombier 	if(d->length != ~(vlong)0){
17094366bb71SDavid du Colombier 		/* length change: discard cached data */
17104366bb71SDavid du Colombier 		if(iswstat && file->length < file->clength)
17114366bb71SDavid du Colombier 			cacheinval(file);
17124366bb71SDavid du Colombier 		file->length = d->length;
17134366bb71SDavid du Colombier 	}
17144366bb71SDavid du Colombier 	if(d->mode != ~0){
17154366bb71SDavid du Colombier 		file->mode = d->mode;
17164366bb71SDavid du Colombier 		file->qid.type = d->mode>>24;
17174366bb71SDavid du Colombier 	}
17184366bb71SDavid du Colombier 	if(d->atime != ~0)
17194366bb71SDavid du Colombier 		file->atime = d->atime;
17204366bb71SDavid du Colombier 	if(d->mtime != ~0)
17214366bb71SDavid du Colombier 		file->mtime = d->mtime;
17224366bb71SDavid du Colombier 	if(*d->uid){
17234366bb71SDavid du Colombier 		freestr(file->uid);
17244366bb71SDavid du Colombier 		file->uid = newstr(d->uid);
17254366bb71SDavid du Colombier 	}
17264366bb71SDavid du Colombier 	if(*d->gid){
17274366bb71SDavid du Colombier 		freestr(file->gid);
17284366bb71SDavid du Colombier 		file->gid = newstr(d->gid);
17294366bb71SDavid du Colombier 	}
17304366bb71SDavid du Colombier 	if(*d->muid){
17314366bb71SDavid du Colombier 		freestr(file->muid);
17324366bb71SDavid du Colombier 		file->muid = newstr(d->muid);
17334366bb71SDavid du Colombier 	}
17344366bb71SDavid du Colombier }
17354366bb71SDavid du Colombier 
17364366bb71SDavid du Colombier void
putfile(File * f)17374366bb71SDavid du Colombier putfile(File *f)
17384366bb71SDavid du Colombier {
17394366bb71SDavid du Colombier 	int i;
17404366bb71SDavid du Colombier 	SFid *sfid;
17414366bb71SDavid du Colombier 
17424366bb71SDavid du Colombier 	if(--f->ref != 0)
17434366bb71SDavid du Colombier 		return;
17444366bb71SDavid du Colombier 	for(i = 0; i < nelem(f->open); i++){
17454366bb71SDavid du Colombier 		sfid = f->open[i];
17464366bb71SDavid du Colombier 		if(sfid != nil){
17474366bb71SDavid du Colombier 			f->open[i] = nil;
17484366bb71SDavid du Colombier 			sfclunk(sfid);
17494366bb71SDavid du Colombier 			freesfid(sfid);
17504366bb71SDavid du Colombier 		}
17514366bb71SDavid du Colombier 	}
17524366bb71SDavid du Colombier 	freestr(f->uid);
17534366bb71SDavid du Colombier 	freestr(f->gid);
17544366bb71SDavid du Colombier 	freestr(f->muid);
17554366bb71SDavid du Colombier 	cacheinval(f);
17564366bb71SDavid du Colombier 	free(f->cached);
17574366bb71SDavid du Colombier 	free(f);
17584366bb71SDavid du Colombier }
17594366bb71SDavid du Colombier 
17604366bb71SDavid du Colombier /*
17614366bb71SDavid du Colombier  * data
17624366bb71SDavid du Colombier  */
17634366bb71SDavid du Colombier 
17644366bb71SDavid du Colombier static	int	radix[] = {10, 12, 14};
17654366bb71SDavid du Colombier static	uint	range[] = {8, 32, 0};
17664366bb71SDavid du Colombier static	uint	cacheused;
17674366bb71SDavid du Colombier 
17684366bb71SDavid du Colombier void
datainit(void)17694366bb71SDavid du Colombier datainit(void)
17704366bb71SDavid du Colombier {
17714366bb71SDavid du Colombier 	datalist.forw = datalist.back = &datalist;
17724366bb71SDavid du Colombier }
17734366bb71SDavid du Colombier 
17744366bb71SDavid du Colombier Data*
allocdata(File * file,uint n,uint nbytes)17754366bb71SDavid du Colombier allocdata(File *file, uint n, uint nbytes)
17764366bb71SDavid du Colombier {
17774366bb71SDavid du Colombier 	Data *d;
17784366bb71SDavid du Colombier 
17794366bb71SDavid du Colombier 	while(cacheused+nbytes > cachesize && datalist.forw != datalist.back)
17804366bb71SDavid du Colombier 		freedata(datalist.forw);
17814366bb71SDavid du Colombier 	cacheused += nbytes;
17824366bb71SDavid du Colombier 	d = mallocz(sizeof(*d), 0);
17834366bb71SDavid du Colombier 	d->owner = file;
17844366bb71SDavid du Colombier 	d->n = n;
17854366bb71SDavid du Colombier 	d->base = malloc(nbytes);
17864366bb71SDavid du Colombier 	d->size = nbytes;
17874366bb71SDavid du Colombier 	d->min = 0;
17884366bb71SDavid du Colombier 	d->max = 0;
17894366bb71SDavid du Colombier 	d->forw = &datalist;
17904366bb71SDavid du Colombier 	d->back = datalist.back;
17914366bb71SDavid du Colombier 	d->back->forw = d;
17924366bb71SDavid du Colombier 	datalist.back = d;
17934366bb71SDavid du Colombier 	return d;
17944366bb71SDavid du Colombier }
17954366bb71SDavid du Colombier 
17964366bb71SDavid du Colombier void
freedata(Data * d)17974366bb71SDavid du Colombier freedata(Data *d)
17984366bb71SDavid du Colombier {
17994366bb71SDavid du Colombier 	d->forw->back = d->back;
18004366bb71SDavid du Colombier 	d->back->forw = d->forw;
18014366bb71SDavid du Colombier 	cacheused -= d->size;
18024366bb71SDavid du Colombier 	if(d->owner != nil)
18034366bb71SDavid du Colombier 		d->owner->cached[d->n] = nil;
18044366bb71SDavid du Colombier 	free(d->base);
18054366bb71SDavid du Colombier 	free(d);
18064366bb71SDavid du Colombier }
18074366bb71SDavid du Colombier 
18084366bb71SDavid du Colombier /*
18094366bb71SDavid du Colombier  * move recently-used data to end
18104366bb71SDavid du Colombier  */
18114366bb71SDavid du Colombier void
usedata(Data * d)18124366bb71SDavid du Colombier usedata(Data *d)
18134366bb71SDavid du Colombier {
18144366bb71SDavid du Colombier 	if(datalist.back == d)
18154366bb71SDavid du Colombier 		return;	/* already at end */
18164366bb71SDavid du Colombier 
18174366bb71SDavid du Colombier 	d->forw->back = d->back;
18184366bb71SDavid du Colombier 	d->back->forw = d->forw;
18194366bb71SDavid du Colombier 
18204366bb71SDavid du Colombier 	d->forw = &datalist;
18214366bb71SDavid du Colombier 	d->back = datalist.back;
18224366bb71SDavid du Colombier 	d->back->forw = d;
18234366bb71SDavid du Colombier 	datalist.back = d;
18244366bb71SDavid du Colombier }
18254366bb71SDavid du Colombier 
18264366bb71SDavid du Colombier /*
18274366bb71SDavid du Colombier  * data cache
18284366bb71SDavid du Colombier  */
18294366bb71SDavid du Colombier 
18304366bb71SDavid du Colombier int
cacheread(File * file,void * buf,vlong offset,int nbytes)18314366bb71SDavid du Colombier cacheread(File *file, void *buf, vlong offset, int nbytes)
18324366bb71SDavid du Colombier {
18334366bb71SDavid du Colombier 	char *p;
18344366bb71SDavid du Colombier 	Data *d;
18354366bb71SDavid du Colombier 	int n, o;
18364366bb71SDavid du Colombier 
18374366bb71SDavid du Colombier 	DPRINT(2, "file %lld length %lld\n", file->qid.path, file->clength);
18384366bb71SDavid du Colombier 	p = buf;
18394366bb71SDavid du Colombier 	while(nbytes > 0){
18404366bb71SDavid du Colombier 		d = finddata(file, offset, &o);
18414366bb71SDavid du Colombier 		if(d == nil)
18424366bb71SDavid du Colombier 			break;
18434366bb71SDavid du Colombier 		if(o < d->min){
18444366bb71SDavid du Colombier 			if(p == (char*)buf)
18454366bb71SDavid du Colombier 				return -(d->min-o);	/* fill the gap */
18464366bb71SDavid du Colombier 			break;
18474366bb71SDavid du Colombier 		}else if(o >= d->max)
18484366bb71SDavid du Colombier 			break;
18494366bb71SDavid du Colombier 		usedata(d);
18504366bb71SDavid du Colombier 		/* o >= d->min && o < d->max */
18514366bb71SDavid du Colombier 		n = nbytes;
18524366bb71SDavid du Colombier 		if(n > d->max-o)
18534366bb71SDavid du Colombier 			n = d->max-o;
18544366bb71SDavid du Colombier 		memmove(p, d->base+o, n);
18554366bb71SDavid du Colombier 		p += n;
18564366bb71SDavid du Colombier 		nbytes -= n;
18574366bb71SDavid du Colombier 		offset += n;
18584366bb71SDavid du Colombier 	}
18594366bb71SDavid du Colombier 	return p-(char*)buf;
18604366bb71SDavid du Colombier }
18614366bb71SDavid du Colombier 
18624366bb71SDavid du Colombier void
cachewrite(File * file,void * buf,vlong offset,int nbytes)18634366bb71SDavid du Colombier cachewrite(File *file, void *buf, vlong offset, int nbytes)
18644366bb71SDavid du Colombier {
18654366bb71SDavid du Colombier 	char *p;
18664366bb71SDavid du Colombier 	Data *d;
18674366bb71SDavid du Colombier 	int n, o;
18684366bb71SDavid du Colombier 
18694366bb71SDavid du Colombier 	p = buf;
18704366bb71SDavid du Colombier 	while(nbytes > 0){
18714366bb71SDavid du Colombier 		d = storedata(file, offset, &o);
18724366bb71SDavid du Colombier 		if(d == nil)
18734366bb71SDavid du Colombier 			break;
18744366bb71SDavid du Colombier 		n = nbytes;
18754366bb71SDavid du Colombier 		if(n > d->size-o)
18764366bb71SDavid du Colombier 			n = d->size-o;
18774366bb71SDavid du Colombier 		cachemerge(d, p, o, n);
18784366bb71SDavid du Colombier 		p += n;
18794366bb71SDavid du Colombier 		offset += n;
18804366bb71SDavid du Colombier 		nbytes -= n;
18814366bb71SDavid du Colombier 	}
18824366bb71SDavid du Colombier 	if(offset > file->clength)
18834366bb71SDavid du Colombier 		file->clength = offset;
18844366bb71SDavid du Colombier }
18854366bb71SDavid du Colombier 
18864366bb71SDavid du Colombier /*
18874366bb71SDavid du Colombier  * merge data in if it overlaps existing contents,
18884366bb71SDavid du Colombier  * or simply replace existing contents otherwise
18894366bb71SDavid du Colombier  */
18904366bb71SDavid du Colombier void
cachemerge(Data * p,char * from,int start,int len)18914366bb71SDavid du Colombier cachemerge(Data *p, char *from, int start, int len)
18924366bb71SDavid du Colombier {
18934366bb71SDavid du Colombier 	int end;
18944366bb71SDavid du Colombier 
18954366bb71SDavid du Colombier 	end = start + len;
18964366bb71SDavid du Colombier 	memmove(p->base+start, from, len);
18974366bb71SDavid du Colombier 
18984366bb71SDavid du Colombier 	if(start > p->max || p->min > end){
18994366bb71SDavid du Colombier 		p->min = start;
19004366bb71SDavid du Colombier 		p->max = end;
19014366bb71SDavid du Colombier 	}else{
19024366bb71SDavid du Colombier 		if(start < p->min)
19034366bb71SDavid du Colombier 			p->min = start;
19044366bb71SDavid du Colombier 		if(end > p->max)
19054366bb71SDavid du Colombier 			p->max = end;
19064366bb71SDavid du Colombier 	}
19074366bb71SDavid du Colombier }
19084366bb71SDavid du Colombier 
19094366bb71SDavid du Colombier void
cacheinval(File * file)19104366bb71SDavid du Colombier cacheinval(File *file)
19114366bb71SDavid du Colombier {
19124366bb71SDavid du Colombier 	Data *d;
19134366bb71SDavid du Colombier 	int i;
19144366bb71SDavid du Colombier 
19154366bb71SDavid du Colombier 	if(file->cached != nil){
19164366bb71SDavid du Colombier 		for(i = 0; i < file->ndata; i++){
19174366bb71SDavid du Colombier 			d = file->cached[i];
19184366bb71SDavid du Colombier 			if(d != nil){
19194366bb71SDavid du Colombier 				file->cached[i] = nil;
19204366bb71SDavid du Colombier 				d->owner = nil;
19214366bb71SDavid du Colombier 				freedata(d);
19224366bb71SDavid du Colombier 			}
19234366bb71SDavid du Colombier 		}
19244366bb71SDavid du Colombier 		/* leave the array */
19254366bb71SDavid du Colombier 	}
19264366bb71SDavid du Colombier 	file->clength = 0;
19274366bb71SDavid du Colombier }
19284366bb71SDavid du Colombier 
19294366bb71SDavid du Colombier Data*
finddata(File * file,uvlong offset,int * blkoff)19304366bb71SDavid du Colombier finddata(File *file, uvlong offset, int *blkoff)
19314366bb71SDavid du Colombier {
19324366bb71SDavid du Colombier 	int r, x;
19334366bb71SDavid du Colombier 	uvlong base, o;
19344366bb71SDavid du Colombier 
19354366bb71SDavid du Colombier 	x = 0;
19364366bb71SDavid du Colombier 	base = 0;
19374366bb71SDavid du Colombier 	for(r = 0; r < nelem(radix); r++){
19384366bb71SDavid du Colombier 		o = (offset - base) >> radix[r];
19394366bb71SDavid du Colombier 		DPRINT(2, "file %llud offset %llud x %d base %llud o %llud\n", file->qid.path, offset, x, base, o);
19404366bb71SDavid du Colombier 		if(range[r] == 0 || o < range[r]){
19414366bb71SDavid du Colombier 			o += x;
19424366bb71SDavid du Colombier 			if(o >= file->ndata)
19434366bb71SDavid du Colombier 				break;
19444366bb71SDavid du Colombier 			*blkoff = (offset-base) & ((1<<radix[r])-1);
19454366bb71SDavid du Colombier 			return file->cached[(int)o];
19464366bb71SDavid du Colombier 		}
19474366bb71SDavid du Colombier 		base += range[r]<<radix[r];
19484366bb71SDavid du Colombier 		x += range[r];
19494366bb71SDavid du Colombier 	}
19504366bb71SDavid du Colombier 	return nil;
19514366bb71SDavid du Colombier }
19524366bb71SDavid du Colombier 
19534366bb71SDavid du Colombier Data*
storedata(File * file,uvlong offset,int * blkoff)19544366bb71SDavid du Colombier storedata(File *file, uvlong offset, int *blkoff)
19554366bb71SDavid du Colombier {
19564366bb71SDavid du Colombier 	int r, x, v, size;
19574366bb71SDavid du Colombier 	uvlong base, o;
19584366bb71SDavid du Colombier 	Data **p;
19594366bb71SDavid du Colombier 
19604366bb71SDavid du Colombier 	if(file->cached == nil){
19614366bb71SDavid du Colombier 		file->cached = mallocz(16*sizeof(*file->cached), 1);
19624366bb71SDavid du Colombier 		file->ndata = 16;
19634366bb71SDavid du Colombier 	}
19644366bb71SDavid du Colombier 	x = 0;
19654366bb71SDavid du Colombier 	base = 0;
19664366bb71SDavid du Colombier 	for(r = 0; r < nelem(radix); r++){
19674366bb71SDavid du Colombier 		o = (offset - base) >> radix[r];
19684366bb71SDavid du Colombier 		DPRINT(2, "store file %llud offset %llud x %d base %llud o %llud\n", file->qid.path, offset, x, base, o);
19694366bb71SDavid du Colombier 		if(range[r] == 0 || o < range[r]){
19704366bb71SDavid du Colombier 			o += x;
19714366bb71SDavid du Colombier 			if(o >= file->ndata){
19724366bb71SDavid du Colombier 				if(o >= 512)
19734366bb71SDavid du Colombier 					return nil;	/* won't fit */
19744366bb71SDavid du Colombier 				v = (o+32+31)&~31;
19754366bb71SDavid du Colombier 				file->cached = realloc(file->cached, v*sizeof(*file->cached));
19764366bb71SDavid du Colombier 				memset(file->cached+file->ndata, 0, (v-file->ndata)*sizeof(*file->cached));
19774366bb71SDavid du Colombier 				file->ndata = v;
19784366bb71SDavid du Colombier 			}
19794366bb71SDavid du Colombier 			size = 1 << radix[r];
19804366bb71SDavid du Colombier 			DPRINT(2, "	-> %d %d\n", (int)o, size);
19814366bb71SDavid du Colombier 			*blkoff = (offset-base) & (size-1);
19824366bb71SDavid du Colombier 			p = &file->cached[(int)o];
19834366bb71SDavid du Colombier 			if(*p == nil)
19844366bb71SDavid du Colombier 				*p = allocdata(file, (int)o, size);
19854366bb71SDavid du Colombier 			else
19864366bb71SDavid du Colombier 				usedata(*p);
19874366bb71SDavid du Colombier 			return *p;
19884366bb71SDavid du Colombier 		}
19894366bb71SDavid du Colombier 		base += range[r]<<radix[r];
19904366bb71SDavid du Colombier 		x += range[r];
19914366bb71SDavid du Colombier 	}
19924366bb71SDavid du Colombier 	return nil;
19934366bb71SDavid du Colombier }
19944366bb71SDavid du Colombier 
19954366bb71SDavid du Colombier /*
19964366bb71SDavid du Colombier  * Strings
19974366bb71SDavid du Colombier  */
19984366bb71SDavid du Colombier 
19994366bb71SDavid du Colombier enum{
20004366bb71SDavid du Colombier 	Strhashmask=	(1<<8)-1,
20014366bb71SDavid du Colombier };
20024366bb71SDavid du Colombier 
20034366bb71SDavid du Colombier static struct{
20044366bb71SDavid du Colombier 	QLock;
20054366bb71SDavid du Colombier 	String*	htab[Strhashmask+1];
20064366bb71SDavid du Colombier } stralloc;
20074366bb71SDavid du Colombier 
20084366bb71SDavid du Colombier String**
hashslot(char * s)20094366bb71SDavid du Colombier hashslot(char *s)
20104366bb71SDavid du Colombier {
20114366bb71SDavid du Colombier 	uint h, g;
20124366bb71SDavid du Colombier 	uchar *p;
20134366bb71SDavid du Colombier 
20144366bb71SDavid du Colombier 	h = 0;
20154366bb71SDavid du Colombier 	for(p = (uchar*)s; *p != 0; p++){
20164366bb71SDavid du Colombier 		h = (h<<4) + *p;
20174366bb71SDavid du Colombier 		g = h & (0xF<<28);
20184366bb71SDavid du Colombier 		if(g != 0)
20194366bb71SDavid du Colombier 			h ^= ((g>>24) & 0xFF) | g;
20204366bb71SDavid du Colombier 	}
20214366bb71SDavid du Colombier 	return &stralloc.htab[h & Strhashmask];
20224366bb71SDavid du Colombier }
20234366bb71SDavid du Colombier 
20244366bb71SDavid du Colombier String*
newstr(char * val)20254366bb71SDavid du Colombier newstr(char *val)
20264366bb71SDavid du Colombier {
20274366bb71SDavid du Colombier 	String *s, **l;
20284366bb71SDavid du Colombier 
20294366bb71SDavid du Colombier 	//qlock(&stralloc);
20304366bb71SDavid du Colombier 	for(l = hashslot(val); (s = *l) != nil; l = &s->next){
20314366bb71SDavid du Colombier 		if(strcmp(s->s, val) == 0){
20324366bb71SDavid du Colombier 			s->ref++;
20334366bb71SDavid du Colombier 			//qunlock(&stralloc);
20344366bb71SDavid du Colombier 			return s;
20354366bb71SDavid du Colombier 		}
20364366bb71SDavid du Colombier 	}
20374366bb71SDavid du Colombier 	s = malloc(sizeof(*s));
20384366bb71SDavid du Colombier 	s->s = strdup(val);
20394366bb71SDavid du Colombier 	s->len = strlen(s->s);
20404366bb71SDavid du Colombier 	s->ref = 1;
20414366bb71SDavid du Colombier 	s->next = *l;
20424366bb71SDavid du Colombier 	*l = s;
20434366bb71SDavid du Colombier 	//qunlock(&stralloc);
20444366bb71SDavid du Colombier 	return s;
20454366bb71SDavid du Colombier }
20464366bb71SDavid du Colombier 
20474366bb71SDavid du Colombier String*
dupstr(String * s)20484366bb71SDavid du Colombier dupstr(String *s)
20494366bb71SDavid du Colombier {
20504366bb71SDavid du Colombier 	s->ref++;
20514366bb71SDavid du Colombier 	return s;
20524366bb71SDavid du Colombier }
20534366bb71SDavid du Colombier 
20544366bb71SDavid du Colombier void
freestr(String * s)20554366bb71SDavid du Colombier freestr(String *s)
20564366bb71SDavid du Colombier {
20574366bb71SDavid du Colombier 	String **l;
20584366bb71SDavid du Colombier 
20594366bb71SDavid du Colombier 	if(s != nil && --s->ref == 0){
20604366bb71SDavid du Colombier 		//qlock(&stralloc);
20614366bb71SDavid du Colombier 		for(l = hashslot(s->s); *l != nil; l = &(*l)->next){
20624366bb71SDavid du Colombier 			if(*l == s){
20634366bb71SDavid du Colombier 				*l = s->next;
20644366bb71SDavid du Colombier 				break;
20654366bb71SDavid du Colombier 			}
20664366bb71SDavid du Colombier 		}
20674366bb71SDavid du Colombier 		//qunlock(&stralloc);
20684366bb71SDavid du Colombier 		free(s->s);
20694366bb71SDavid du Colombier 		free(s);
20704366bb71SDavid du Colombier 	}
20714366bb71SDavid du Colombier }
2072