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	"lib.h"
3
#include	"dat.h"
4
#include	"fns.h"
5
#include	"error.h"
6
 
7
/*
8
 * References are managed as follows:
9
 * The channel to the server - a network connection or pipe - has one
10
 * reference for every Chan open on the server.  The server channel has
11
 * c->mux set to the Mnt used for muxing control to that server.  Mnts
12
 * have no reference count; they go away when c goes away.
13
 * Each channel derived from the mount point has mchan set to c,
14
 * and increfs/decrefs mchan to manage references on the server
15
 * connection.
16
 */
17
 
18
#define MAXRPC (IOHDRSZ+8192)
19
 
20
struct Mntrpc
21
{
22
	Chan*	c;		/* Channel for whom we are working */
23
	Mntrpc*	list;		/* Free/pending list */
24
	Fcall	request;	/* Outgoing file system protocol message */
25
	Fcall 	reply;		/* Incoming reply */
26
	Mnt*	m;		/* Mount device during rpc */
27
	Rendez	r;		/* Place to hang out */
28
	uchar*	rpc;		/* I/O Data buffer */
29
	uint		rpclen;	/* len of buffer */
30
	Block	*b;		/* reply blocks */
31
	char	done;		/* Rpc completed */
32
	uvlong	stime;		/* start time for mnt statistics */
33
	ulong	reqlen;		/* request length for mnt statistics */
34
	ulong	replen;		/* reply length for mnt statistics */
35
	Mntrpc*	flushed;	/* message this one flushes */
36
};
37
 
38
enum
39
{
40
	TAGSHIFT = 5,			/* ulong has to be 32 bits */
41
	TAGMASK = (1<<TAGSHIFT)-1,
42
	NMASK = (64*1024)>>TAGSHIFT,
43
};
44
 
45
struct Mntalloc
46
{
47
	Lock lk;
48
	Mnt*	list;		/* Mount devices in use */
49
	Mnt*	mntfree;	/* Free list */
50
	Mntrpc*	rpcfree;
51
	int	nrpcfree;
52
	int	nrpcused;
53
	ulong	id;
54
	ulong	tagmask[NMASK];
55
}mntalloc;
56
 
57
void	mattach(Mnt*, Chan*, char*);
58
Mnt*	mntchk(Chan*);
59
void	mntdirfix(uchar*, Chan*);
60
Mntrpc*	mntflushalloc(Mntrpc*, ulong);
61
void	mntflushfree(Mnt*, Mntrpc*);
62
void	mntfree(Mntrpc*);
63
void	mntgate(Mnt*);
64
void	mntpntfree(Mnt*);
65
void	mntqrm(Mnt*, Mntrpc*);
66
Mntrpc*	mntralloc(Chan*, ulong);
67
long	mntrdwr(int, Chan*, void*, long, vlong);
68
int	mntrpcread(Mnt*, Mntrpc*);
69
void	mountio(Mnt*, Mntrpc*);
70
void	mountmux(Mnt*, Mntrpc*);
71
void	mountrpc(Mnt*, Mntrpc*);
72
int	rpcattn(void*);
73
Chan*	mntchan(void);
74
 
75
char	Esbadstat[] = "invalid directory entry received from server";
76
char Enoversion[] = "version not established for mount channel";
77
 
78
 
79
void (*mntstats)(int, Chan*, uvlong, ulong);
80
 
81
static void
82
mntreset(void)
83
{
84
	mntalloc.id = 1;
85
	mntalloc.tagmask[0] = 1;			/* don't allow 0 as a tag */
86
	mntalloc.tagmask[NMASK-1] = 0x80000000UL;	/* don't allow NOTAG */
87
	fmtinstall('F', fcallfmt);
88
	fmtinstall('D', dirfmt);
89
/* We can't install %M since eipfmt does and is used in the kernel [sape] */
90
 
91
	cinit();
92
}
93
 
94
/*
95
 * Version is not multiplexed: message sent only once per connection.
96
 */
