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