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	"../port/lib.h"
3
#include	"mem.h"
4
#include	"dat.h"
5
#include	"fns.h"
6
#include	"../port/error.h"
7
 
8
/*
9
 * References are managed as follows:
10
 * The channel to the server - a network connection or pipe - has one
11
 * reference for every Chan open on the server.  The server channel has
12
 * c->mux set to the Mnt used for muxing control to that server.  Mnts
13
 * have no reference count; they go away when c goes away.
14
 * Each channel derived from the mount point has mchan set to c,
15
 * and increfs/decrefs mchan to manage references on the server
16
 * connection.
17
 */
18
 
19
#define MAXRPC (IOHDRSZ+8192)
20
 
21
struct Mntrpc
22
{
23
	Chan*	c;		/* Channel for whom we are working */
24
	Mntrpc*	list;		/* Free/pending list */
25
	Fcall	request;	/* Outgoing file system protocol message */
26
	Fcall 	reply;		/* Incoming reply */
27
	Mnt*	m;		/* Mount device during rpc */
28
	Rendez	r;		/* Place to hang out */
29
	uchar*	rpc;		/* I/O Data buffer */
30
	uint	rpclen;		/* len of buffer */
31
	Block	*b;		/* reply blocks */
32
	char	done;		/* Rpc completed */
33
	uvlong	stime;		/* start time for mnt statistics */
34
	ulong	reqlen;		/* request length for mnt statistics */
35
	ulong	replen;		/* reply length for mnt statistics */
36
	Mntrpc*	flushed;	/* message this one flushes */
37
};
38
 
39
enum
40
{
41
	TAGSHIFT = 5,			/* ulong has to be 32 bits */
42
	TAGMASK = (1<<TAGSHIFT)-1,
43
	NMASK = (64*1024)>>TAGSHIFT,
44
};
45
 
46
struct Mntalloc
47
{
48
	Lock;
49
	Mnt*	list;		/* Mount devices in use */
50
	Mnt*	mntfree;	/* Free list */
51
	Mntrpc*	rpcfree;
52
	int	nrpcfree;
53
	int	nrpcused;
54
	ulong	id;
55
	ulong	tagmask[NMASK];
56
}mntalloc;
57
 
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);
165
	oo = c->offset;
166
	c->offset += k;
167
	unlock(c);
168
 
169
	l = devtab[c->type]->write(c, msg, k, oo);
170
 
171
	if(l < k){
172
		lock(c);
173
		c->offset -= k - l;
174
		unlock(c);
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);
184
	c->offset += k;
185
	unlock(c);
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);
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);
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, nil, nil);
221
	m->msize = f.msize;
222
	unlock(&mntalloc);
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);
234
	m->queue = 0;
235
	m->rip = 0;
236
 
237
	c->flag |= CMSG;
238
	c->mux = m;
239
	m->c = c;
240
	unlock(m);
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);
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);
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);
373
	c->dev = mntalloc.id++;
374
	unlock(&mntalloc);
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
	nc->flag |= c->flag&CCACHE;
416
 
417
	if(waserror()) {
418
		mntfree(r);
419
		nexterror();
420
	}
421
	r->request.type = Twalk;
422
	r->request.fid = c->fid;
423
	r->request.newfid = nc->fid;
424
	r->request.nwname = nname;
425
	memmove(r->request.wname, name, nname*sizeof(char*));
426
 
427
	mountrpc(m, r);
428
 
429
	if(r->reply.nwqid > nname)
430
		error("too many QIDs returned by walk");
431
	if(r->reply.nwqid < nname){
432
		if(alloc)
433
			cclose(nc);
434
		wq->clone = nil;
435
		if(r->reply.nwqid == 0){
436
			free(wq);
437
			wq = nil;
438
			goto Return;
439
		}
440
	}
441
 
442
	/* move new fid onto mnt device and update its qid */
443
	if(wq->clone != nil){
444
		if(wq->clone != c){
445
			wq->clone->type = c->type;
446
			wq->clone->mchan = c->mchan;
447
			incref(c->mchan);
448
		}
449
		if(r->reply.nwqid > 0)
450
			wq->clone->qid = r->reply.wqid[r->reply.nwqid-1];
451
	}
452
	wq->nqid = r->reply.nwqid;
453
	for(i=0; i<wq->nqid; i++)
454
		wq->qid[i] = r->reply.wqid[i];
455
 
456
    Return:
457
	poperror();
