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
initfcall(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
addport(char * port)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
getclock(void)183 getclock(void)
184 {
185 	return time(0);
186 }
187 
188 void
startfsys(int foreground)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
fsysproc(void * v)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
fsysrespond(Fcall * t,uchar * buf,char * err)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*
newfid(int 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
dostat(Dirtab * dir,uchar * buf,uint nbuf,uint clock)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
queuesend(Dirtab * d,Plumbmsg * m)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
queueread(Dirtab * d,Fcall * t,uchar * buf,Fid * f)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
drainqueue(Dirtab * d)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
flushqueue(Dirtab * d,int oldtag)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
removesenders(Dirtab * d,Fid * fid)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
hold(Plumbmsg * m,Dirtab * d)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
queueheld(Dirtab * d)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
dispose(Fcall * t,uchar * buf,Plumbmsg * m,Ruleset * rs,Exec * e)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*
fsysversion(Fcall * t,uchar * buf,Fid * fid)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*
fsysauth(Fcall * t,uchar * buf,Fid * fid)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*
fsysattach(Fcall * t,uchar * buf,Fid * f)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*
fsysflush(Fcall * t,uchar * buf,Fid * fid)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*
fsyswalk(Fcall * t,uchar * buf,Fid * f)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*
fsysopen(Fcall * t,uchar * buf,Fid * f)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*
fsyscreate(Fcall * t,uchar * buf,Fid * fid)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*
fsysreadrules(Fcall * t,uchar * buf)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*
fsysread(Fcall * t,uchar * buf,Fid * f)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*
fsyswrite(Fcall * t,uchar * buf,Fid * f)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*
fsysstat(Fcall * t,uchar * buf,Fid * f)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*
fsyswstat(Fcall * t,uchar * buf,Fid * fid)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*
fsysremove(Fcall * t,uchar * buf,Fid * fid)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*
fsysclunk(Fcall * t,uchar * buf,Fid * f)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 }
974