xref: /plan9/sys/src/cmd/webfs/fs.c (revision 9dfc0cb2ef9edafc5a26f7e8dbe4ef1ca2b37021)
19a747e4fSDavid du Colombier /*
29a747e4fSDavid du Colombier  * Web file system.  Conventionally mounted at /mnt/web
39a747e4fSDavid du Colombier  *
49a747e4fSDavid du Colombier  *	ctl				send control messages (might go away)
59a747e4fSDavid du Colombier  *	cookies			list of cookies, editable
69a747e4fSDavid du Colombier  *	clone			open and read to obtain new connection
79a747e4fSDavid du Colombier  *	n				connection directory
89a747e4fSDavid du Colombier  *		ctl				control messages (like get url)
99a747e4fSDavid du Colombier  *		body				retrieved data
109a747e4fSDavid du Colombier  *		content-type		mime content-type of body
119a747e4fSDavid du Colombier  *		postbody			data to be posted
129a747e4fSDavid du Colombier  *		parsed			parsed version of url
139a747e4fSDavid du Colombier  * 			url				entire url
149a747e4fSDavid du Colombier  *			scheme			http, ftp, etc.
159a747e4fSDavid du Colombier  *			host				hostname
169a747e4fSDavid du Colombier  *			path				path on host
179a747e4fSDavid du Colombier  *			query			query after path
189a747e4fSDavid du Colombier  *			fragment			#foo anchor reference
199a747e4fSDavid du Colombier  *			user				user name (ftp)
209a747e4fSDavid du Colombier  *			password			password (ftp)
219a747e4fSDavid du Colombier  *			ftptype			transfer mode (ftp)
229a747e4fSDavid du Colombier  */
239a747e4fSDavid du Colombier 
249a747e4fSDavid du Colombier #include <u.h>
259a747e4fSDavid du Colombier #include <libc.h>
269a747e4fSDavid du Colombier #include <bio.h>
279a747e4fSDavid du Colombier #include <ip.h>
289a747e4fSDavid du Colombier #include <plumb.h>
299a747e4fSDavid du Colombier #include <thread.h>
309a747e4fSDavid du Colombier #include <fcall.h>
319a747e4fSDavid du Colombier #include <9p.h>
329a747e4fSDavid du Colombier #include "dat.h"
339a747e4fSDavid du Colombier #include "fns.h"
349a747e4fSDavid du Colombier 
359a747e4fSDavid du Colombier int fsdebug;
369a747e4fSDavid du Colombier 
379a747e4fSDavid du Colombier enum
389a747e4fSDavid du Colombier {
399a747e4fSDavid du Colombier 	Qroot,
409a747e4fSDavid du Colombier 	Qrootctl,
419a747e4fSDavid du Colombier 	Qclone,
429a747e4fSDavid du Colombier 	Qcookies,
439a747e4fSDavid du Colombier 	Qclient,
449a747e4fSDavid du Colombier 	Qctl,
459a747e4fSDavid du Colombier 	Qbody,
469a747e4fSDavid du Colombier 	Qbodyext,
479a747e4fSDavid du Colombier 	Qcontenttype,
489a747e4fSDavid du Colombier 	Qpostbody,
499a747e4fSDavid du Colombier 	Qparsed,
509a747e4fSDavid du Colombier 	Qurl,
519a747e4fSDavid du Colombier 	Qscheme,
529a747e4fSDavid du Colombier 	Qschemedata,
539a747e4fSDavid du Colombier 	Quser,
549a747e4fSDavid du Colombier 	Qpasswd,
559a747e4fSDavid du Colombier 	Qhost,
569a747e4fSDavid du Colombier 	Qport,
579a747e4fSDavid du Colombier 	Qpath,
589a747e4fSDavid du Colombier 	Qquery,
599a747e4fSDavid du Colombier 	Qfragment,
609a747e4fSDavid du Colombier 	Qftptype,
619a747e4fSDavid du Colombier 	Qend,
629a747e4fSDavid du Colombier };
639a747e4fSDavid du Colombier 
649a747e4fSDavid du Colombier #define PATH(type, n)	((type)|((n)<<8))
659a747e4fSDavid du Colombier #define TYPE(path)		((int)(path) & 0xFF)
669a747e4fSDavid du Colombier #define NUM(path)		((uint)(path)>>8)
679a747e4fSDavid du Colombier 
689a747e4fSDavid du Colombier Channel *creq;
699a747e4fSDavid du Colombier Channel *creqwait;
709a747e4fSDavid du Colombier Channel *cclunk;
719a747e4fSDavid du Colombier Channel *cclunkwait;
729a747e4fSDavid du Colombier 
739a747e4fSDavid du Colombier typedef struct Tab Tab;
749a747e4fSDavid du Colombier struct Tab
759a747e4fSDavid du Colombier {
769a747e4fSDavid du Colombier 	char *name;
779a747e4fSDavid du Colombier 	ulong mode;
789a747e4fSDavid du Colombier 	int offset;
799a747e4fSDavid du Colombier };
809a747e4fSDavid du Colombier 
819a747e4fSDavid du Colombier Tab tab[] =
829a747e4fSDavid du Colombier {
839a747e4fSDavid du Colombier 	"/",			DMDIR|0555,		0,
849a747e4fSDavid du Colombier 	"ctl",			0666,			0,
859a747e4fSDavid du Colombier 	"clone",		0666,			0,
869a747e4fSDavid du Colombier 	"cookies",		0666,			0,
879a747e4fSDavid du Colombier 	"XXX",		DMDIR|0555,		0,
889a747e4fSDavid du Colombier 	"ctl",			0666,			0,
899a747e4fSDavid du Colombier 	"body",		0444,			0,
909a747e4fSDavid du Colombier 	"XXX",		0444,			0,
919a747e4fSDavid du Colombier 	"contenttype",	0444,			0,
929a747e4fSDavid du Colombier 	"postbody",	0666,			0,
939a747e4fSDavid du Colombier 	"parsed",		DMDIR|0555,		0,
949a747e4fSDavid du Colombier 	"url",			0444,			offsetof(Url, url),
959a747e4fSDavid du Colombier 	"scheme",		0444,			offsetof(Url, scheme),
969a747e4fSDavid du Colombier 	"schemedata",	0444,			offsetof(Url, schemedata),
979a747e4fSDavid du Colombier 	"user",		0444,			offsetof(Url, user),
989a747e4fSDavid du Colombier 	"passwd",		0444,			offsetof(Url, passwd),
999a747e4fSDavid du Colombier 	"host",		0444,			offsetof(Url, host),
1009a747e4fSDavid du Colombier 	"port",		0444,			offsetof(Url, port),
1019a747e4fSDavid du Colombier 	"path",		0444,			offsetof(Url, path),
1029a747e4fSDavid du Colombier 	"query",		0444,			offsetof(Url, query),
1039a747e4fSDavid du Colombier 	"fragment",	0444,			offsetof(Url, fragment),
1049a747e4fSDavid du Colombier 	"ftptype",		0444,			offsetof(Url, ftp.type),
1059a747e4fSDavid du Colombier };
1069a747e4fSDavid du Colombier 
1079a747e4fSDavid du Colombier ulong time0;
1089a747e4fSDavid du Colombier 
1099a747e4fSDavid du Colombier static void
fillstat(Dir * d,uvlong path,ulong length,char * ext)1109a747e4fSDavid du Colombier fillstat(Dir *d, uvlong path, ulong length, char *ext)
1119a747e4fSDavid du Colombier {
1129a747e4fSDavid du Colombier 	Tab *t;
1139a747e4fSDavid du Colombier 	int type;
1149a747e4fSDavid du Colombier 	char buf[32];
1159a747e4fSDavid du Colombier 
1169a747e4fSDavid du Colombier 	memset(d, 0, sizeof(*d));
1179a747e4fSDavid du Colombier 	d->uid = estrdup("web");
1189a747e4fSDavid du Colombier 	d->gid = estrdup("web");
1199a747e4fSDavid du Colombier 	d->qid.path = path;
1209a747e4fSDavid du Colombier 	d->atime = d->mtime = time0;
1219a747e4fSDavid du Colombier 	d->length = length;
1229a747e4fSDavid du Colombier 	type = TYPE(path);
1239a747e4fSDavid du Colombier 	t = &tab[type];
1249a747e4fSDavid du Colombier 	if(type == Qbodyext) {
1259a747e4fSDavid du Colombier 		snprint(buf, sizeof buf, "body.%s", ext == nil ? "xxx" : ext);
1269a747e4fSDavid du Colombier 		d->name = estrdup(buf);
1279a747e4fSDavid du Colombier 	}
1289a747e4fSDavid du Colombier 	else if(t->name)
1299a747e4fSDavid du Colombier 		d->name = estrdup(t->name);
1309a747e4fSDavid du Colombier 	else{	/* client directory */
1319a747e4fSDavid du Colombier 		snprint(buf, sizeof buf, "%ud", NUM(path));
1329a747e4fSDavid du Colombier 		d->name = estrdup(buf);
1339a747e4fSDavid du Colombier 	}
1349a747e4fSDavid du Colombier 	d->qid.type = t->mode>>24;
1359a747e4fSDavid du Colombier 	d->mode = t->mode;
1369a747e4fSDavid du Colombier }
1379a747e4fSDavid du Colombier 
1389a747e4fSDavid du Colombier static void
fsstat(Req * r)1399a747e4fSDavid du Colombier fsstat(Req *r)
1409a747e4fSDavid du Colombier {
1419a747e4fSDavid du Colombier 	fillstat(&r->d, r->fid->qid.path, 0, nil);
1429a747e4fSDavid du Colombier 	respond(r, nil);
1439a747e4fSDavid du Colombier }
1449a747e4fSDavid du Colombier 
1459a747e4fSDavid du Colombier static int
rootgen(int i,Dir * d,void *)1469a747e4fSDavid du Colombier rootgen(int i, Dir *d, void*)
1479a747e4fSDavid du Colombier {
1489a747e4fSDavid du Colombier 	char buf[32];
1499a747e4fSDavid du Colombier 
1509a747e4fSDavid du Colombier 	i += Qroot+1;
1519a747e4fSDavid du Colombier 	if(i < Qclient){
1529a747e4fSDavid du Colombier 		fillstat(d, i, 0, nil);
1539a747e4fSDavid du Colombier 		return 0;
1549a747e4fSDavid du Colombier 	}
1559a747e4fSDavid du Colombier 	i -= Qclient;
1569a747e4fSDavid du Colombier 	if(i < nclient){
1579a747e4fSDavid du Colombier 		fillstat(d, PATH(Qclient, i), 0, nil);
1589a747e4fSDavid du Colombier 		snprint(buf, sizeof buf, "%d", i);
1599a747e4fSDavid du Colombier 		free(d->name);
1609a747e4fSDavid du Colombier 		d->name = estrdup(buf);
1619a747e4fSDavid du Colombier 		return 0;
1629a747e4fSDavid du Colombier 	}
1639a747e4fSDavid du Colombier 	return -1;
1649a747e4fSDavid du Colombier }
1659a747e4fSDavid du Colombier 
1669a747e4fSDavid du Colombier static int
clientgen(int i,Dir * d,void * aux)1679a747e4fSDavid du Colombier clientgen(int i, Dir *d, void *aux)
1689a747e4fSDavid du Colombier {
1699a747e4fSDavid du Colombier 	Client *c;
1709a747e4fSDavid du Colombier 
1719a747e4fSDavid du Colombier 	c = aux;
1729a747e4fSDavid du Colombier 	i += Qclient+1;
1739a747e4fSDavid du Colombier 	if(i <= Qparsed){
1749a747e4fSDavid du Colombier 		fillstat(d, PATH(i, c->num), 0, c->ext);
1759a747e4fSDavid du Colombier 		return 0;
1769a747e4fSDavid du Colombier 	}
1779a747e4fSDavid du Colombier 	return -1;
1789a747e4fSDavid du Colombier }
1799a747e4fSDavid du Colombier 
1809a747e4fSDavid du Colombier static int
parsedgen(int i,Dir * d,void * aux)1819a747e4fSDavid du Colombier parsedgen(int i, Dir *d, void *aux)
1829a747e4fSDavid du Colombier {
1839a747e4fSDavid du Colombier 	Client *c;
1849a747e4fSDavid du Colombier 
1859a747e4fSDavid du Colombier 	c = aux;
1869a747e4fSDavid du Colombier 	i += Qparsed+1;
1879a747e4fSDavid du Colombier 	if(i < Qend){
1889a747e4fSDavid du Colombier 		fillstat(d, PATH(i, c->num), 0, nil);
1899a747e4fSDavid du Colombier 		return 0;
1909a747e4fSDavid du Colombier 	}
1919a747e4fSDavid du Colombier 	return -1;
1929a747e4fSDavid du Colombier }
1939a747e4fSDavid du Colombier 
1949a747e4fSDavid du Colombier static void
fsread(Req * r)1959a747e4fSDavid du Colombier fsread(Req *r)
1969a747e4fSDavid du Colombier {
1979a747e4fSDavid du Colombier 	char *s;
1989a747e4fSDavid du Colombier 	char e[ERRMAX];
1999a747e4fSDavid du Colombier 	Client *c;
2009a747e4fSDavid du Colombier 	ulong path;
2019a747e4fSDavid du Colombier 
2029a747e4fSDavid du Colombier 	path = r->fid->qid.path;
2039a747e4fSDavid du Colombier 	switch(TYPE(path)){
2049a747e4fSDavid du Colombier 	default:
2059a747e4fSDavid du Colombier 		snprint(e, sizeof e, "bug in webfs path=%lux\n", path);
2069a747e4fSDavid du Colombier 		respond(r, e);
2079a747e4fSDavid du Colombier 		break;
2089a747e4fSDavid du Colombier 
2099a747e4fSDavid du Colombier 	case Qroot:
2109a747e4fSDavid du Colombier 		dirread9p(r, rootgen, nil);
2119a747e4fSDavid du Colombier 		respond(r, nil);
2129a747e4fSDavid du Colombier 		break;
2139a747e4fSDavid du Colombier 
2149a747e4fSDavid du Colombier 	case Qrootctl:
2159a747e4fSDavid du Colombier 		globalctlread(r);
2169a747e4fSDavid du Colombier 		break;
2179a747e4fSDavid du Colombier 
2189a747e4fSDavid du Colombier 	case Qcookies:
2199a747e4fSDavid du Colombier 		cookieread(r);
2209a747e4fSDavid du Colombier 		break;
2219a747e4fSDavid du Colombier 
2229a747e4fSDavid du Colombier 	case Qclient:
2239a747e4fSDavid du Colombier 		dirread9p(r, clientgen, client[NUM(path)]);
2249a747e4fSDavid du Colombier 		respond(r, nil);
2259a747e4fSDavid du Colombier 		break;
2269a747e4fSDavid du Colombier 
2279a747e4fSDavid du Colombier 	case Qctl:
2289a747e4fSDavid du Colombier 		ctlread(r, client[NUM(path)]);
2299a747e4fSDavid du Colombier 		break;
2309a747e4fSDavid du Colombier 
2319a747e4fSDavid du Colombier 	case Qcontenttype:
2329a747e4fSDavid du Colombier 		c = client[NUM(path)];
2339a747e4fSDavid du Colombier 		if(c->contenttype == nil)
2349a747e4fSDavid du Colombier 			r->ofcall.count = 0;
2359a747e4fSDavid du Colombier 		else
2369a747e4fSDavid du Colombier 			readstr(r, c->contenttype);
2379a747e4fSDavid du Colombier 		respond(r, nil);
2389a747e4fSDavid du Colombier 		break;
2399a747e4fSDavid du Colombier 
2409a747e4fSDavid du Colombier 	case Qpostbody:
2419a747e4fSDavid du Colombier 		c = client[NUM(path)];
2429a747e4fSDavid du Colombier 		readbuf(r, c->postbody, c->npostbody);
2439a747e4fSDavid du Colombier 		respond(r, nil);
2449a747e4fSDavid du Colombier 		break;
2459a747e4fSDavid du Colombier 
2469a747e4fSDavid du Colombier 	case Qbody:
2479a747e4fSDavid du Colombier 	case Qbodyext:
2489a747e4fSDavid du Colombier 		c = client[NUM(path)];
2499a747e4fSDavid du Colombier 		if(c->iobusy){
2509a747e4fSDavid du Colombier 			respond(r, "already have i/o pending");
2519a747e4fSDavid du Colombier 			break;
2529a747e4fSDavid du Colombier 		}
2539a747e4fSDavid du Colombier 		c->iobusy = 1;
2549a747e4fSDavid du Colombier 		sendp(c->creq, r);
2559a747e4fSDavid du Colombier 		break;
2569a747e4fSDavid du Colombier 
2579a747e4fSDavid du Colombier 	case Qparsed:
2589a747e4fSDavid du Colombier 		dirread9p(r, parsedgen, client[NUM(path)]);
2599a747e4fSDavid du Colombier 		respond(r, nil);
2609a747e4fSDavid du Colombier 		break;
2619a747e4fSDavid du Colombier 
2629a747e4fSDavid du Colombier 	case Qurl:
2639a747e4fSDavid du Colombier 	case Qscheme:
2649a747e4fSDavid du Colombier 	case Qschemedata:
2659a747e4fSDavid du Colombier 	case Quser:
2669a747e4fSDavid du Colombier 	case Qpasswd:
2679a747e4fSDavid du Colombier 	case Qhost:
2689a747e4fSDavid du Colombier 	case Qport:
2699a747e4fSDavid du Colombier 	case Qpath:
2709a747e4fSDavid du Colombier 	case Qquery:
2719a747e4fSDavid du Colombier 	case Qfragment:
2729a747e4fSDavid du Colombier 	case Qftptype:
2739a747e4fSDavid du Colombier 		c = client[NUM(path)];
2749a747e4fSDavid du Colombier 		r->ofcall.count = 0;
275d9306527SDavid du Colombier 		if(c->url != nil
276*9dfc0cb2SDavid du Colombier 		&& (s = *(char**)((uintptr)c->url+tab[TYPE(path)].offset)) != nil)
2779a747e4fSDavid du Colombier 			readstr(r, s);
2789a747e4fSDavid du Colombier 		respond(r, nil);
2799a747e4fSDavid du Colombier 		break;
2809a747e4fSDavid du Colombier 	}
2819a747e4fSDavid du Colombier }
2829a747e4fSDavid du Colombier 
2839a747e4fSDavid du Colombier static void
fswrite(Req * r)2849a747e4fSDavid du Colombier fswrite(Req *r)
2859a747e4fSDavid du Colombier {
2869a747e4fSDavid du Colombier 	int m;
2879a747e4fSDavid du Colombier 	ulong path;
2889a747e4fSDavid du Colombier 	char e[ERRMAX], *buf, *cmd, *arg;
2899a747e4fSDavid du Colombier 	Client *c;
2909a747e4fSDavid du Colombier 
2919a747e4fSDavid du Colombier 	path = r->fid->qid.path;
2929a747e4fSDavid du Colombier 	switch(TYPE(path)){
2939a747e4fSDavid du Colombier 	default:
2949a747e4fSDavid du Colombier 		snprint(e, sizeof e, "bug in webfs path=%lux\n", path);
2959a747e4fSDavid du Colombier 		respond(r, e);
2969a747e4fSDavid du Colombier 		break;
2979a747e4fSDavid du Colombier 
2989a747e4fSDavid du Colombier 	case Qcookies:
2999a747e4fSDavid du Colombier 		cookiewrite(r);
3009a747e4fSDavid du Colombier 		break;
3019a747e4fSDavid du Colombier 
3029a747e4fSDavid du Colombier 	case Qrootctl:
3039a747e4fSDavid du Colombier 	case Qctl:
3049a747e4fSDavid du Colombier 		if(r->ifcall.count >= 1024){
3059a747e4fSDavid du Colombier 			respond(r, "ctl message too long");
3069a747e4fSDavid du Colombier 			return;
3079a747e4fSDavid du Colombier 		}
3089a747e4fSDavid du Colombier 		buf = estredup(r->ifcall.data, (char*)r->ifcall.data+r->ifcall.count);
3099a747e4fSDavid du Colombier 		cmd = buf;
3109a747e4fSDavid du Colombier 		arg = strpbrk(cmd, "\t ");
3119a747e4fSDavid du Colombier 		if(arg){
3129a747e4fSDavid du Colombier 			*arg++ = '\0';
3139a747e4fSDavid du Colombier 			arg += strspn(arg, "\t ");
3149a747e4fSDavid du Colombier 		}else
3159a747e4fSDavid du Colombier 			arg = "";
3169a747e4fSDavid du Colombier 		r->ofcall.count = r->ifcall.count;
3179a747e4fSDavid du Colombier 		if(TYPE(path)==Qrootctl){
3189a747e4fSDavid du Colombier 			if(!ctlwrite(r, &globalctl, cmd, arg)
3199a747e4fSDavid du Colombier 			&& !globalctlwrite(r, cmd, arg))
3209a747e4fSDavid du Colombier 				respond(r, "unknown control command");
3219a747e4fSDavid du Colombier 		}else{
3229a747e4fSDavid du Colombier 			c = client[NUM(path)];
3239a747e4fSDavid du Colombier 			if(!ctlwrite(r, &c->ctl, cmd, arg)
3249a747e4fSDavid du Colombier 			&& !clientctlwrite(r, c, cmd, arg))
3259a747e4fSDavid du Colombier 				respond(r, "unknown control command");
3269a747e4fSDavid du Colombier 		}
3279a747e4fSDavid du Colombier 		free(buf);
3289a747e4fSDavid du Colombier 		break;
3299a747e4fSDavid du Colombier 
3309a747e4fSDavid du Colombier 	case Qpostbody:
3319a747e4fSDavid du Colombier 		c = client[NUM(path)];
3329a747e4fSDavid du Colombier 		if(c->bodyopened){
3339a747e4fSDavid du Colombier 			respond(r, "cannot write postbody after opening body");
3349a747e4fSDavid du Colombier 			break;
3359a747e4fSDavid du Colombier 		}
3369a747e4fSDavid du Colombier 		if(r->ifcall.offset >= 128*1024*1024){	/* >128MB is probably a mistake */
3379a747e4fSDavid du Colombier 			respond(r, "offset too large");
3389a747e4fSDavid du Colombier 			break;
3399a747e4fSDavid du Colombier 		}
3409a747e4fSDavid du Colombier 		m = r->ifcall.offset + r->ifcall.count;
3419a747e4fSDavid du Colombier 		if(c->npostbody < m){
3429a747e4fSDavid du Colombier 			c->postbody = erealloc(c->postbody, m);
3439a747e4fSDavid du Colombier 			memset(c->postbody+c->npostbody, 0, m-c->npostbody);
3449a747e4fSDavid du Colombier 			c->npostbody = m;
3459a747e4fSDavid du Colombier 		}
3469a747e4fSDavid du Colombier 		memmove(c->postbody+r->ifcall.offset, r->ifcall.data, r->ifcall.count);
3479a747e4fSDavid du Colombier 		r->ofcall.count = r->ifcall.count;
3489a747e4fSDavid du Colombier 		respond(r, nil);
3499a747e4fSDavid du Colombier 		break;
3509a747e4fSDavid du Colombier 	}
3519a747e4fSDavid du Colombier }
3529a747e4fSDavid du Colombier 
3539a747e4fSDavid du Colombier static void
fsopen(Req * r)3549a747e4fSDavid du Colombier fsopen(Req *r)
3559a747e4fSDavid du Colombier {
3569a747e4fSDavid du Colombier 	static int need[4] = { 4, 2, 6, 1 };
3579a747e4fSDavid du Colombier 	ulong path;
3589a747e4fSDavid du Colombier 	int n;
3599a747e4fSDavid du Colombier 	Client *c;
3609a747e4fSDavid du Colombier 	Tab *t;
3619a747e4fSDavid du Colombier 
3629a747e4fSDavid du Colombier 	/*
3639a747e4fSDavid du Colombier 	 * lib9p already handles the blatantly obvious.
3649a747e4fSDavid du Colombier 	 * we just have to enforce the permissions we have set.
3659a747e4fSDavid du Colombier 	 */
3669a747e4fSDavid du Colombier 	path = r->fid->qid.path;
3679a747e4fSDavid du Colombier 	t = &tab[TYPE(path)];
3689a747e4fSDavid du Colombier 	n = need[r->ifcall.mode&3];
3699a747e4fSDavid du Colombier 	if((n&t->mode) != n){
3709a747e4fSDavid du Colombier 		respond(r, "permission denied");
3719a747e4fSDavid du Colombier 		return;
3729a747e4fSDavid du Colombier 	}
3739a747e4fSDavid du Colombier 
3749a747e4fSDavid du Colombier 	switch(TYPE(path)){
3759a747e4fSDavid du Colombier 	case Qcookies:
3769a747e4fSDavid du Colombier 		cookieopen(r);
3779a747e4fSDavid du Colombier 		break;
3789a747e4fSDavid du Colombier 
3799a747e4fSDavid du Colombier 	case Qpostbody:
3809a747e4fSDavid du Colombier 		c = client[NUM(path)];
3819a747e4fSDavid du Colombier 		c->havepostbody++;
3829a747e4fSDavid du Colombier 		c->ref++;
3839a747e4fSDavid du Colombier 		respond(r, nil);
3849a747e4fSDavid du Colombier 		break;
3859a747e4fSDavid du Colombier 
3869a747e4fSDavid du Colombier 	case Qbody:
3879a747e4fSDavid du Colombier 	case Qbodyext:
3889a747e4fSDavid du Colombier 		c = client[NUM(path)];
3899a747e4fSDavid du Colombier 		if(c->url == nil){
3909a747e4fSDavid du Colombier 			respond(r, "url is not yet set");
3919a747e4fSDavid du Colombier 			break;
3929a747e4fSDavid du Colombier 		}
3939a747e4fSDavid du Colombier 		c->bodyopened = 1;
3949a747e4fSDavid du Colombier 		c->ref++;
3959a747e4fSDavid du Colombier 		sendp(c->creq, r);
3969a747e4fSDavid du Colombier 		break;
3979a747e4fSDavid du Colombier 
3989a747e4fSDavid du Colombier 	case Qclone:
3999a747e4fSDavid du Colombier 		n = newclient(0);
4009a747e4fSDavid du Colombier 		path = PATH(Qctl, n);
4019a747e4fSDavid du Colombier 		r->fid->qid.path = path;
4029a747e4fSDavid du Colombier 		r->ofcall.qid.path = path;
4039a747e4fSDavid du Colombier 		if(fsdebug)
4049a747e4fSDavid du Colombier 			fprint(2, "open clone => path=%lux\n", path);
4059a747e4fSDavid du Colombier 		t = &tab[Qctl];
4069a747e4fSDavid du Colombier 		/* fall through */
4079a747e4fSDavid du Colombier 	default:
4089a747e4fSDavid du Colombier 		if(t-tab >= Qclient)
4099a747e4fSDavid du Colombier 			client[NUM(path)]->ref++;
4109a747e4fSDavid du Colombier 		respond(r, nil);
4119a747e4fSDavid du Colombier 		break;
4129a747e4fSDavid du Colombier 	}
4139a747e4fSDavid du Colombier }
4149a747e4fSDavid du Colombier 
4159a747e4fSDavid du Colombier static void
fsdestroyfid(Fid * fid)4169a747e4fSDavid du Colombier fsdestroyfid(Fid *fid)
4179a747e4fSDavid du Colombier {
4189a747e4fSDavid du Colombier 	sendp(cclunk, fid);
4199a747e4fSDavid du Colombier 	recvp(cclunkwait);
4209a747e4fSDavid du Colombier }
4219a747e4fSDavid du Colombier 
4229a747e4fSDavid du Colombier static void
fsattach(Req * r)4239a747e4fSDavid du Colombier fsattach(Req *r)
4249a747e4fSDavid du Colombier {
4259a747e4fSDavid du Colombier 	if(r->ifcall.aname && r->ifcall.aname[0]){
4269a747e4fSDavid du Colombier 		respond(r, "invalid attach specifier");
4279a747e4fSDavid du Colombier 		return;
4289a747e4fSDavid du Colombier 	}
4299a747e4fSDavid du Colombier 	r->fid->qid.path = PATH(Qroot, 0);
4309a747e4fSDavid du Colombier 	r->fid->qid.type = QTDIR;
4319a747e4fSDavid du Colombier 	r->fid->qid.vers = 0;
4329a747e4fSDavid du Colombier 	r->ofcall.qid = r->fid->qid;
4339a747e4fSDavid du Colombier 	respond(r, nil);
4349a747e4fSDavid du Colombier }
4359a747e4fSDavid du Colombier 
4369a747e4fSDavid du Colombier static char*
fswalk1(Fid * fid,char * name,Qid * qid)4379a747e4fSDavid du Colombier fswalk1(Fid *fid, char *name, Qid *qid)
4389a747e4fSDavid du Colombier {
4399a747e4fSDavid du Colombier 	int i, n;
4409a747e4fSDavid du Colombier 	ulong path;
4419a747e4fSDavid du Colombier 	char buf[32], *ext;
4429a747e4fSDavid du Colombier 
4439a747e4fSDavid du Colombier 	path = fid->qid.path;
4449a747e4fSDavid du Colombier 	if(!(fid->qid.type&QTDIR))
4459a747e4fSDavid du Colombier 		return "walk in non-directory";
4469a747e4fSDavid du Colombier 
4479a747e4fSDavid du Colombier 	if(strcmp(name, "..") == 0){
4489a747e4fSDavid du Colombier 		switch(TYPE(path)){
4499a747e4fSDavid du Colombier 		case Qparsed:
4509a747e4fSDavid du Colombier 			qid->path = PATH(Qclient, NUM(path));
4519a747e4fSDavid du Colombier 			qid->type = tab[Qclient].mode>>24;
4529a747e4fSDavid du Colombier 			return nil;
4539a747e4fSDavid du Colombier 		case Qclient:
4549a747e4fSDavid du Colombier 		case Qroot:
4559a747e4fSDavid du Colombier 			qid->path = PATH(Qroot, 0);
4569a747e4fSDavid du Colombier 			qid->type = tab[Qroot].mode>>24;
4579a747e4fSDavid du Colombier 			return nil;
4589a747e4fSDavid du Colombier 		default:
4599a747e4fSDavid du Colombier 			return "bug in fswalk1";
4609a747e4fSDavid du Colombier 		}
4619a747e4fSDavid du Colombier 	}
4629a747e4fSDavid du Colombier 
4639a747e4fSDavid du Colombier 	i = TYPE(path)+1;
4649a747e4fSDavid du Colombier 	for(; i<nelem(tab); i++){
4659a747e4fSDavid du Colombier 		if(i==Qclient){
4669a747e4fSDavid du Colombier 			n = atoi(name);
4679a747e4fSDavid du Colombier 			snprint(buf, sizeof buf, "%d", n);
4689a747e4fSDavid du Colombier 			if(n < nclient && strcmp(buf, name) == 0){
4699a747e4fSDavid du Colombier 				qid->path = PATH(i, n);
4709a747e4fSDavid du Colombier 				qid->type = tab[i].mode>>24;
4719a747e4fSDavid du Colombier 				return nil;
4729a747e4fSDavid du Colombier 			}
4739a747e4fSDavid du Colombier 			break;
4749a747e4fSDavid du Colombier 		}
4759a747e4fSDavid du Colombier 		if(i==Qbodyext){
4769a747e4fSDavid du Colombier 			ext = client[NUM(path)]->ext;
4779a747e4fSDavid du Colombier 			snprint(buf, sizeof buf, "body.%s", ext == nil ? "xxx" : ext);
4789a747e4fSDavid du Colombier 			if(strcmp(buf, name) == 0){
4799a747e4fSDavid du Colombier 				qid->path = PATH(i, NUM(path));
4809a747e4fSDavid du Colombier 				qid->type = tab[i].mode>>24;
4819a747e4fSDavid du Colombier 				return nil;
4829a747e4fSDavid du Colombier 			}
4839a747e4fSDavid du Colombier 		}
4849a747e4fSDavid du Colombier 		else if(strcmp(name, tab[i].name) == 0){
4859a747e4fSDavid du Colombier 			qid->path = PATH(i, NUM(path));
4869a747e4fSDavid du Colombier 			qid->type = tab[i].mode>>24;
4879a747e4fSDavid du Colombier 			return nil;
4889a747e4fSDavid du Colombier 		}
4899a747e4fSDavid du Colombier 		if(tab[i].mode&DMDIR)
4909a747e4fSDavid du Colombier 			break;
4919a747e4fSDavid du Colombier 	}
4929a747e4fSDavid du Colombier 	return "directory entry not found";
4939a747e4fSDavid du Colombier }
4949a747e4fSDavid du Colombier 
4959a747e4fSDavid du Colombier static void
fsflush(Req * r)4969a747e4fSDavid du Colombier fsflush(Req *r)
4979a747e4fSDavid du Colombier {
4989a747e4fSDavid du Colombier 	Req *or;
4999a747e4fSDavid du Colombier 	int t;
5009a747e4fSDavid du Colombier 	Client *c;
5019a747e4fSDavid du Colombier 	ulong path;
5029a747e4fSDavid du Colombier 
5039a747e4fSDavid du Colombier 	or=r;
5049a747e4fSDavid du Colombier 	while(or->ifcall.type==Tflush)
5059a747e4fSDavid du Colombier 		or = or->oldreq;
5069a747e4fSDavid du Colombier 
5079a747e4fSDavid du Colombier 	if(or->ifcall.type != Tread && or->ifcall.type != Topen)
5089a747e4fSDavid du Colombier 		abort();
5099a747e4fSDavid du Colombier 
5109a747e4fSDavid du Colombier 	path = or->fid->qid.path;
5119a747e4fSDavid du Colombier 	t = TYPE(path);
5129a747e4fSDavid du Colombier 	if(t != Qbody && t != Qbodyext)
5139a747e4fSDavid du Colombier 		abort();
5149a747e4fSDavid du Colombier 
5159a747e4fSDavid du Colombier 	c = client[NUM(path)];
5169a747e4fSDavid du Colombier 	sendp(c->creq, r);
5173ff48bf5SDavid du Colombier 	iointerrupt(c->io);
5189a747e4fSDavid du Colombier }
5199a747e4fSDavid du Colombier 
5209a747e4fSDavid du Colombier static void
fsthread(void *)5219a747e4fSDavid du Colombier fsthread(void*)
5229a747e4fSDavid du Colombier {
5239a747e4fSDavid du Colombier 	ulong path;
5249a747e4fSDavid du Colombier 	Alt a[3];
5259a747e4fSDavid du Colombier 	Fid *fid;
5269a747e4fSDavid du Colombier 	Req *r;
5279a747e4fSDavid du Colombier 
5289a747e4fSDavid du Colombier 	threadsetname("fsthread");
5299a747e4fSDavid du Colombier 	plumbstart();
5309a747e4fSDavid du Colombier 
5319a747e4fSDavid du Colombier 	a[0].op = CHANRCV;
5329a747e4fSDavid du Colombier 	a[0].c = cclunk;
5339a747e4fSDavid du Colombier 	a[0].v = &fid;
5349a747e4fSDavid du Colombier 	a[1].op = CHANRCV;
5359a747e4fSDavid du Colombier 	a[1].c = creq;
5369a747e4fSDavid du Colombier 	a[1].v = &r;
5379a747e4fSDavid du Colombier 	a[2].op = CHANEND;
5389a747e4fSDavid du Colombier 
5399a747e4fSDavid du Colombier 	for(;;){
5409a747e4fSDavid du Colombier 		switch(alt(a)){
5419a747e4fSDavid du Colombier 		case 0:
5429a747e4fSDavid du Colombier 			path = fid->qid.path;
5439a747e4fSDavid du Colombier 			if(TYPE(path)==Qcookies)
5449a747e4fSDavid du Colombier 				cookieclunk(fid);
5459a747e4fSDavid du Colombier 			if(fid->omode != -1 && TYPE(path) >= Qclient)
5469a747e4fSDavid du Colombier 				closeclient(client[NUM(path)]);
5479a747e4fSDavid du Colombier 			sendp(cclunkwait, nil);
5489a747e4fSDavid du Colombier 			break;
5499a747e4fSDavid du Colombier 		case 1:
5509a747e4fSDavid du Colombier 			switch(r->ifcall.type){
5519a747e4fSDavid du Colombier 			case Tattach:
5529a747e4fSDavid du Colombier 				fsattach(r);
5539a747e4fSDavid du Colombier 				break;
5549a747e4fSDavid du Colombier 			case Topen:
5559a747e4fSDavid du Colombier 				fsopen(r);
5569a747e4fSDavid du Colombier 				break;
5579a747e4fSDavid du Colombier 			case Tread:
5589a747e4fSDavid du Colombier 				fsread(r);
5599a747e4fSDavid du Colombier 				break;
5609a747e4fSDavid du Colombier 			case Twrite:
5619a747e4fSDavid du Colombier 				fswrite(r);
5629a747e4fSDavid du Colombier 				break;
5639a747e4fSDavid du Colombier 			case Tstat:
5649a747e4fSDavid du Colombier 				fsstat(r);
5659a747e4fSDavid du Colombier 				break;
5669a747e4fSDavid du Colombier 			case Tflush:
5679a747e4fSDavid du Colombier 				fsflush(r);
5689a747e4fSDavid du Colombier 				break;
5699a747e4fSDavid du Colombier 			default:
5709a747e4fSDavid du Colombier 				respond(r, "bug in fsthread");
5719a747e4fSDavid du Colombier 				break;
5729a747e4fSDavid du Colombier 			}
5739a747e4fSDavid du Colombier 			sendp(creqwait, 0);
5749a747e4fSDavid du Colombier 			break;
5759a747e4fSDavid du Colombier 		}
5769a747e4fSDavid du Colombier 	}
5779a747e4fSDavid du Colombier }
5789a747e4fSDavid du Colombier 
5799a747e4fSDavid du Colombier static void
fssend(Req * r)5809a747e4fSDavid du Colombier fssend(Req *r)
5819a747e4fSDavid du Colombier {
5829a747e4fSDavid du Colombier 	sendp(creq, r);
5839a747e4fSDavid du Colombier 	recvp(creqwait);	/* avoids need to deal with spurious flushes */
5849a747e4fSDavid du Colombier }
5859a747e4fSDavid du Colombier 
5869a747e4fSDavid du Colombier void
initfs(void)5879a747e4fSDavid du Colombier initfs(void)
5889a747e4fSDavid du Colombier {
5899a747e4fSDavid du Colombier 	time0 = time(0);
5909a747e4fSDavid du Colombier 	creq = chancreate(sizeof(void*), 0);
5919a747e4fSDavid du Colombier 	creqwait = chancreate(sizeof(void*), 0);
5929a747e4fSDavid du Colombier 	cclunk = chancreate(sizeof(void*), 0);
5939a747e4fSDavid du Colombier 	cclunkwait = chancreate(sizeof(void*), 0);
5949a747e4fSDavid du Colombier 	procrfork(fsthread, nil, STACK, RFNAMEG);
5959a747e4fSDavid du Colombier }
5969a747e4fSDavid du Colombier 
5979a747e4fSDavid du Colombier void
takedown(Srv *)5989a747e4fSDavid du Colombier takedown(Srv*)
5999a747e4fSDavid du Colombier {
6009a747e4fSDavid du Colombier 	closecookies();
6019a747e4fSDavid du Colombier 	threadexitsall("done");
6029a747e4fSDavid du Colombier }
6039a747e4fSDavid du Colombier 
6049a747e4fSDavid du Colombier Srv fs =
6059a747e4fSDavid du Colombier {
6069a747e4fSDavid du Colombier .attach=		fssend,
6079a747e4fSDavid du Colombier .destroyfid=	fsdestroyfid,
6089a747e4fSDavid du Colombier .walk1=		fswalk1,
6099a747e4fSDavid du Colombier .open=		fssend,
6109a747e4fSDavid du Colombier .read=		fssend,
6119a747e4fSDavid du Colombier .write=		fssend,
6129a747e4fSDavid du Colombier .stat=		fssend,
6139a747e4fSDavid du Colombier .flush=		fssend,
6149a747e4fSDavid du Colombier .end=		takedown,
6159a747e4fSDavid du Colombier };
6169a747e4fSDavid du Colombier 
617