458
	mntfree(r);
459
	poperror();
460
	return wq;
461
}
462
 
463
static int
464
mntstat(Chan *c, uchar *dp, int n)
465
{
466
	Mnt *m;
467
	Mntrpc *r;
468
 
469
	if(n < BIT16SZ)
470
		error(Eshortstat);
471
	m = mntchk(c);
472
	r = mntralloc(c, m->msize);
473
	if(waserror()) {
474
		mntfree(r);
475
		nexterror();
476
	}
477
	r->request.type = Tstat;
478
	r->request.fid = c->fid;
479
	mountrpc(m, r);
480
 
481
	if(r->reply.nstat > n){
482
		n = BIT16SZ;
483
		PBIT16((uchar*)dp, r->reply.nstat-2);
484
	}else{
485
		n = r->reply.nstat;
486
		memmove(dp, r->reply.stat, n);
487
		validstat(dp, n);
488
		mntdirfix(dp, c);
489
	}
490
	poperror();
491
	mntfree(r);
492
	return n;
493
}
494
 
495
static Chan*
496
mntopencreate(int type, Chan *c, char *name, int omode, ulong perm)
497
{
498
	Mnt *m;
499
	Mntrpc *r;
500
 
501
	m = mntchk(c);
502
	r = mntralloc(c, m->msize);
503
	if(waserror()) {
504
		mntfree(r);
505
		nexterror();
506
	}
507
	r->request.type = type;
508
	r->request.fid = c->fid;
509
	r->request.mode = omode;
510
	if(type == Tcreate){
511
		r->request.perm = perm;
512
		r->request.name = name;
513
	}
514
	mountrpc(m, r);
515
 
516
	c->qid = r->reply.qid;
517
	c->offset = 0;
518
	c->mode = openmode(omode);
519
	c->iounit = r->reply.iounit;
520
	if(c->iounit == 0 || c->iounit > m->msize-IOHDRSZ)
521
		c->iounit = m->msize-IOHDRSZ;
522
	c->flag |= COPEN;
523
	poperror();
524
	mntfree(r);
525
 
526
	if(c->flag & CCACHE)
527
		copen(c);
528
 
529
	return c;
530
}
531
 
532
static Chan*
533
mntopen(Chan *c, int omode)
534
{
535
	return mntopencreate(Topen, c, nil, omode, 0);
536
}
537
 
538
static void
539
mntcreate(Chan *c, char *name, int omode, ulong perm)
540
{
541
	mntopencreate(Tcreate, c, name, omode, perm);
542
}
543
 
544
static void
545
mntclunk(Chan *c, int t)
546
{
547
	Mnt *m;
548
	Mntrpc *r;
549
 
550
	m = mntchk(c);
551
	r = mntralloc(c, m->msize);
552
	if(waserror()){
553
		mntfree(r);
554
		nexterror();
555
	}
556
 
557
	r->request.type = t;
558
	r->request.fid = c->fid;
559
	mountrpc(m, r);
560
	mntfree(r);
561
	poperror();
562
}
563
 
564
void
565
muxclose(Mnt *m)
566
{
567
	Mntrpc *q, *r;
568
 
569
	for(q = m->queue; q; q = r) {
570
		r = q->list;
571
		mntfree(q);
572
	}
573
	m->id = 0;
574
	free(m->version);
575
	m->version = nil;
576
	mntpntfree(m);
577
}
578
 
579
void
580
mntpntfree(Mnt *m)
581
{
582
	Mnt *f, **l;
583
	Queue *q;
584
 
585
	lock(&mntalloc);
586
	l = &mntalloc.list;
587
	for(f = *l; f; f = f->list) {
588
		if(f == m) {
589
			*l = m->list;
590
			break;
591
		}
592
		l = &f->list;
593
	}
594
	m->list = mntalloc.mntfree;
595
	mntalloc.mntfree = m;
596
	q = m->q;
597
	unlock(&mntalloc);
598
 
599
	qfree(q);
600
}
601
 
602
static void
603
mntclose(Chan *c)
604
{
605
	mntclunk(c, Tclunk);
606
}
607
 
608
static void
609
mntremove(Chan *c)
610
{
611
	mntclunk(c, Tremove);
612
}
613
 