97
long
98
mntversion(Chan *c, char *version, int msize, int returnlen)
99
{
100
	Fcall f;
101
	uchar *msg;
102
	Mnt *m;
103
	char *v;
104
	long k, l;
105
	uvlong oo;
106
	char buf[128];
107
 
108
	qlock(&c->umqlock);	/* make sure no one else does this until we've established ourselves */
109
	if(waserror()){
110
		qunlock(&c->umqlock);
111
		nexterror();
112
	}
113
 
114
	/* defaults */
115
	if(msize == 0)
116
		msize = MAXRPC;
117
	if(msize > c->iounit && c->iounit != 0)
118
		msize = c->iounit;
119
	v = version;
120
	if(v == nil || v[0] == '\0')
121
		v = VERSION9P;
122
 
123
	/* validity */
124
	if(msize < 0)
125
		error("bad iounit in version call");
126
	if(strncmp(v, VERSION9P, strlen(VERSION9P)) != 0)
127
		error("bad 9P version specification");
128
 
129
	m = c->mux;
130
 
131
	if(m != nil){
132
		qunlock(&c->umqlock);
133
		poperror();
134
 
135
		strecpy(buf, buf+sizeof buf, m->version);
136
		k = strlen(buf);
137
		if(strncmp(buf, v, k) != 0){
138
			snprint(buf, sizeof buf, "incompatible 9P versions %s %s", m->version, v);
139
			error(buf);
140
		}
141
		if(returnlen > 0){
142
			if(returnlen < k)
143
				error(Eshort);
144
			memmove(version, buf, k);
145
		}
146
		return k;
147
	}
148
 
149
	f.type = Tversion;
150
	f.tag = NOTAG;
151
	f.msize = msize;
152
	f.version = v;
153
	msg = malloc(8192+IOHDRSZ);
154
	if(msg == nil)
155
		exhausted("version memory");
156
	if(waserror()){
157
		free(msg);
158
		nexterror();
159
	}
160
	k = convS2M(&f, msg, 8192+IOHDRSZ);
161
	if(k == 0)
162
		error("bad fversion conversion on send");
163
 
164
	lock(&c->ref.lk);
165
	oo = c->offset;
166
	c->offset += k;
167
	unlock(&c->ref.lk);
168
 
169
	l = devtab[c->type]->write(c, msg, k, oo);
170
 
171
	if(l < k){
172
		lock(&c->ref.lk);
173
		c->offset -= k - l;
174
		unlock(&c->ref.lk);
175
		error("short write in fversion");
176
	}
177
 
178
	/* message sent; receive and decode reply */
179
	k = devtab[c->type]->read(c, msg, 8192+IOHDRSZ, c->offset);
180
	if(k <= 0)
181
		error("EOF receiving fversion reply");
182
 
183
	lock(&c->ref.lk);
184
	c->offset += k;
185
	unlock(&c->ref.lk);
186
 
187
	l = convM2S(msg, k, &f);
188
	if(l != k)
189
		error("bad fversion conversion on reply");
190
	if(f.type != Rversion){
191
		if(f.type == Rerror)
192
			error(f.ename);
193
		error("unexpected reply type in fversion");
194
	}
195
	if(f.msize > msize)
196
		error("server tries to increase msize in fversion");
197
	if(f.msize<256 || f.msize>1024*1024)
198
		error("nonsense value of msize in fversion");
199
	k = strlen(f.version);
200
	if(strncmp(f.version, v, k) != 0)
201
		error("bad 9P version returned from server");
202
 
203
	/* now build Mnt associated with this connection */
204
	lock(&mntalloc.lk);
205
	m = mntalloc.mntfree;
206
	if(m != 0)
207
		mntalloc.mntfree = m->list;
208
	else {
209
		m = malloc(sizeof(Mnt));
210
		if(m == 0) {
211
			unlock(&mntalloc.lk);
212
			exhausted("mount devices");
213
		}
214
	}
215
	m->list = mntalloc.list;
216
	mntalloc.list = m;
217
	m->version = nil;
218
	kstrdup(&m->version, f.version);
219
	m->id = mntalloc.id++;
220
	m->q = qopen(10*MAXRPC, 0, 0, nil);
221
	m->msize = f.msize;
222
	unlock(&mntalloc.lk);
223
 
224
	if(returnlen > 0){
225
		if(returnlen < k)
226
			error(Eshort);
227
		memmove(version, f.version, k);
228
	}
229
 
230
	poperror();	/* msg */
231
	free(msg);
232
 
233
	lock(&m->lk);
234
	m->queue = 0;
235
	m->rip = 0;
236
 
237
	c->flag |= CMSG;
238
	c->mux = m;
239
	m->c = c;
240
	unlock(&m->lk);
241
 
242
	poperror();	/* c */
243
	qunlock(&c->umqlock);
244
 
245
	return k;
246
}
247
 
