plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

fsys.c (18312B)


      1 #include <u.h>
      2 #include <libc.h>
      3 #include <bio.h>
      4 #include <regexp.h>
      5 #include <thread.h>
      6 #include <fcall.h>
      7 #include <plumb.h>
      8 #include "plumber.h"
      9 
     10 enum
     11 {
     12 	Stack = 32*1024
     13 };
     14 
     15 typedef struct Dirtab Dirtab;
     16 typedef struct Fid Fid;
     17 typedef struct Holdq Holdq;
     18 typedef struct Readreq Readreq;
     19 typedef struct Sendreq Sendreq;
     20 
     21 struct Dirtab
     22 {
     23 	char		*name;
     24 	uchar	type;
     25 	uint		qid;
     26 	uint		perm;
     27 	int		nopen;		/* #fids open on this port */
     28 	Fid		*fopen;
     29 	Holdq	*holdq;
     30 	Readreq	*readq;
     31 	Sendreq	*sendq;
     32 };
     33 
     34 struct Fid
     35 {
     36 	int		fid;
     37 	int		busy;
     38 	int		open;
     39 	int		mode;
     40 	Qid		qid;
     41 	Dirtab	*dir;
     42 	long		offset;		/* zeroed at beginning of each message, read or write */
     43 	char		*writebuf;		/* partial message written so far; offset tells how much */
     44 	Fid		*next;
     45 	Fid		*nextopen;
     46 };
     47 
     48 struct Readreq
     49 {
     50 	Fid		*fid;
     51 	Fcall		*fcall;
     52 	uchar	*buf;
     53 	Readreq	*next;
     54 };
     55 
     56 struct Sendreq
     57 {
     58 	int			nfid;		/* number of fids that should receive this message */
     59 	int			nleft;		/* number left that haven't received it */
     60 	Fid			**fid;	/* fid[nfid] */
     61 	Plumbmsg	*msg;
     62 	char			*pack;	/* plumbpack()ed message */
     63 	int			npack;	/* length of pack */
     64 	Sendreq		*next;
     65 };
     66 
     67 struct Holdq
     68 {
     69 	Plumbmsg	*msg;
     70 	Holdq		*next;
     71 };
     72 
     73 struct	/* needed because incref() doesn't return value */
     74 {
     75 	Lock	lk;
     76 	int	ref;
     77 } rulesref;
     78 
     79 enum
     80 {
     81 	NDIR	= 50,
     82 	Nhash	= 16,
     83 
     84 	Qdir		= 0,
     85 	Qrules	= 1,
     86 	Qsend	= 2,
     87 	Qport	= 3,
     88 	NQID	= Qport
     89 };
     90 
     91 static Dirtab dir[NDIR] =
     92 {
     93 	{ ".",			QTDIR,	Qdir,			0500|DMDIR },
     94 	{ "rules",		QTFILE,	Qrules,		0600 },
     95 	{ "send",		QTFILE,	Qsend,		0200 }
     96 };
     97 static int	ndir = NQID;
     98 
     99 static int		srvfd;
    100 #define clock plumbclock	/* SunOS name clash */
    101 static int		clock;
    102 static Fid		*fids[Nhash];
    103 static QLock	readlock;
    104 static QLock	queue;
    105 static int		messagesize = 8192+IOHDRSZ;	/* good start */
    106 
    107 static void	fsysproc(void*);
    108 static void fsysrespond(Fcall*, uchar*, char*);
    109 static Fid*	newfid(int);
    110 
    111 static Fcall* fsysflush(Fcall*, uchar*, Fid*);
    112 static Fcall* fsysversion(Fcall*, uchar*, Fid*);
    113 static Fcall* fsysauth(Fcall*, uchar*, Fid*);
    114 static Fcall* fsysattach(Fcall*, uchar*, Fid*);
    115 static Fcall* fsyswalk(Fcall*, uchar*, Fid*);
    116 static Fcall* fsysopen(Fcall*, uchar*, Fid*);
    117 static Fcall* fsyscreate(Fcall*, uchar*, Fid*);
    118 static Fcall* fsysread(Fcall*, uchar*, Fid*);
    119 static Fcall* fsyswrite(Fcall*, uchar*, Fid*);
    120 static Fcall* fsysclunk(Fcall*, uchar*, Fid*);
    121 static Fcall* fsysremove(Fcall*, uchar*, Fid*);
    122 static Fcall* fsysstat(Fcall*, uchar*, Fid*);
    123 static Fcall* fsyswstat(Fcall*, uchar*, Fid*);
    124 
    125 Fcall* 	(*fcall[Tmax])(Fcall*, uchar*, Fid*);
    126 
    127 static void
    128 initfcall(void)
    129 {
    130 	fcall[Tflush]	= fsysflush;
    131 	fcall[Tversion]	= fsysversion;
    132 	fcall[Tauth]	= fsysauth;
    133 	fcall[Tattach]	= fsysattach;
    134 	fcall[Twalk]	= fsyswalk;
    135 	fcall[Topen]	= fsysopen;
    136 	fcall[Tcreate]	= fsyscreate;
    137 	fcall[Tread]	= fsysread;
    138 	fcall[Twrite]	= fsyswrite;
    139 	fcall[Tclunk]	= fsysclunk;
    140 	fcall[Tremove]= fsysremove;
    141 	fcall[Tstat]	= fsysstat;
    142 	fcall[Twstat]	= fsyswstat;
    143 }
    144 
    145 char	Ebadfcall[] =	"bad fcall type";
    146 char	Eperm[] = 	"permission denied";
    147 char	Enomem[] =	"malloc failed for buffer";
    148 char	Enotdir[] =	"not a directory";
    149 char	Enoexist[] =	"plumb file does not exist";
    150 char	Eisdir[] =		"file is a directory";
    151 char	Ebadmsg[] =	"bad plumb message format";
    152 char Enosuchport[] ="no such plumb port";
    153 char Enoport[] =	"couldn't find destination for message";
    154 char	Einuse[] = 	"file already open";
    155 
    156 /*
    157  * Add new port.  A no-op if port already exists or is the null string
    158  */
    159 void
    160 addport(char *port)
    161 {
    162 	int i;
    163 
    164 	if(port == nil)
    165 		return;
    166 	for(i=NQID; i<ndir; i++)
    167 		if(strcmp(port, dir[i].name) == 0)
    168 			return;
    169 	if(i == NDIR){
    170 		fprint(2, "plumb: too many ports; max %d\n", NDIR);
    171 		return;
    172 	}
    173 	ndir++;
    174 	dir[i].name = estrdup(port);
    175 	dir[i].qid = i;
    176 	dir[i].perm = 0400;
    177 	nports++;
    178 	ports = erealloc(ports, nports*sizeof(char*));
    179 	ports[nports-1] = dir[i].name;
    180 }
    181 
    182 static ulong
    183 getclock(void)
    184 {
    185 	return time(0);
    186 }
    187 
    188 void
    189 startfsys(int foreground)
    190 {
    191 	int p[2];
    192 
    193 	fmtinstall('F', fcallfmt);
    194 	clock = getclock();
    195 	if(pipe(p) < 0)
    196 		error("can't create pipe: %r");
    197 	/* 0 will be server end, 1 will be client end */
    198 	srvfd = p[0];
    199 	if(post9pservice(p[1], "plumb", nil) < 0)
    200 		sysfatal("post9pservice plumb: %r");
    201 	close(p[1]);
    202 	if(foreground)
    203 		fsysproc(nil);
    204 	else
    205 		proccreate(fsysproc, nil, Stack);
    206 }
    207 
    208 static void
    209 fsysproc(void *v)
    210 {
    211 	int n;
    212 	Fcall *t;
    213 	Fid *f;
    214 	uchar *buf;
    215 
    216 	USED(v);
    217 	initfcall();
    218 	t = nil;
    219 	for(;;){
    220 		buf = malloc(messagesize);	/* avoid memset of emalloc */
    221 		if(buf == nil)
    222 			error("malloc failed: %r");
    223 		qlock(&readlock);
    224 		n = read9pmsg(srvfd, buf, messagesize);
    225 		if(n <= 0){
    226 			if(n < 0)
    227 				error("i/o error on server channel");
    228 			threadexitsall("unmounted");
    229 		}
    230 		/*
    231 		 * can give false positive (create an extra fsysproc) once in a while,
    232 		 * but no false negatives, so good enough.  once we have one extra
    233 		 * we'll never have more.
    234 		 */
    235 		if(readlock.waiting.head == nil)	/* no other processes waiting to read; start one */
    236 			proccreate(fsysproc, nil, Stack);
    237 		qunlock(&readlock);
    238 		if(t == nil)
    239 			t = emalloc(sizeof(Fcall));
    240 		if(convM2S(buf, n, t) != n)
    241 			error("convert error in convM2S");
    242 		if(debug)
    243 			fprint(2, "<= %F\n", t);
    244 		if(fcall[t->type] == 0)
    245 			fsysrespond(t, buf, Ebadfcall);
    246 		else{
    247 			if(t->type==Tversion || t->type==Tauth)
    248 				f = nil;
    249 			else
    250 				f = newfid(t->fid);
    251 			t = (*fcall[t->type])(t, buf, f);
    252 		}
    253 	}
    254 }
    255 
    256 static void
    257 fsysrespond(Fcall *t, uchar *buf, char *err)
    258 {
    259 	int n;
    260 
    261 	if(err){
    262 		t->type = Rerror;
    263 		t->ename = err;
    264 	}else
    265 		t->type++;
    266 	if(buf == nil)
    267 		buf = emalloc(messagesize);
    268 	n = convS2M(t, buf, messagesize);
    269 	if(n < 0)
    270 		error("convert error in convS2M");
    271 	if(write(srvfd, buf, n) != n)
    272 		error("write error in respond");
    273 	if(debug)
    274 		fprint(2, "=> %F\n", t);
    275 	free(buf);
    276 }
    277 
    278 static
    279 Fid*
    280 newfid(int fid)
    281 {
    282 	Fid *f, *ff, **fh;
    283 
    284 	qlock(&queue);
    285 	ff = nil;
    286 	fh = &fids[fid&(Nhash-1)];
    287 	for(f=*fh; f; f=f->next)
    288 		if(f->fid == fid)
    289 			goto Return;
    290 		else if(ff==nil && !f->busy)
    291 			ff = f;
    292 	if(ff){
    293 		ff->fid = fid;
    294 		f = ff;
    295 		goto Return;
    296 	}
    297 	f = emalloc(sizeof *f);
    298 	f->fid = fid;
    299 	f->next = *fh;
    300 	*fh = f;
    301     Return:
    302 	qunlock(&queue);
    303 	return f;
    304 }
    305 
    306 static uint
    307 dostat(Dirtab *dir, uchar *buf, uint nbuf, uint clock)
    308 {
    309 	Dir d;
    310 
    311 	d.qid.type = dir->type;
    312 	d.qid.path = dir->qid;
    313 	d.qid.vers = 0;
    314 	d.mode = dir->perm;
    315 	d.length = 0;	/* would be nice to do better */
    316 	d.name = dir->name;
    317 	d.uid = user;
    318 	d.gid = user;
    319 	d.muid = user;
    320 	d.atime = clock;
    321 	d.mtime = clock;
    322 	return convD2M(&d, buf, nbuf);
    323 }
    324 
    325 static void
    326 queuesend(Dirtab *d, Plumbmsg *m)
    327 {
    328 	Sendreq *s, *t;
    329 	Fid *f;
    330 	int i;
    331 
    332 	s = emalloc(sizeof(Sendreq));
    333 	s->nfid = d->nopen;
    334 	s->nleft = s->nfid;
    335 	s->fid = emalloc(s->nfid*sizeof(Fid*));
    336 	i = 0;
    337 	/* build array of fids open on this channel */
    338 	for(f=d->fopen; f!=nil; f=f->nextopen)
    339 		s->fid[i++] = f;
    340 	s->msg = m;
    341 	s->next = nil;
    342 	/* link to end of queue; drainqueue() searches in sender order so this implements a FIFO */
    343 	for(t=d->sendq; t!=nil; t=t->next)
    344 		if(t->next == nil)
    345 			break;
    346 	if(t == nil)
    347 		d->sendq = s;
    348 	else
    349 		t->next = s;
    350 }
    351 
    352 static void
    353 queueread(Dirtab *d, Fcall *t, uchar *buf, Fid *f)
    354 {
    355 	Readreq *r;
    356 
    357 	r = emalloc(sizeof(Readreq));
    358 	r->fcall = t;
    359 	r->buf = buf;
    360 	r->fid = f;
    361 	r->next = d->readq;
    362 	d->readq = r;
    363 }
    364 
    365 static void
    366 drainqueue(Dirtab *d)
    367 {
    368 	Readreq *r, *nextr, *prevr;
    369 	Sendreq *s, *nexts, *prevs;
    370 	int i, n;
    371 
    372 	prevs = nil;
    373 	for(s=d->sendq; s!=nil; s=nexts){
    374 		nexts = s->next;
    375 		for(i=0; i<s->nfid; i++){
    376 			prevr = nil;
    377 			for(r=d->readq; r!=nil; r=nextr){
    378 				nextr = r->next;
    379 				if(r->fid == s->fid[i]){
    380 					/* pack the message if necessary */
    381 					if(s->pack == nil)
    382 						s->pack = plumbpack(s->msg, &s->npack);
    383 					/* exchange the stuff... */
    384 					r->fcall->data = s->pack+r->fid->offset;
    385 					n = s->npack - r->fid->offset;
    386 					if(n > messagesize-IOHDRSZ)
    387 						n = messagesize-IOHDRSZ;
    388 					if(n > r->fcall->count)
    389 						n = r->fcall->count;
    390 					r->fcall->count = n;
    391 					fsysrespond(r->fcall, r->buf, nil);
    392 					r->fid->offset += n;
    393 					if(r->fid->offset >= s->npack){
    394 						/* message transferred; delete this fid from send queue */
    395 						r->fid->offset = 0;
    396 						s->fid[i] = nil;
    397 						s->nleft--;
    398 					}
    399 					/* delete read request from queue */
    400 					if(prevr)
    401 						prevr->next = r->next;
    402 					else
    403 						d->readq = r->next;
    404 					free(r->fcall);
    405 					free(r);
    406 					break;
    407 				}else
    408 					prevr = r;
    409 			}
    410 		}
    411 		/* if no fids left, delete this send from queue */
    412 		if(s->nleft == 0){
    413 			free(s->fid);
    414 			plumbfree(s->msg);
    415 			free(s->pack);
    416 			if(prevs)
    417 				prevs->next = s->next;
    418 			else
    419 				d->sendq = s->next;
    420 			free(s);
    421 		}else
    422 			prevs = s;
    423 	}
    424 }
    425 
    426 /* can't flush a send because they are always answered synchronously */
    427 static void
    428 flushqueue(Dirtab *d, int oldtag)
    429 {
    430 	Readreq *r, *prevr;
    431 
    432 	prevr = nil;
    433 	for(r=d->readq; r!=nil; r=r->next){
    434 		if(oldtag == r->fcall->tag){
    435 			/* delete read request from queue */
    436 			if(prevr)
    437 				prevr->next = r->next;
    438 			else
    439 				d->readq = r->next;
    440 			free(r->fcall);
    441 			free(r->buf);
    442 			free(r);
    443 			return;
    444 		}
    445 		prevr = r;
    446 	}
    447 }
    448 
    449 /* remove messages awaiting delivery to now-closing fid */
    450 static void
    451 removesenders(Dirtab *d, Fid *fid)
    452 {
    453 	Sendreq *s, *nexts, *prevs;
    454 	int i;
    455 
    456 	prevs = nil;
    457 	for(s=d->sendq; s!=nil; s=nexts){
    458 		nexts = s->next;
    459 		for(i=0; i<s->nfid; i++)
    460 			if(fid == s->fid[i]){
    461 				/* delete this fid from send queue */
    462 				s->fid[i] = nil;
    463 				s->nleft--;
    464 				break;
    465 			}
    466 		/* if no fids left, delete this send from queue */
    467 		if(s->nleft == 0){
    468 			free(s->fid);
    469 			plumbfree(s->msg);
    470 			free(s->pack);
    471 			if(prevs)
    472 				prevs->next = s->next;
    473 			else
    474 				d->sendq = s->next;
    475 			free(s);
    476 		}else
    477 			prevs = s;
    478 	}
    479 }
    480 
    481 static void
    482 hold(Plumbmsg *m, Dirtab *d)
    483 {
    484 	Holdq *h, *q;
    485 
    486 	h = emalloc(sizeof(Holdq));
    487 	h->msg = m;
    488 	/* add to end of queue */
    489 	if(d->holdq == nil)
    490 		d->holdq = h;
    491 	else{
    492 		for(q=d->holdq; q->next!=nil; q=q->next)
    493 			;
    494 		q->next = h;
    495 	}
    496 }
    497 
    498 static void
    499 queueheld(Dirtab *d)
    500 {
    501 	Holdq *h;
    502 
    503 	while(d->holdq != nil){
    504 		h = d->holdq;
    505 		d->holdq = h->next;
    506 		queuesend(d, h->msg);
    507 		/* no need to drain queue because we know no-one is reading yet */
    508 		free(h);
    509 	}
    510 }
    511 
    512 static void
    513 dispose(Fcall *t, uchar *buf, Plumbmsg *m, Ruleset *rs, Exec *e)
    514 {
    515 	int i;
    516 	char *err;
    517 
    518 	qlock(&queue);
    519 	err = nil;
    520 	if(m->dst==nil || m->dst[0]=='\0'){
    521 		err = Enoport;
    522 		if(rs != nil)
    523 			err = startup(rs, e);
    524 		plumbfree(m);
    525 	}else
    526 		for(i=NQID; i<ndir; i++)
    527 			if(strcmp(m->dst, dir[i].name) == 0){
    528 				if(dir[i].nopen == 0){
    529 					err = startup(rs, e);
    530 					if(e!=nil && e->holdforclient)
    531 						hold(m, &dir[i]);
    532 					else
    533 						plumbfree(m);
    534 				}else{
    535 					queuesend(&dir[i], m);
    536 					drainqueue(&dir[i]);
    537 				}
    538 				break;
    539 			}
    540 	freeexec(e);
    541 	qunlock(&queue);
    542 	fsysrespond(t, buf, err);
    543 	free(t);
    544 }
    545 
    546 static Fcall*
    547 fsysversion(Fcall *t, uchar *buf, Fid *fid)
    548 {
    549 	USED(fid);
    550 
    551 	if(t->msize < 256){
    552 		fsysrespond(t, buf, "version: message size too small");
    553 		return t;
    554 	}
    555 	if(t->msize < messagesize)
    556 		messagesize = t->msize;
    557 	t->msize = messagesize;
    558 	if(strncmp(t->version, "9P2000", 6) != 0){
    559 		fsysrespond(t, buf, "unrecognized 9P version");
    560 		return t;
    561 	}
    562 	t->version = "9P2000";
    563 	fsysrespond(t, buf, nil);
    564 	return t;
    565 }
    566 
    567 static Fcall*
    568 fsysauth(Fcall *t, uchar *buf, Fid *fid)
    569 {
    570 	USED(fid);
    571 	fsysrespond(t, buf, "plumber: authentication not required");
    572 	return t;
    573 }
    574 
    575 static Fcall*
    576 fsysattach(Fcall *t, uchar *buf, Fid *f)
    577 {
    578 	Fcall out;
    579 
    580 /*
    581 	if(strcmp(t->uname, user) != 0){
    582 		fsysrespond(&out, buf, Eperm);
    583 		return t;
    584 	}
    585 */
    586 	f->busy = 1;
    587 	f->open = 0;
    588 	f->qid.type = QTDIR;
    589 	f->qid.path = Qdir;
    590 	f->qid.vers = 0;
    591 	f->dir = dir;
    592 	memset(&out, 0, sizeof(Fcall));
    593 	out.type = t->type;
    594 	out.tag = t->tag;
    595 	out.fid = f->fid;
    596 	out.qid = f->qid;
    597 	fsysrespond(&out, buf, nil);
    598 	return t;
    599 }
    600 
    601 static Fcall*
    602 fsysflush(Fcall *t, uchar *buf, Fid *fid)
    603 {
    604 	int i;
    605 
    606 	USED(fid);
    607 	qlock(&queue);
    608 	for(i=NQID; i<ndir; i++)
    609 		flushqueue(&dir[i], t->oldtag);
    610 	qunlock(&queue);
    611 	fsysrespond(t, buf, nil);
    612 	return t;
    613 }
    614 
    615 static Fcall*
    616 fsyswalk(Fcall *t, uchar *buf, Fid *f)
    617 {
    618 	Fcall out;
    619 	Fid *nf;
    620 	ulong path;
    621 	Dirtab *d, *dir;
    622 	Qid q;
    623 	int i;
    624 	uchar type;
    625 	char *err;
    626 
    627 	if(f->open){
    628 		fsysrespond(t, buf, "clone of an open fid");
    629 		return t;
    630 	}
    631 
    632 	nf = nil;
    633 	if(t->fid  != t->newfid){
    634 		nf = newfid(t->newfid);
    635 		if(nf->busy){
    636 			fsysrespond(t, buf, "clone to a busy fid");
    637 			return t;
    638 		}
    639 		nf->busy = 1;
    640 		nf->open = 0;
    641 		nf->dir = f->dir;
    642 		nf->qid = f->qid;
    643 		f = nf;	/* walk f */
    644 	}
    645 
    646 	out.nwqid = 0;
    647 	err = nil;
    648 	dir = f->dir;
    649 	q = f->qid;
    650 
    651 	if(t->nwname > 0){
    652 		for(i=0; i<t->nwname; i++){
    653 			if((q.type & QTDIR) == 0){
    654 				err = Enotdir;
    655 				break;
    656 			}
    657 			if(strcmp(t->wname[i], "..") == 0){
    658 				type = QTDIR;
    659 				path = Qdir;
    660 	Accept:
    661 				q.type = type;
    662 				q.vers = 0;
    663 				q.path = path;
    664 				out.wqid[out.nwqid++] = q;
    665 				continue;
    666 			}
    667 			d = dir;
    668 			d++;	/* skip '.' */
    669 			for(; d->name; d++)
    670 				if(strcmp(t->wname[i], d->name) == 0){
    671 					type = d->type;
    672 					path = d->qid;
    673 					dir = d;
    674 					goto Accept;
    675 				}
    676 			err = Enoexist;
    677 			break;
    678 		}
    679 	}
    680 
    681 	out.type = t->type;
    682 	out.tag = t->tag;
    683 	if(err!=nil || out.nwqid<t->nwname){
    684 		if(nf)
    685 			nf->busy = 0;
    686 	}else if(out.nwqid == t->nwname){
    687 		f->qid = q;
    688 		f->dir = dir;
    689 	}
    690 
    691 	fsysrespond(&out, buf, err);
    692 	return t;
    693 }
    694 
    695 static Fcall*
    696 fsysopen(Fcall *t, uchar *buf, Fid *f)
    697 {
    698 	int m, clearrules, mode;
    699 
    700 	clearrules = 0;
    701 	if(t->mode & OTRUNC){
    702 		if(f->qid.path != Qrules)
    703 			goto Deny;
    704 		clearrules = 1;
    705 	}
    706 	/* can't truncate anything, so just disregard */
    707 	mode = t->mode & ~(OTRUNC|OCEXEC);
    708 	/* can't execute or remove anything */
    709 	if(mode==OEXEC || (mode&ORCLOSE))
    710 		goto Deny;
    711 	switch(mode){
    712 	default:
    713 		goto Deny;
    714 	case OREAD:
    715 		m = 0400;
    716 		break;
    717 	case OWRITE:
    718 		m = 0200;
    719 		break;
    720 	case ORDWR:
    721 		m = 0600;
    722 		break;
    723 	}
    724 	if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
    725 		goto Deny;
    726 	if(f->qid.path==Qrules && (mode==OWRITE || mode==ORDWR)){
    727 		lock(&rulesref.lk);
    728 		if(rulesref.ref++ != 0){
    729 			rulesref.ref--;
    730 			unlock(&rulesref.lk);
    731 			fsysrespond(t, buf, Einuse);
    732 			return t;
    733 		}
    734 		unlock(&rulesref.lk);
    735 	}
    736 	if(clearrules){
    737 		writerules(nil, 0);
    738 		rules[0] = nil;
    739 	}
    740 	t->qid = f->qid;
    741 	t->iounit = 0;
    742 	qlock(&queue);
    743 	f->mode = mode;
    744 	f->open = 1;
    745 	f->dir->nopen++;
    746 	f->nextopen = f->dir->fopen;
    747 	f->dir->fopen = f;
    748 	queueheld(f->dir);
    749 	qunlock(&queue);
    750 	fsysrespond(t, buf, nil);
    751 	return t;
    752 
    753     Deny:
    754 	fsysrespond(t, buf, Eperm);
    755 	return t;
    756 }
    757 
    758 static Fcall*
    759 fsyscreate(Fcall *t, uchar *buf, Fid *fid)
    760 {
    761 	USED(fid);
    762 	fsysrespond(t, buf, Eperm);
    763 	return t;
    764 }
    765 
    766 static Fcall*
    767 fsysreadrules(Fcall *t, uchar *buf)
    768 {
    769 	char *p;
    770 	int n;
    771 
    772 	p = printrules();
    773 	n = strlen(p);
    774 	t->data = p;
    775 	if(t->offset >= n)
    776 		t->count = 0;
    777 	else{
    778 		t->data = p+t->offset;
    779 		if(t->offset+t->count > n)
    780 			t->count = n-t->offset;
    781 	}
    782 	fsysrespond(t, buf, nil);
    783 	free(p);
    784 	return t;
    785 }
    786 
    787 static Fcall*
    788 fsysread(Fcall *t, uchar *buf, Fid *f)
    789 {
    790 	uchar *b;
    791 	int i, n, o, e;
    792 	uint len;
    793 	Dirtab *d;
    794 	uint clock;
    795 
    796 	if(f->qid.path != Qdir){
    797 		if(f->qid.path == Qrules)
    798 			return fsysreadrules(t, buf);
    799 		/* read from port */
    800 		if(f->qid.path < NQID){
    801 			fsysrespond(t, buf, "internal error: unknown read port");
    802 			return t;
    803 		}
    804 		qlock(&queue);
    805 		queueread(f->dir, t, buf, f);
    806 		drainqueue(f->dir);
    807 		qunlock(&queue);
    808 		return nil;
    809 	}
    810 	o = t->offset;
    811 	e = t->offset+t->count;
    812 	clock = getclock();
    813 	b = malloc(messagesize-IOHDRSZ);
    814 	if(b == nil){
    815 		fsysrespond(t, buf, Enomem);
    816 		return t;
    817 	}
    818 	n = 0;
    819 	d = dir;
    820 	d++;	/* first entry is '.' */
    821 	for(i=0; d->name!=nil && i<e; i+=len){
    822 		len = dostat(d, b+n, messagesize-IOHDRSZ-n, clock);
    823 		if(len <= BIT16SZ)
    824 			break;
    825 		if(i >= o)
    826 			n += len;
    827 		d++;
    828 	}
    829 	t->data = (char*)b;
    830 	t->count = n;
    831 	fsysrespond(t, buf, nil);
    832 	free(b);
    833 	return t;
    834 }
    835 
    836 static Fcall*
    837 fsyswrite(Fcall *t, uchar *buf, Fid *f)
    838 {
    839 	Plumbmsg *m;
    840 	int i, n;
    841 	long count;
    842 	char *data;
    843 	Exec *e;
    844 
    845 	switch((int)f->qid.path){
    846 	case Qdir:
    847 		fsysrespond(t, buf, Eisdir);
    848 		return t;
    849 	case Qrules:
    850 		clock = getclock();
    851 		fsysrespond(t, buf, writerules(t->data, t->count));
    852 		return t;
    853 	case Qsend:
    854 		if(f->offset == 0){
    855 			data = t->data;
    856 			count = t->count;
    857 		}else{
    858 			/* partial message already assembled */
    859 			f->writebuf = erealloc(f->writebuf, f->offset + t->count);
    860 			memmove(f->writebuf+f->offset, t->data, t->count);
    861 			data = f->writebuf;
    862 			count = f->offset+t->count;
    863 		}
    864 		m = plumbunpackpartial(data, count, &n);
    865 		if(m == nil){
    866 			if(n == 0){
    867 				f->offset = 0;
    868 				free(f->writebuf);
    869 				f->writebuf = nil;
    870 				fsysrespond(t, buf, Ebadmsg);
    871 				return t;
    872 			}
    873 			/* can read more... */
    874 			if(f->offset == 0){
    875 				f->writebuf = emalloc(t->count);
    876 				memmove(f->writebuf, t->data, t->count);
    877 			}
    878 			/* else buffer has already been grown */
    879 			f->offset += t->count;
    880 			fsysrespond(t, buf, nil);
    881 			return t;
    882 		}
    883 		/* release partial buffer */
    884 		f->offset = 0;
    885 		free(f->writebuf);
    886 		f->writebuf = nil;
    887 		for(i=0; rules[i]; i++)
    888 			if((e=matchruleset(m, rules[i])) != nil){
    889 				dispose(t, buf, m, rules[i], e);
    890 				return nil;
    891 			}
    892 		if(m->dst != nil){
    893 			dispose(t, buf, m, nil, nil);
    894 			return nil;
    895 		}
    896 		fsysrespond(t, buf, "no matching plumb rule");
    897 		return t;
    898 	}
    899 	fsysrespond(t, buf, "internal error: write to unknown file");
    900 	return t;
    901 }
    902 
    903 static Fcall*
    904 fsysstat(Fcall *t, uchar *buf, Fid *f)
    905 {
    906 	t->stat = emalloc(messagesize-IOHDRSZ);
    907 	t->nstat = dostat(f->dir, t->stat, messagesize-IOHDRSZ, clock);
    908 	fsysrespond(t, buf, nil);
    909 	free(t->stat);
    910 	t->stat = nil;
    911 	return t;
    912 }
    913 
    914 static Fcall*
    915 fsyswstat(Fcall *t, uchar *buf, Fid *fid)
    916 {
    917 	USED(fid);
    918 	fsysrespond(t, buf, Eperm);
    919 	return t;
    920 }
    921 
    922 static Fcall*
    923 fsysremove(Fcall *t, uchar *buf, Fid *fid)
    924 {
    925 	USED(fid);
    926 	fsysrespond(t, buf, Eperm);
    927 	return t;
    928 }
    929 
    930 static Fcall*
    931 fsysclunk(Fcall *t, uchar *buf, Fid *f)
    932 {
    933 	Fid *prev, *p;
    934 	Dirtab *d;
    935 
    936 	qlock(&queue);
    937 	if(f->open){
    938 		d = f->dir;
    939 		d->nopen--;
    940 		if(d->qid==Qrules && (f->mode==OWRITE || f->mode==ORDWR)){
    941 			/*
    942 			 * just to be sure last rule is parsed; error messages will be lost, though,
    943 			 * unless last write ended with a blank line
    944 			 */
    945 			writerules(nil, 0);
    946 			lock(&rulesref.lk);
    947 			rulesref.ref--;
    948 			unlock(&rulesref.lk);
    949 		}
    950 		prev = nil;
    951 		for(p=d->fopen; p; p=p->nextopen){
    952 			if(p == f){
    953 				if(prev)
    954 					prev->nextopen = f->nextopen;
    955 				else
    956 					d->fopen = f->nextopen;
    957 				removesenders(d, f);
    958 				break;
    959 			}
    960 			prev = p;
    961 		}
    962 	}
    963 	f->busy = 0;
    964 	f->open = 0;
    965 	f->offset = 0;
    966 	if(f->writebuf != nil){
    967 		free(f->writebuf);
    968 		f->writebuf = nil;
    969 	}
    970 	qunlock(&queue);
    971 	fsysrespond(t, buf, nil);
    972 	return t;
    973 }