614
static int
615
mntwstat(Chan *c, uchar *dp, int n)
616
{
617
	Mnt *m;
618
	Mntrpc *r;
619
 
620
	m = mntchk(c);
621
	r = mntralloc(c, m->msize);
622
	if(waserror()) {
623
		mntfree(r);
624
		nexterror();
625
	}
626
	r->request.type = Twstat;
627
	r->request.fid = c->fid;
628
	r->request.nstat = n;
629
	r->request.stat = dp;
630
	mountrpc(m, r);
631
	poperror();
632
	mntfree(r);
633
	return n;
634
}
635
 
636
static long
637
mntread(Chan *c, void *buf, long n, vlong off)
638
{
639
	uchar *p, *e;
640
	int nc, cache, isdir, dirlen;
641
 
642
	isdir = 0;
643
	cache = c->flag & CCACHE;
644
	if(c->qid.type & QTDIR) {
645
		cache = 0;
646
		isdir = 1;
647
	}
648
 
649
	p = buf;
650
	if(cache) {
651
		nc = cread(c, buf, n, off);
652
		if(nc > 0) {
653
			n -= nc;
654
			if(n == 0)
655
				return nc;
656
			p += nc;
657
			off += nc;
658
		}
659
		n = mntrdwr(Tread, c, p, n, off);
660
		cupdate(c, p, n, off);
661
		return n + nc;
662
	}
663
 
664
	n = mntrdwr(Tread, c, buf, n, off);
665
	if(isdir) {
666
		for(e = &p[n]; p+BIT16SZ < e; p += dirlen){
667
			dirlen = BIT16SZ+GBIT16(p);
668
			if(p+dirlen > e)
669
				break;
670
			validstat(p, dirlen);
671
			mntdirfix(p, c);
672
		}
673
		if(p != e)
674
			error(Esbadstat);
675
	}
676
	return n;
677
}
678
 
679
static long
680
mntwrite(Chan *c, void *buf, long n, vlong off)
681
{
682
	return mntrdwr(Twrite, c, buf, n, off);
683
}
684
 
685
long
686
mntrdwr(int type, Chan *c, void *buf, long n, vlong off)
687
{
688
	Mnt *m;
689
 	Mntrpc *r;
690
	char *uba;
691
	int cache;
692
	ulong cnt, nr, nreq;
693
 
694
	m = mntchk(c);
695
	uba = buf;
696
	cnt = 0;
697
	cache = c->flag & CCACHE;
698
	if(c->qid.type & QTDIR)
699
		cache = 0;
700
	for(;;) {
701
		r = mntralloc(c, m->msize);
702
		if(waserror()) {
703
			mntfree(r);
704
			nexterror();
705
		}
706
		r->request.type = type;
707
		r->request.fid = c->fid;
708
		r->request.offset = off;
709
		r->request.data = uba;
710
		nr = n;
711
		if(nr > m->msize-IOHDRSZ)
712
			nr = m->msize-IOHDRSZ;
713
		r->request.count = nr;
714
		mountrpc(m, r);
715
		nreq = r->request.count;
716
		nr = r->reply.count;
717
		if(nr > nreq)
718
			nr = nreq;
719
 
720
		if(type == Tread)
721
			r->b = bl2mem((uchar*)uba, r->b, nr);
722
		else if(cache)
723
			cwrite(c, (uchar*)uba, nr, off);
724
 
725
		poperror();
726
		mntfree(r);
727
		off += nr;
728
		uba += nr;
729
		cnt += nr;
730
		n -= nr;
731
		if(nr != nreq || n == 0 || up->nnote)
732
			break;
733
	}
734
	return cnt;
735
}
736
 
737
void
738
mountrpc(Mnt *m, Mntrpc *r)
739
{
740
	char *sn, *cn;
741
	int t;
742
 
743
	r->reply.tag = 0;
744
	r->reply.type = Tmax;	/* can't ever be a valid message type */
745
 
746
	mountio(m, r);
747
 
748
	t = r->reply.type;
749
	switch(t) {
750
	case Rerror:
751
		error(r->reply.ename);
752
	case Rflush:
753
		error(Eintr);
754
	default:
755
		if(t == r->request.type+1)
756
			break;
757
		sn = "?";
758
		if(m->c->path != nil)
759
			sn = m->c->path->s;
760
		cn = "?";
761
		if(r->c != nil && r->c->path != nil)
762
			cn = r->c->path->s;
763
		print("mnt: proc %s %lud: mismatch from %s %s rep %#p tag %d fid %d T%d R%d rp %d\n",
764
			up->text, up->pid, sn, cn,
765
			r, r->request.tag, r->request.fid, r->request.type,
766
			r->reply.type, r->reply.tag);
767
		error(Emountrpc);
768
	}
769
}
770
 