248
Chan*
249
mntauth(Chan *c, char *spec)
250
{
251
	Mnt *m;
252
	Mntrpc *r;
253
 
254
	m = c->mux;
255
 
256
	if(m == nil){
257
		mntversion(c, VERSION9P, MAXRPC, 0);
258
		m = c->mux;
259
		if(m == nil)
260
			error(Enoversion);
261
	}
262
 
263
	c = mntchan();
264
	if(waserror()) {
265
		/* Close must not be called since it will
266
		 * call mnt recursively
267
		 */
268
		chanfree(c);
269
		nexterror();
270
	}
271
 
272
	r = mntralloc(0, m->msize);
273
 
274
	if(waserror()) {
275
		mntfree(r);
276
		nexterror();
277
	}
278
 
279
	r->request.type = Tauth;
280
	r->request.afid = c->fid;
281
	r->request.uname = up->user;
282
	r->request.aname = spec;
283
	mountrpc(m, r);
284
 
285
	c->qid = r->reply.aqid;
286
	c->mchan = m->c;
287
	incref(&m->c->ref);
288
	c->mqid = c->qid;
289
	c->mode = ORDWR;
290
 
291
	poperror();	/* r */
292
	mntfree(r);
293
 
294
	poperror();	/* c */
295
 
296
	return c;
297
 
298
}
299
 
300
static Chan*
301
mntattach(char *muxattach)
302
{
303
	Mnt *m;
304
	Chan *c;
305
	Mntrpc *r;
306
	struct bogus{
307
		Chan	*chan;
308
		Chan	*authchan;
309
		char	*spec;
310
		int	flags;
311
	}bogus;
312
 
313
	bogus = *((struct bogus *)muxattach);
314
	c = bogus.chan;
315
 
316
	m = c->mux;
317
 
318
	if(m == nil){
319
		mntversion(c, nil, 0, 0);
320
		m = c->mux;
321
		if(m == nil)
322
			error(Enoversion);
323
	}
324
 
325
	c = mntchan();
326
	if(waserror()) {
327
		/* Close must not be called since it will
328
		 * call mnt recursively
329
		 */
330
		chanfree(c);
331
		nexterror();
332
	}
333
 
334
	r = mntralloc(0, m->msize);
335
 
336
	if(waserror()) {
337
		mntfree(r);
338
		nexterror();
339
	}
340
 
341
	r->request.type = Tattach;
342
	r->request.fid = c->fid;
343
	if(bogus.authchan == nil)
344
		r->request.afid = NOFID;
345
	else
346
		r->request.afid = bogus.authchan->fid;
347
	r->request.uname = up->user;
348
	r->request.aname = bogus.spec;
349
	mountrpc(m, r);
350
 
351
	c->qid = r->reply.qid;
352
	c->mchan = m->c;
353
	incref(&m->c->ref);
354
	c->mqid = c->qid;
355
 
356
	poperror();	/* r */
357
	mntfree(r);
358
 
359
	poperror();	/* c */
360
 
361
	if(bogus.flags&MCACHE)
362
		c->flag |= CCACHE;
363
	return c;
364
}
365
 
366
Chan*
367
mntchan(void)
368
{
369
	Chan *c;
370
 
371
	c = devattach('M', 0);
372
	lock(&mntalloc.lk);
373
	c->dev = mntalloc.id++;
374
	unlock(&mntalloc.lk);
375
 
376
	if(c->mchan)
377
		panic("mntchan non-zero %p", c->mchan);
378
	return c;
379
}
380
 
381
static Walkqid*
382
mntwalk(Chan *c, Chan *nc, char **name, int nname)
383
{
384
	int i, alloc;
385
	Mnt *m;
386
	Mntrpc *r;
387
	Walkqid *wq;
388
 
389
	if(nc != nil)
390
		print("mntwalk: nc != nil\n");
391
	if(nname > MAXWELEM)
392
		error("devmnt: too many name elements");
393
	alloc = 0;
394
	wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
395
	if(waserror()){
396
		if(alloc && wq->clone!=nil)
397
			cclose(wq->clone);
398
		free(wq);
399
		return nil;
400
	}
401
 
402
	alloc = 0;
403
	m = mntchk(c);
404
	r = mntralloc(c, m->msize);
405
	if(nc == nil){
406
		nc = devclone(c);
407
		/*
408
		 * Until the other side accepts this fid, we can't mntclose it.
409
		 * Therefore set type to 0 for now; rootclose is known to be safe.
410
		 */
411
		nc->type = 0;
412
		alloc = 1;
413
	}
414
	wq->clone = nc;
415
 
416
	if(waserror()) {
417
		mntfree(r);
418
		nexterror();
419
	}
420
	r->request.type = Twalk;
421
	r->request.fid = c->fid;
422
	r->request.newfid = nc->fid;
423
	r->request.nwname = nname;
424
	memmove(r->request.wname, name, nname*sizeof(char*));
425
 
426
	mountrpc(m, r);
427
 
428
	if(r->reply.nwqid > nname)
429
		error("too many QIDs returned by walk");
430
	if(r->reply.nwqid < nname){
431
		if(alloc)
432
			cclose(nc);
433
		wq->clone = nil;
434
		if(r->reply.nwqid == 0){
435
			free(wq);
436
			wq = nil;
437
			goto Return;
438
		}
439
	}
440
 
441
	/* move new fid onto mnt device and update its qid */
442
	if(wq->clone != nil){
443
		if(wq->clone != c){
444
			wq->clone->type = c->type;
445
			wq->clone->mchan = c->mchan;
446
			incref(&c->mchan->ref);
447
		}
448
		if(r->reply.nwqid > 0)
449
			wq->clone->qid = r->reply.wqid[r->reply.nwqid-1];
450
	}
451
	wq->nqid = r->reply.nwqid;
452
	for(i=0; i<wq->nqid; i++)
453
		wq->qid[i] = r->reply.wqid[i];
454
 
455
    Return:
456
	poperror();
457
	mntfree(r);
458
	poperror();
459
	return wq;
460
}
461
 
