Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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
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
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
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*
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
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
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
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
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
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
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
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
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
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*
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*
577
fsysauth(Fcall *t, uchar *buf, Fid*)
578
{
579
	fsysrespond(t, buf, "plumber: authentication not required");
580
	return t;
581
}
582
 
583
static Fcall*
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*
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*
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*
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*
764
fsyscreate(Fcall *t, uchar *buf, Fid*)
765
{
766
	fsysrespond(t, buf, Eperm);
767
	return t;
768
}
769
 
770
static Fcall*
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*
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*
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*
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*
919
fsyswstat(Fcall *t, uchar *buf, Fid*)
920
{
921
	fsysrespond(t, buf, Eperm);
922
	return t;
923
}
924
 
925
static Fcall*
926
fsysremove(Fcall *t, uchar *buf, Fid*)
927
{
928
	fsysrespond(t, buf, Eperm);
929
	return t;
930
}
931
 
932
static Fcall*
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
}