771
void
772
mountio(Mnt *m, Mntrpc *r)
773
{
774
	int n;
775
 
776
	while(waserror()) {
777
		if(m->rip == up)
778
			mntgate(m);
779
		if(strcmp(up->errstr, Eintr) != 0){
780
			mntflushfree(m, r);
781
			nexterror();
782
		}
783
		r = mntflushalloc(r, m->msize);
784
	}
785
 
786
	lock(m);
787
	r->m = m;
788
	r->list = m->queue;
789
	m->queue = r;
790
	unlock(m);
791
 
792
	/* Transmit a file system rpc */
793
	if(m->msize == 0)
794
		panic("msize");
795
	n = convS2M(&r->request, r->rpc, m->msize);
796
	if(n < 0)
797
		panic("bad message type in mountio");
798
	if(devtab[m->c->type]->write(m->c, r->rpc, n, 0) != n)
799
		error(Emountrpc);
800
	r->stime = fastticks(nil);
801
	r->reqlen = n;
802
 
803
	/* Gate readers onto the mount point one at a time */
804
	for(;;) {
805
		lock(m);
806
		if(m->rip == 0)
807
			break;
808
		unlock(m);
809
		sleep(&r->r, rpcattn, r);
810
		if(r->done){
811
			poperror();
812
			mntflushfree(m, r);
813
			return;
814
		}
815
	}
816
	m->rip = up;
817
	unlock(m);
818
	while(r->done == 0) {
819
		if(mntrpcread(m, r) < 0)
820
			error(Emountrpc);
821
		mountmux(m, r);
822
	}
823
	mntgate(m);
824
	poperror();
825
	mntflushfree(m, r);
826
}
827
 
828
static int
829
doread(Mnt *m, int len)
830
{
831
	Block *b;
832
 
833
	while(qlen(m->q) < len){
834
		b = devtab[m->c->type]->bread(m->c, m->msize, 0);
835
		if(b == nil)
836
			return -1;
837
		if(blocklen(b) == 0){
838
			freeblist(b);
839
			return -1;
840
		}
841
		qaddlist(m->q, b);
842
	}
843
	return 0;
844
}
845
 
846
int
847
mntrpcread(Mnt *m, Mntrpc *r)
848
{
849
	int i, t, len, hlen;
850
	Block *b, **l, *nb;
851
 
852
	r->reply.type = 0;
853
	r->reply.tag = 0;
854
 
855
	/* read at least length, type, and tag and pullup to a single block */
856
	if(doread(m, BIT32SZ+BIT8SZ+BIT16SZ) < 0)
857
		return -1;
858
	nb = pullupqueue(m->q, BIT32SZ+BIT8SZ+BIT16SZ);
859
 
860
	/* read in the rest of the message, avoid ridiculous (for now) message sizes */
861
	len = GBIT32(nb->rp);
862
	if(len > m->msize){
863
		qdiscard(m->q, qlen(m->q));
864
		return -1;
865
	}
866
	if(doread(m, len) < 0)
867
		return -1;
868
 
869
	/* pullup the header (i.e. everything except data) */
870
	t = nb->rp[BIT32SZ];
871
	switch(t){
872
	case Rread:
873
		hlen = BIT32SZ+BIT8SZ+BIT16SZ+BIT32SZ;
874
		break;
875
	default:
876
		hlen = len;
877
		break;
878
	}
879
	nb = pullupqueue(m->q, hlen);
880
 
881
	if(convM2S(nb->rp, len, &r->reply) <= 0){
882
		/* bad message, dump it */
883
		print("mntrpcread: convM2S failed\n");
884
		qdiscard(m->q, len);
885
		return -1;
886
	}
887
 
888
	/* hang the data off of the fcall struct */
889
	l = &r->b;
890
	*l = nil;
891
	do {
892
		b = qremove(m->q);
893
		if(hlen > 0){
894
			b->rp += hlen;
895
			len -= hlen;
896
			hlen = 0;
897
		}
898
		i = BLEN(b);
899
		if(i <= len){
900
			len -= i;
901
			*l = b;
902
			l = &(b->next);
903
		} else {
904
			/* split block and put unused bit back */
905
			nb = allocb(i-len);
906
			memmove(nb->wp, b->rp+len, i-len);
907
			b->wp = b->rp+len;
908
			nb->wp += i-len;
909
			qputback(m->q, nb);
910
			*l = b;
911
			return 0;
912
		}
913
	}while(len > 0);
914
 
915
	return 0;
916
}
917
 