462
static int
463
mntstat(Chan *c, uchar *dp, int n)
464
{
465
	Mnt *m;
466
	Mntrpc *r;
467
 
468
	if(n < BIT16SZ)
469
		error(Eshortstat);
470
	m = mntchk(c);
471
	r = mntralloc(c, m->msize);
472
	if(waserror()) {
473
		mntfree(r);
474
		nexterror();
475
	}
476
	r->request.type = Tstat;
477
	r->request.fid = c->fid;
478
	mountrpc(m, r);
479
 
480
/* r->reply.nstat is 16 bits
481
	if(r->reply.nstat >= 1<<16)
482
		error("returned stat buffer count too large");
483
*/
484
 
485
	if(r->reply.nstat > n){
486
		/*
487
		 * 12/31/2002 RSC
488
		 * 
489
		 * This should be nstat-2, which is the first two
490
		 * bytes of the stat buffer.  But dirstat and dirfstat
491
		 * depended on getting the full nstat (they didn't
492
		 * add BIT16SZ themselves).  I fixed dirstat and dirfstat
493
		 * but am leaving this unchanged for now.  After a
494
		 * few months, once enough of the relevant binaries
495
		 * have been recompiled for other reasons, we can
496
		 * change this to nstat-2.  Devstat gets this right
497
		 * (via convD2M).
498
		 */
499
		/* doesn't fit; just patch the count and return */
500
		PBIT16((uchar*)dp, r->reply.nstat);
501
		n = BIT16SZ;
502
	}else{
503
		n = r->reply.nstat;
504
		memmove(dp, r->reply.stat, n);
505
		validstat(dp, n);
506
		mntdirfix(dp, c);
507
	}
508
	poperror();
509
	mntfree(r);
510
	return n;
511
}
512
 
513
static Chan*
514
mntopencreate(int type, Chan *c, char *name, int omode, ulong perm)
515
{
516
	Mnt *m;
517
	Mntrpc *r;
518
 
519
	m = mntchk(c);
520
	r = mntralloc(c, m->msize);
521
	if(waserror()) {
522
		mntfree(r);
523
		nexterror();
524
	}
525
	r->request.type = type;
526
	r->request.fid = c->fid;
527
	r->request.mode = omode;
528
	if(type == Tcreate){
529
		r->request.perm = perm;
530
		r->request.name = name;
531
	}
532
	mountrpc(m, r);
533
 
534
	c->qid = r->reply.qid;
535
	c->offset = 0;
536
	c->mode = openmode(omode);
537
	c->iounit = r->reply.iounit;
538
	if(c->iounit == 0 || c->iounit > m->msize-IOHDRSZ)
539
		c->iounit = m->msize-IOHDRSZ;
540
	c->flag |= COPEN;
541
	poperror();
542
	mntfree(r);
543
 
544
	if(c->flag & CCACHE)
545
		copen(c);
546
 
547
	return c;
548
}
549
 
550
static Chan*
551
mntopen(Chan *c, int omode)
552
{
553
	return mntopencreate(Topen, c, nil, omode, 0);
554
}
555
 
556
static void
557
mntcreate(Chan *c, char *name, int omode, ulong perm)
558
{
559
	mntopencreate(Tcreate, c, name, omode, perm);
560
}
561
 
562
static void
563
mntclunk(Chan *c, int t)
564
{
565
	Mnt *m;
566
	Mntrpc *r;
567
 
568
	m = mntchk(c);
569
	r = mntralloc(c, m->msize);
570
	if(waserror()){
571
		mntfree(r);
572
		nexterror();
573
	}
574
 
575
	r->request.type = t;
576
	r->request.fid = c->fid;
577
	mountrpc(m, r);
578
	mntfree(r);
579
	poperror();
580
}
581
 
582
void
583
muxclose(Mnt *m)
584
{
585
	Mntrpc *q, *r;
586
 
587
	for(q = m->queue; q; q = r) {
588
		r = q->list;
589
		mntfree(q);
590
	}
591
	m->id = 0;
592
	free(m->version);
593
	m->version = nil;
594
	mntpntfree(m);
595
}
596
 
597
void
598
mntpntfree(Mnt *m)
599
{
600
	Mnt *f, **l;
601
	Queue *q;
602
 
603
	lock(&mntalloc.lk);
604
	l = &mntalloc.list;
605
	for(f = *l; f; f = f->list) {
606
		if(f == m) {
607
			*l = m->list;
608
			break;
609
		}
610
		l = &f->list;
611
	}
612
	m->list = mntalloc.mntfree;
613
	mntalloc.mntfree = m;
614
	q = m->q;
615
	unlock(&mntalloc.lk);
616
 
617
	qfree(q);
618
}
619
 
620
static void
621
mntclose(Chan *c)
622
{
623
	mntclunk(c, Tclunk);
624
}
625
 
626
static void
627
mntremove(Chan *c)
628
{
629
	mntclunk(c, Tremove);
630
}
631
 
632
static int
633
mntwstat(Chan *c, uchar *dp, int n)
634
{
635
	Mnt *m;
636
	Mntrpc *r;
637
 
638
	m = mntchk(c);
639
	r = mntralloc(c, m->msize);
640
	if(waserror()) {
641
		mntfree(r);
642
		nexterror();
643
	}
644
	r->request.type = Twstat;
645
	r->request.fid = c->fid;
646
	r->request.nstat = n;
647
	r->request.stat = dp;
648
	mountrpc(m, r);
649
	poperror();
650
	mntfree(r);
651
	return n;
652
}
653
 
654
static long
655
mntread(Chan *c, void *buf, long n, vlong off)
656
{
657
	uchar *p, *e;
658
	int nc, cache, isdir, dirlen;
659
 
660
	isdir = 0;
661
	cache = c->flag & CCACHE;
662
	if(c->qid.type & QTDIR) {
663
		cache = 0;
664
		isdir = 1;
665
	}
666
 
667
	p = buf;
668
	if(cache) {
669
		nc = cread(c, buf, n, off);
670
		if(nc > 0) {
671
			n -= nc;
672
			if(n == 0)
673
				return nc;
674
			p += nc;
675
			off += nc;
676
		}
677
		n = mntrdwr(Tread, c, p, n, off);
678
		cupdate(c, p, n, off);
679
		return n + nc;
680
	}
681
 
682
	n = mntrdwr(Tread, c, buf, n, off);
683
	if(isdir) {
684
		for(e = &p[n]; p+BIT16SZ < e; p += dirlen){
685
			dirlen = BIT16SZ+GBIT16(p);
686
			if(p+dirlen > e)
687
				break;
688
			validstat(p, dirlen);
689
			mntdirfix(p, c);
690
		}
691
		if(p != e)
692
			error(Esbadstat);
693
	}
694
	return n;
695
}
696
 
697
static long
698
mntwrite(Chan *c, void *buf, long n, vlong off)
699
{
700
	return mntrdwr(Twrite, c, buf, n, off);
701
}
702
 
703
long
704
mntrdwr(int type, Chan *c, void *buf, long n, vlong off)
705
{
706
	Mnt *m;
707
 	Mntrpc *r;
708
	char *uba;
709
	int cache;
710
	ulong cnt, nr, nreq;
711
 
712
	m = mntchk(c);
713
	uba = buf;
714
	cnt = 0;
715
	cache = c->flag & CCACHE;
716
	if(c->qid.type & QTDIR)
717
		cache = 0;
718
	for(;;) {
719
		r = mntralloc(c, m->msize);
720
		if(waserror()) {
721
			mntfree(r);
722
			nexterror();
723
		}
724
		r->request.type = type;
725
		r->request.fid = c->fid;
726
		r->request.offset = off;
727
		r->request.data = uba;
728
		nr = n;
729
		if(nr > m->msize-IOHDRSZ)
730
			nr = m->msize-IOHDRSZ;
731
		r->request.count = nr;
732
		mountrpc(m, r);
733
		nreq = r->request.count;
734
		nr = r->reply.count;
735
		if(nr > nreq)
736
			nr = nreq;
737
 
738
		if(type == Tread)
739
			r->b = bl2mem((uchar*)uba, r->b, nr);
740
		else if(cache)
741
			cwrite(c, (uchar*)uba, nr, off);
742
 
743
		poperror();
744
		mntfree(r);
745
		off += nr;
746
		uba += nr;
747
		cnt += nr;
748
		n -= nr;
749
		if(nr != nreq || n == 0)
750
			break;
751
	}
752
	return cnt;
753
}
754
 