918
void
919
mntgate(Mnt *m)
920
{
921
	Mntrpc *q;
922
 
923
	lock(m);
924
	m->rip = 0;
925
	for(q = m->queue; q; q = q->list) {
926
		if(q->done == 0)
927
		if(wakeup(&q->r))
928
			break;
929
	}
930
	unlock(m);
931
}
932
 
933
void
934
mountmux(Mnt *m, Mntrpc *r)
935
{
936
	Mntrpc **l, *q;
937
 
938
	lock(m);
939
	l = &m->queue;
940
	for(q = *l; q; q = q->list) {
941
		/* look for a reply to a message */
942
		if(q->request.tag == r->reply.tag) {
943
			*l = q->list;
944
			if(q != r) {
945
				/*
946
				 * Completed someone else.
947
				 * Trade pointers to receive buffer.
948
				 */
949
				q->reply = r->reply;
950
				q->b = r->b;
951
				r->b = nil;
952
			}
953
			q->done = 1;
954
			unlock(m);
955
			if(mntstats != nil)
956
				(*mntstats)(q->request.type,
957
					m->c, q->stime,
958
					q->reqlen + r->replen);
959
			if(q != r)
960
				wakeup(&q->r);
961
			return;
962
		}
963
		l = &q->list;
964
	}
965
	unlock(m);
966
	print("unexpected reply tag %ud; type %d\n", r->reply.tag, r->reply.type);
967
}
968
 
969
/*
970
 * Create a new flush request and chain the previous
971
 * requests from it
972
 */
973
Mntrpc*
974
mntflushalloc(Mntrpc *r, ulong iounit)
975
{
976
	Mntrpc *fr;
977
 
978
	fr = mntralloc(0, iounit);
979
 
980
	fr->request.type = Tflush;
981
	if(r->request.type == Tflush)
982
		fr->request.oldtag = r->request.oldtag;
983
	else
984
		fr->request.oldtag = r->request.tag;
985
	fr->flushed = r;
986
 
987
	return fr;
988
}
989
 
990
/*
991
 *  Free a chain of flushes.  Remove each unanswered
992
 *  flush and the original message from the unanswered
993
 *  request queue.  Mark the original message as done
994
 *  and if it hasn't been answered set the reply to to
995
 *  Rflush.
996
 */
997
void
998
mntflushfree(Mnt *m, Mntrpc *r)
999
{
1000
	Mntrpc *fr;
1001
 
1002
	while(r){
1003
		fr = r->flushed;
1004
		if(!r->done){
1005
			r->reply.type = Rflush;
1006
			mntqrm(m, r);
1007
		}
1008
		if(fr)
1009
			mntfree(r);
1010
		r = fr;
1011
	}
1012
}
1013
 
1014
int
1015
alloctag(void)
1016
{
1017
	int i, j;
1018
	ulong v;
1019
 
1020
	for(i = 0; i < NMASK; i++){
1021
		v = mntalloc.tagmask[i];
1022
		if(v == ~0UL)
1023
			continue;
1024
		for(j = 0; j < 1<<TAGSHIFT; j++)
1025
			if((v & (1<<j)) == 0){
1026
				mntalloc.tagmask[i] |= 1<<j;
1027
				return (i<<TAGSHIFT) + j;
1028
			}
1029
	}
1030
	panic("no friggin tags left");
1031
	return NOTAG;
1032
}
1033
 
1034
void
1035
freetag(int t)
1036
{
1037
	mntalloc.tagmask[t>>TAGSHIFT] &= ~(1<<(t&TAGMASK));
1038
}
1039
 
1040
Mntrpc*
1041
mntralloc(Chan *c, ulong msize)
1042
{
1043
	Mntrpc *new;
1044
 
1045
	lock(&mntalloc);
1046
	new = mntalloc.rpcfree;
1047
	if(new == nil){
1048
		new = malloc(sizeof(Mntrpc));
1049
		if(new == nil) {
1050
			unlock(&mntalloc);
1051
			exhausted("mount rpc header");
1052
		}
1053
		/*
1054
		 * The header is split from the data buffer as
1055
		 * mountmux may swap the buffer with another header.
1056
		 */
1057
		new->rpc = mallocz(msize, 0);
1058
		if(new->rpc == nil){
1059
			free(new);
1060
			unlock(&mntalloc);
1061
			exhausted("mount rpc buffer");
1062
		}
1063
		new->rpclen = msize;
1064
		new->request.tag = alloctag();
1065
	}
1066
	else {
1067
		mntalloc.rpcfree = new->list;
1068
		mntalloc.nrpcfree--;
1069
		if(new->rpclen < msize){
1070
			free(new->rpc);
1071
			new->rpc = mallocz(msize, 0);
1072
			if(new->rpc == nil){
1073
				free(new);
1074
				mntalloc.nrpcused--;
1075
				unlock(&mntalloc);
1076
				exhausted("mount rpc buffer");
1077
			}
1078
			new->rpclen = msize;
1079
		}
1080
	}
1081
	mntalloc.nrpcused++;
1082
	unlock(&mntalloc);
1083
	new->c = c;
1084
	new->done = 0;
1085
	new->flushed = nil;
1086
	new->b = nil;
1087
	return new;
1088
}
1089
 
1090
void
1091
mntfree(Mntrpc *r)
1092
{
1093
	if(r->b != nil)
1094
		freeblist(r->b);
1095
	lock(&mntalloc);
1096
	if(mntalloc.nrpcfree >= 10){
1097
		free(r->rpc);
1098
		freetag(r->request.tag);
1099
		free(r);
1100
	}
1101
	else{
1102
		r->list = mntalloc.rpcfree;
1103
		mntalloc.rpcfree = r;
1104
		mntalloc.nrpcfree++;
1105
	}
1106
	mntalloc.nrpcused--;
1107
	unlock(&mntalloc);
1108
}
1109
 
1110
void
1111
mntqrm(Mnt *m, Mntrpc *r)
1112
{
1113
	Mntrpc **l, *f;
1114
 
1115
	lock(m);
1116
	r->done = 1;
1117
 
1118
	l = &m->queue;
1119
	for(f = *l; f; f = f->list) {
1120
		if(f == r) {
1121
			*l = r->list;
1122
			break;
1123
		}
1124
		l = &f->list;
1125
	}
1126
	unlock(m);
1127
}
1128
 
1129
Mnt*
1130
mntchk(Chan *c)
1131
{
1132
	Mnt *m;
1133
 
1134
	/* This routine is mostly vestiges of prior lives; now it's just sanity checking */
1135
 
1136
	if(c->mchan == nil)
1137
		panic("mntchk 1: nil mchan c %s\n", chanpath(c));
1138
 
1139
	m = c->mchan->mux;
1140
 
1141
	if(m == nil)
1142
		print("mntchk 2: nil mux c %s c->mchan %s \n", chanpath(c), chanpath(c->mchan));
1143
 
1144
	/*
1145
	 * Was it closed and reused (was error(Eshutdown); now, it cannot happen)
1146
	 */
1147
	if(m->id == 0 || m->id >= c->dev)
1148
		panic("mntchk 3: can't happen");
1149
 
1150
	return m;
1151
}
1152
 
1153
/*
1154
 * Rewrite channel type and dev for in-flight data to
1155
 * reflect local values.  These entries are known to be
1156
 * the first two in the Dir encoding after the count.
1157
 */
1158
void
1159
mntdirfix(uchar *dirbuf, Chan *c)
1160
{
1161
	uint r;
1162
 
1163
	r = devtab[c->type]->dc;
1164
	dirbuf += BIT16SZ;	/* skip count */
1165
	PBIT16(dirbuf, r);
1166
	dirbuf += BIT16SZ;
1167
	PBIT32(dirbuf, c->dev);
1168
}
1169
 
1170
int
1171
rpcattn(void *v)
1172
{
1173
	Mntrpc *r;
1174
 
1175
	r = v;
1176
	return r->done || r->m->rip == 0;
1177
}
1178
 
1179
Dev mntdevtab = {
1180
	'M',
1181
	"mnt",
1182
 
1183
	mntreset,
1184
	devinit,
1185
	devshutdown,
1186
	mntattach,
1187
	mntwalk,
1188
	mntstat,
1189
	mntopen,
1190
	mntcreate,
1191
	mntclose,
1192
	mntread,
1193
	devbread,
1194
	mntwrite,
1195
	devbwrite,
1196
	mntremove,
1197
	mntwstat,
1198
};