755
void
756
mountrpc(Mnt *m, Mntrpc *r)
757
{
758
	char *sn, *cn;
759
	int t;
760
 
761
	r->reply.tag = 0;
762
	r->reply.type = Tmax;	/* can't ever be a valid message type */
763
 
764
	mountio(m, r);
765
 
766
	t = r->reply.type;
767
	switch(t) {
768
	case Rerror:
769
		error(r->reply.ename);
770
	case Rflush:
771
		error(Eintr);
772
	default:
773
		if(t == r->request.type+1)
774
			break;
775
		sn = "?";
776
		if(m->c->name != nil)
777
			sn = m->c->name->s;
778
		cn = "?";
779
		if(r->c != nil && r->c->name != nil)
780
			cn = r->c->name->s;
781
		print("mnt: proc %lud: mismatch from %s %s rep 0x%lux tag %d fid %d T%d R%d rp %d\n",
782
			up->pid, sn, cn,
783
			r, r->request.tag, r->request.fid, r->request.type,
784
			r->reply.type, r->reply.tag);
785
		error(Emountrpc);
786
	}
787
}
788
 
789
void
790
mountio(Mnt *m, Mntrpc *r)
791
{
792
	int n;
793
 
794
	while(waserror()) {
795
		if(m->rip == up)
796
			mntgate(m);
797
		if(strcmp(up->errstr, Eintr) != 0){
798
			mntflushfree(m, r);
799
			nexterror();
800
		}
801
		r = mntflushalloc(r, m->msize);
802
	}
803
 
804
	lock(&m->lk);
805
	r->m = m;
806
	r->list = m->queue;
807
	m->queue = r;
808
	unlock(&m->lk);
809
 
810
	/* Transmit a file system rpc */
811
	if(m->msize == 0)
812
		panic("msize");
813
	n = convS2M(&r->request, r->rpc, m->msize);
814
	if(n < 0)
815
		panic("bad message type in mountio");
816
	if(devtab[m->c->type]->write(m->c, r->rpc, n, 0) != n)
817
		error(Emountrpc);
818
	r->stime = fastticks(nil);
819
	r->reqlen = n;
820
 
821
	/* Gate readers onto the mount point one at a time */
822
	for(;;) {
823
		lock(&m->lk);
824
		if(m->rip == 0)
825
			break;
826
		unlock(&m->lk);
827
		sleep(&r->r, rpcattn, r);
828
		if(r->done){
829
			poperror();
830
			mntflushfree(m, r);
831
			return;
832
		}
833
	}
834
	m->rip = up;
835
	unlock(&m->lk);
836
	while(r->done == 0) {
837
		if(mntrpcread(m, r) < 0)
838
			error(Emountrpc);
839
		mountmux(m, r);
840
	}
841
	mntgate(m);
842
	poperror();
843
	mntflushfree(m, r);
844
}
845
 
846
static int
847
doread(Mnt *m, int len)
848
{
849
	Block *b;
850
 
851
	while(qlen(m->q) < len){
852
		b = devtab[m->c->type]->bread(m->c, m->msize, 0);
853
		if(b == nil)
854
			return -1;
855
		if(BLEN(b) == 0){
856
			freeblist(b);
857
			return -1;
858
		}
859
		qaddlist(m->q, b);
860
	}
861
	return 0;
862
}
863
 
864
int
865
mntrpcread(Mnt *m, Mntrpc *r)
866
{
867
	int i, t, len, hlen;
868
	Block *b, **l, *nb;
869
 
870
	r->reply.type = 0;
871
	r->reply.tag = 0;
872
 
873
	/* read at least length, type, and tag and pullup to a single block */
874
	if(doread(m, BIT32SZ+BIT8SZ+BIT16SZ) < 0)
875
		return -1;
876
	nb = pullupqueue(m->q, BIT32SZ+BIT8SZ+BIT16SZ);
877
 
878
	/* read in the rest of the message, avoid rediculous (for now) message sizes */
879
	len = GBIT32(nb->rp);
880
	if(len > m->msize){
881
		qdiscard(m->q, qlen(m->q));
882
		return -1;
883
	}
884
	if(doread(m, len) < 0)
885
		return -1;
886
 
887
	/* pullup the header (i.e. everything except data) */
888
	t = nb->rp[BIT32SZ];
889
	switch(t){
890
	case Rread:
891
		hlen = BIT32SZ+BIT8SZ+BIT16SZ+BIT32SZ;
892
		break;
893
	default:
894
		hlen = len;
895
		break;
896
	}
897
	nb = pullupqueue(m->q, hlen);
898
 
899
	if(convM2S(nb->rp, len, &r->reply) <= 0){
900
		/* bad message, dump it */
901
		print("mntrpcread: convM2S failed\n");
902
		qdiscard(m->q, len);
903
		return -1;
904
	}
905
 
906
	/* hang the data off of the fcall struct */
907
	l = &r->b;
908
	*l = nil;
909
	do {
910
		b = qremove(m->q);
911
		if(hlen > 0){
912
			b->rp += hlen;
913
			len -= hlen;
914
			hlen = 0;
915
		}
916
		i = BLEN(b);
917
		if(i <= len){
918
			len -= i;
919
			*l = b;
920
			l = &(b->next);
921
		} else {
922
			/* split block and put unused bit back */
923
			nb = allocb(i-len);
924
			memmove(nb->wp, b->rp+len, i-len);
925
			b->wp = b->rp+len;
926
			nb->wp += i-len;
927
			qputback(m->q, nb);
928
			*l = b;
929
			return 0;
930
		}
931
	}while(len > 0);
932
 
933
	return 0;
934
}
935
 
936
void
937
mntgate(Mnt *m)
938
{
939
	Mntrpc *q;
940
 
941
	lock(&m->lk);
942
	m->rip = 0;
943
	for(q = m->queue; q; q = q->list) {
944
		if(q->done == 0)
945
		if(wakeup(&q->r))
946
			break;
947
	}
948
	unlock(&m->lk);
949
}
950
 
951
void
952
mountmux(Mnt *m, Mntrpc *r)
953
{
954
	Mntrpc **l, *q;
955
 
956
	lock(&m->lk);
957
	l = &m->queue;
958
	for(q = *l; q; q = q->list) {
959
		/* look for a reply to a message */
960
		if(q->request.tag == r->reply.tag) {
961
			*l = q->list;
962
			if(q != r) {
963
				/*
964
				 * Completed someone else.
965
				 * Trade pointers to receive buffer.
966
				 */
967
				q->reply = r->reply;
968
				q->b = r->b;
969
				r->b = nil;
970
			}
971
			q->done = 1;
972
			unlock(&m->lk);
973
			if(mntstats != 0)
974
				(*mntstats)(q->request.type,
975
					m->c, q->stime,
976
					q->reqlen + r->replen);
977
			if(q != r)
978
				wakeup(&q->r);
979
			return;
980
		}
981
		l = &q->list;
982
	}
983
	unlock(&m->lk);
984
	print("unexpected reply tag %ud; type %d\n", r->reply.tag, r->reply.type);
985
}
986
 
987
/*
988
 * Create a new flush request and chain the previous
989
 * requests from it
990
 */
991
Mntrpc*
992
mntflushalloc(Mntrpc *r, ulong iounit)
993
{
994
	Mntrpc *fr;
995
 
996
	fr = mntralloc(0, iounit);
997
 
998
	fr->request.type = Tflush;
999
	if(r->request.type == Tflush)
1000
		fr->request.oldtag = r->request.oldtag;
1001
	else
1002
		fr->request.oldtag = r->request.tag;
1003
	fr->flushed = r;
1004
 
1005
	return fr;
1006
}
1007
 
1008
/*
1009
 *  Free a chain of flushes.  Remove each unanswered
1010
 *  flush and the original message from the unanswered
1011
 *  request queue.  Mark the original message as done
1012
 *  and if it hasn't been answered set the reply to to
1013
 *  Rflush.
1014
 */
1015
void
1016
mntflushfree(Mnt *m, Mntrpc *r)
1017
{
1018
	Mntrpc *fr;
1019
 
1020
	while(r){
1021
		fr = r->flushed;
1022
		if(!r->done){
1023
			r->reply.type = Rflush;
1024
			mntqrm(m, r);
1025
		}
1026
		if(fr)
1027
			mntfree(r);
1028
		r = fr;
1029
	}
1030
}
1031
 
1032
int
1033
alloctag(void)
1034
{
1035
	int i, j;
1036
	ulong v;
1037
 
1038
	for(i = 0; i < NMASK; i++){
1039
		v = mntalloc.tagmask[i];
1040
		if(v == ~0)
1041
			continue;
1042
		for(j = 0; j < 1<<TAGSHIFT; j++)
1043
			if((v & (1<<j)) == 0){
1044
				mntalloc.tagmask[i] |= 1<<j;
1045
				return (i<<TAGSHIFT) + j;
1046
			}
1047
	}
1048
	panic("no friggin tags left");
1049
	return NOTAG;
1050
}
1051
 
1052
void
1053
freetag(int t)
1054
{
1055
	mntalloc.tagmask[t>>TAGSHIFT] &= ~(1<<(t&TAGMASK));
1056
}
1057
 
1058
Mntrpc*
1059
mntralloc(Chan *c, ulong msize)
1060
{
1061
	Mntrpc *new;
1062
 
1063
	lock(&mntalloc.lk);
1064
	new = mntalloc.rpcfree;
1065
	if(new == nil){
1066
		new = malloc(sizeof(Mntrpc));
1067
		if(new == nil) {
1068
			unlock(&mntalloc.lk);
1069
			exhausted("mount rpc header");
1070
		}
1071
		/*
1072
		 * The header is split from the data buffer as
1073
		 * mountmux may swap the buffer with another header.
1074
		 */
1075
		new->rpc = mallocz(msize, 0);
1076
		if(new->rpc == nil){
1077
			free(new);
1078
			unlock(&mntalloc.lk);
1079
			exhausted("mount rpc buffer");
1080
		}
1081
		new->rpclen = msize;
1082
		new->request.tag = alloctag();
1083
	}
1084
	else {
1085
		mntalloc.rpcfree = new->list;
1086
		mntalloc.nrpcfree--;
1087
		if(new->rpclen < msize){
1088
			free(new->rpc);
1089
			new->rpc = mallocz(msize, 0);
1090
			if(new->rpc == nil){
1091
				free(new);
1092
				mntalloc.nrpcused--;
1093
				unlock(&mntalloc.lk);
1094
				exhausted("mount rpc buffer");
1095
			}
1096
			new->rpclen = msize;
1097
		}
1098
	}
1099
	mntalloc.nrpcused++;
1100
	unlock(&mntalloc.lk);
1101
	new->c = c;
1102
	new->done = 0;
1103
	new->flushed = nil;
1104
	new->b = nil;
1105
	return new;
1106
}
1107
 
1108
void
1109
mntfree(Mntrpc *r)
1110
{
1111
	if(r->b != nil)
1112
		freeblist(r->b);
1113
	lock(&mntalloc.lk);
1114
	if(mntalloc.nrpcfree >= 10){
1115
		free(r->rpc);
1116
		free(r);
1117
		freetag(r->request.tag);
1118
	}
1119
	else{
1120
		r->list = mntalloc.rpcfree;
1121
		mntalloc.rpcfree = r;
1122
		mntalloc.nrpcfree++;
1123
	}
1124
	mntalloc.nrpcused--;
1125
	unlock(&mntalloc.lk);
1126
}
1127
 
1128
void
1129
mntqrm(Mnt *m, Mntrpc *r)
1130
{
1131
	Mntrpc **l, *f;
1132
 
1133
	lock(&m->lk);
1134
	r->done = 1;
1135
 
1136
	l = &m->queue;
1137
	for(f = *l; f; f = f->list) {
1138
		if(f == r) {
1139
			*l = r->list;
1140
			break;
1141
		}
1142
		l = &f->list;
1143
	}
1144
	unlock(&m->lk);
1145
}
1146
 
1147
Mnt*
1148
mntchk(Chan *c)
1149
{
1150
	Mnt *m;
1151
 
1152
	/* This routine is mostly vestiges of prior lives; now it's just sanity checking */
1153
 
1154
	if(c->mchan == nil)
1155
		panic("mntchk 1: nil mchan c %s\n", c2name(c));
1156
 
1157
	m = c->mchan->mux;
1158
 
1159
	if(m == nil)
1160
		print("mntchk 2: nil mux c %s c->mchan %s \n", c2name(c), c2name(c->mchan));
1161
 
1162
	/*
1163
	 * Was it closed and reused (was error(Eshutdown); now, it can't happen)
1164
	 */
1165
	if(m->id == 0 || m->id >= c->dev)
1166
		panic("mntchk 3: can't happen");
1167
 
1168
	return m;
1169
}
1170
 
1171
/*
1172
 * Rewrite channel type and dev for in-flight data to
1173
 * reflect local values.  These entries are known to be
1174
 * the first two in the Dir encoding after the count.
1175
 */
1176
void
1177
mntdirfix(uchar *dirbuf, Chan *c)
1178
{
1179
	uint r;
1180
 
1181
	r = devtab[c->type]->dc;
1182
	dirbuf += BIT16SZ;	/* skip count */
1183
	PBIT16(dirbuf, r);
1184
	dirbuf += BIT16SZ;
1185
	PBIT32(dirbuf, c->dev);
1186
}
1187
 
1188
int
1189
rpcattn(void *v)
1190
{
1191
	Mntrpc *r;
1192
 
1193
	r = v;
1194
	return r->done || r->m->rip == 0;
1195
}
1196
 
1197
Dev mntdevtab = {
1198
	'M',
1199
	"mnt",
1200
 
1201
	mntreset,
1202
	devinit,
1203
	devshutdown,
1204
	mntattach,
1205
	mntwalk,
1206
	mntstat,
1207
	mntopen,
1208
	mntcreate,
1209
	mntclose,
1210
	mntread,
1211
	devbread,
1212
	mntwrite,
1213
	devbwrite,
1214
	mntremove,
1215
	mntwstat,
1216
};