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	<fcall.h>
4
#include	"compat.h"
5
#include	"error.h"
6
 
7
typedef	struct Fid	Fid;
8
typedef	struct Export	Export;
9
typedef	struct Exq	Exq;
10
typedef	struct Exwork	Exwork;
11
 
12
enum
13
{
14
	Nfidhash	= 32,
15
	Maxfdata	= 8192,
16
	Maxrpc		= IOHDRSZ + Maxfdata,
17
};
18
 
19
struct Export
20
{
21
	Ref	r;
22
	Exq*	work;
23
	Lock	fidlock;
24
	Fid*	fid[Nfidhash];
25
	int	io;		/* fd to read/write */
26
	int	iounit;
27
	int	nroots;
28
	Chan	**roots;
29
};
30
 
31
struct Fid
32
{
33
	Fid*	next;
34
	Fid**	last;
35
	Chan*	chan;
36
	long	offset;
37
	int	fid;
38
	int	ref;		/* fcalls using the fid; locked by Export.Lock */
39
	int	attached;	/* fid attached or cloned but not clunked */
40
};
41
 
42
struct Exq
43
{
44
	Lock	lk;
45
	int	responding;	/* writing out reply message */
46
	int	noresponse;	/* don't respond to this one */
47
	Exq*	next;
48
	int	shut;		/* has been noted for shutdown */
49
	Export*	export;
50
	void*	slave;
51
	Fcall	rpc;
52
	uchar	buf[Maxrpc];
53
};
54
 
55
struct Exwork
56
{
57
	Lock	l;
58
 
59
	int	ref;
60
 
61
	int	nwaiters;	/* queue of slaves waiting for work */
62
	QLock	qwait;
63
	Rendez	rwait;
64
 
65
	Exq	*head;		/* work waiting for a slave */
66
	Exq	*tail;
67
};
68
 
69
Exwork exq;
70
 
71
static void	exshutdown(Export*);
72
static void	exflush(Export*, int, int);
73
static void	exslave(void*);
74
static void	exfree(Export*);
75
static void	exportproc(Export*);
76
 
77
static char*	Exattach(Export*, Fcall*, uchar*);
78
static char*	Exauth(Export*, Fcall*, uchar*);
79
static char*	Exclunk(Export*, Fcall*, uchar*);
80
static char*	Excreate(Export*, Fcall*, uchar*);
81
static char*	Exversion(Export*, Fcall*, uchar*);
82
static char*	Exopen(Export*, Fcall*, uchar*);
83
static char*	Exread(Export*, Fcall*, uchar*);
84
static char*	Exremove(Export*, Fcall*, uchar*);
85
static char*	Exsession(Export*, Fcall*, uchar*);
86
static char*	Exstat(Export*, Fcall*, uchar*);
87
static char*	Exwalk(Export*, Fcall*, uchar*);
88
static char*	Exwrite(Export*, Fcall*, uchar*);
89
static char*	Exwstat(Export*, Fcall*, uchar*);
90
 
91
static char	*(*fcalls[Tmax])(Export*, Fcall*, uchar*);
92
 
93
static char	Enofid[]   = "no such fid";
94
static char	Eseekdir[] = "can't seek on a directory";
95
static char	Ereaddir[] = "unaligned read of a directory";
96
static int	exdebug = 0;
97
 
98
int
99
sysexport(int fd, Chan **roots, int nroots)
100
{
101
	Export *fs;
102
 
103
	fs = smalloc(sizeof(Export));
104
	fs->r.ref = 1;
105
	fs->io = fd;
106
	fs->roots = roots;
107
	fs->nroots = nroots;
108
 
109
	exportproc(fs);
110
 
111
	return 0;
112
}
113
 
114
static void
115
exportinit(void)
116
{
117
	lock(&exq.l);
118
	exq.ref++;
119
	if(fcalls[Tversion] != nil){
120
		unlock(&exq.l);
121
		return;
122
	}
123
 
124
	fmtinstall('F', fcallfmt);
125
	fcalls[Tversion] = Exversion;
126
	fcalls[Tauth] = Exauth;
127
	fcalls[Tattach] = Exattach;
128
	fcalls[Twalk] = Exwalk;
129
	fcalls[Topen] = Exopen;
130
	fcalls[Tcreate] = Excreate;
131
	fcalls[Tread] = Exread;
132
	fcalls[Twrite] = Exwrite;
133
	fcalls[Tclunk] = Exclunk;
134
	fcalls[Tremove] = Exremove;
135
	fcalls[Tstat] = Exstat;
136
	fcalls[Twstat] = Exwstat;
137
	unlock(&exq.l);
138
}
139
 
140
static void
141
exportproc(Export *fs)
142
{
143
	Exq *q;
144
	int n, ed;
145
 
146
	exportinit();
147
	ed = errdepth(-1);
148
	for(;;){
149
		errdepth(ed);
150
		q = smalloc(sizeof(Exq));
151
 
152
		n = read9pmsg(fs->io, q->buf, Maxrpc);
153
		if(n <= 0 || convM2S(q->buf, n, &q->rpc) != n)
154
			goto bad;
155
 
156
		if(exdebug)
157
			print("export %d <- %F\n", getpid(), &q->rpc);
158
 
159
		if(q->rpc.type == Tflush){
160
			exflush(fs, q->rpc.tag, q->rpc.oldtag);
161
			free(q);
162
			continue;
163
		}
164
 
165
		q->export = fs;
166
		incref(&fs->r);
167
 
168
		lock(&exq.l);
169
		if(exq.head == nil)
170
			exq.head = q;
171
		else
172
			exq.tail->next = q;
173
		q->next = nil;
174
		exq.tail = q;
175
		n = exq.nwaiters;
176
		if(n)
177
			exq.nwaiters = n - 1;
178
		unlock(&exq.l);
179
		if(!n)
180
			kproc("exportfs", exslave, nil);
181
		rendwakeup(&exq.rwait);
182
	}
183
bad:
184
	free(q);
185
	if(exdebug)
186
		fprint(2, "export proc shutting down: %r\n");
187
	exshutdown(fs);
188
	exfree(fs);
189
}
190
 
191
static void
192
exflush(Export *fs, int flushtag, int tag)
193
{
194
	Exq *q, **last;
195
	Fcall fc;
196
	uchar buf[Maxrpc];
197
	int n;
198
 
199
	/* hasn't been started? */
200
	lock(&exq.l);
201
	last = &exq.head;
202
	for(q = exq.head; q != nil; q = q->next){
203
		if(q->export == fs && q->rpc.tag == tag){
204
			*last = q->next;
205
			unlock(&exq.l);
206
			exfree(fs);
207
			free(q);
208
			goto Respond;
209
		}
210
		last = &q->next;
211
	}
212
	unlock(&exq.l);
213
 
214
	/* in progress? */
215
	lock(&fs->r);
216
	for(q = fs->work; q != nil; q = q->next){
217
		if(q->rpc.tag == tag){
218
			lock(&q->lk);
219
			q->noresponse = 1;
220
			if(!q->responding)
221
				rendintr(q->slave);
222
			unlock(&q->lk);
223
			break;
224
		}
225
	}
226
	unlock(&fs->r);
227
 
228
Respond:
229
	fc.type = Rflush;
230
	fc.tag = flushtag;
231
 
232
	n = convS2M(&fc, buf, Maxrpc);
233
	if(n == 0)
234
		panic("convS2M error on write");
235
	if(write(fs->io, buf, n) != n)
236
		panic("mount write");
237
}
238
 
239
static void
240
exshutdown(Export *fs)
241
{
242
	Exq *q, **last;
243
 
244
	lock(&exq.l);
245
	last = &exq.head;
246
	for(q = exq.head; q != nil; q = *last){
247
		if(q->export == fs){
248
			*last = q->next;
249
			exfree(fs);
250
			free(q);
251
			continue;
252
		}
253
		last = &q->next;
254
	}
255
 
256
	/*
257
	 * cleanly shut down the slaves if this is the last fs around
258
	 */
259
	exq.ref--;
260
	if(!exq.ref)
261
		rendwakeup(&exq.rwait);
262
	unlock(&exq.l);
263
 
264
	/*
265
	 * kick any sleepers
266
	 */
267
	lock(&fs->r);
268
	for(q = fs->work; q != nil; q = q->next){
269
		lock(&q->lk);
270
		q->noresponse = 1;
271
		if(!q->responding)
272
			rendintr(q->slave);
273
		unlock(&q->lk);
274
	}
275
	unlock(&fs->r);
276
}
277
 
278
static void
279
exfree(Export *fs)
280
{
281
	Fid *f, *n;
282
	int i;
283
 
284
	if(decref(&fs->r) != 0)
285
		return;
286
	for(i = 0; i < Nfidhash; i++){
287
		for(f = fs->fid[i]; f != nil; f = n){
288
			if(f->chan != nil)
289
				cclose(f->chan);
290
			n = f->next;
291
			free(f);
292
		}
293
	}
294
	free(fs);
295
}
296
 
297
static int
298
exwork(void *)
299
{
300
	int work;
301
 
302
	lock(&exq.l);
303
	work = exq.head != nil || !exq.ref;
304
	unlock(&exq.l);
305
	return work;
306
}
307
 
308
static void
309
exslave(void *)
310
{
311
	Export *fs;
312
	Exq *q, *t, **last;
313
	char *volatile err;
314
	int n, ed;
315
 
316
	while(waserror())
317
		fprint(2, "exslave %d errored out of loop -- heading back in!\n", getpid());
318
	ed = errdepth(-1);
319
	for(;;){
320
		errdepth(ed);
321
		qlock(&exq.qwait);
322
		if(waserror()){
323
			qunlock(&exq.qwait);
324
			nexterror();
325
		}
326
		rendsleep(&exq.rwait, exwork, nil);
327
 
328
		lock(&exq.l);
329
		if(!exq.ref){
330
			unlock(&exq.l);
331
			poperror();
332
			qunlock(&exq.qwait);
333
			break;
334
		}
335
		q = exq.head;
336
		if(q == nil){
337
			unlock(&exq.l);
338
			poperror();
339
			qunlock(&exq.qwait);
340
			continue;
341
		}
342
		exq.head = q->next;
343
		if(exq.head == nil)
344
			exq.tail = nil;
345
		poperror();
346
		qunlock(&exq.qwait);
347
 
348
		/*
349
		 * put the job on the work queue before it's
350
		 * visible as off of the head queue, so it's always
351
		 * findable for flushes and shutdown
352
		 */
353
		q->slave = up;
354
		q->noresponse = 0;
355
		q->responding = 0;
356
		rendclearintr();
357
		fs = q->export;
358
		lock(&fs->r);
359
		q->next = fs->work;
360
		fs->work = q;
361
		unlock(&fs->r);
362
 
363
		unlock(&exq.l);
364
 
365
		if(exdebug > 1)
366
			print("exslave dispatch %d %F\n", getpid(), &q->rpc);
367
 
368
		if(waserror()){
369
			print("exslave err %r\n");
370
			err = up->error;
371
		}else{
372
			if(q->rpc.type >= Tmax || !fcalls[q->rpc.type])
373
				err = "bad fcall type";
374
			else
375
				err = (*fcalls[q->rpc.type])(fs, &q->rpc, &q->buf[IOHDRSZ]);
376
			poperror();
377
		}
378
 
379
		q->rpc.type++;
380
		if(err){
381
			q->rpc.type = Rerror;
382
			q->rpc.ename = err;
383
		}
384
		n = convS2M(&q->rpc, q->buf, Maxrpc);
385
 
386
		if(exdebug)
387
			print("exslave %d -> %F\n", getpid(), &q->rpc);
388
 
389
		lock(&q->lk);
390
		if(!q->noresponse){
391
			q->responding = 1;
392
			unlock(&q->lk);
393
			write(fs->io, q->buf, n);
394
		}else
395
			unlock(&q->lk);
396
 
397
		/*
398
		 * exflush might set noresponse at this point, but
399
		 * setting noresponse means don't send a response now;
400
		 * it's okay that we sent a response already.
401
		 */
402
		if(exdebug > 1)
403
			print("exslave %d written %d\n", getpid(), q->rpc.tag);
404
 
405
		lock(&fs->r);
406
		last = &fs->work;
407
		for(t = fs->work; t != nil; t = t->next){
408
			if(t == q){
409
				*last = q->next;
410
				break;
411
			}
412
			last = &t->next;
413
		}
414
		unlock(&fs->r);
415
 
416
		exfree(q->export);
417
		free(q);
418
 
419
		rendclearintr();
420
		lock(&exq.l);
421
		exq.nwaiters++;
422
		unlock(&exq.l);
423
	}
424
	if(exdebug)
425
		fprint(2, "export slaveshutting down\n");
426
	kexit();
427
}
428
 
429
Fid*
430
Exmkfid(Export *fs, int fid)
431
{
432
	ulong h;
433
	Fid *f, *nf;
434
 
435
	nf = mallocz(sizeof(Fid), 1);
436
	if(nf == nil)
437
		return nil;
438
	lock(&fs->fidlock);
439
	h = fid % Nfidhash;
440
	for(f = fs->fid[h]; f != nil; f = f->next){
441
		if(f->fid == fid){
442
			unlock(&fs->fidlock);
443
			free(nf);
444
			return nil;
445
		}
446
	}
447
 
448
	nf->next = fs->fid[h];
449
	if(nf->next != nil)
450
		nf->next->last = &nf->next;
451
	nf->last = &fs->fid[h];
452
	fs->fid[h] = nf;
453
 
454
	nf->fid = fid;
455
	nf->ref = 1;
456
	nf->attached = 1;
457
	nf->offset = 0;
458
	nf->chan = nil;
459
	unlock(&fs->fidlock);
460
	return nf;
461
}
462
 
463
Fid*
464
Exgetfid(Export *fs, int fid)
465
{
466
	Fid *f;
467
	ulong h;
468
 
469
	lock(&fs->fidlock);
470
	h = fid % Nfidhash;
471
	for(f = fs->fid[h]; f; f = f->next){
472
		if(f->fid == fid){
473
			if(f->attached == 0)
474
				break;
475
			f->ref++;
476
			unlock(&fs->fidlock);
477
			return f;
478
		}
479
	}
480
	unlock(&fs->fidlock);
481
	return nil;
482
}
483
 
484
void
485
Exputfid(Export *fs, Fid *f)
486
{
487
	lock(&fs->fidlock);
488
	f->ref--;
489
	if(f->ref == 0 && f->attached == 0){
490
		if(f->chan != nil)
491
			cclose(f->chan);
492
		f->chan = nil;
493
		*f->last = f->next;
494
		if(f->next != nil)
495
			f->next->last = f->last;
496
		unlock(&fs->fidlock);
497
		free(f);
498
		return;
499
	}
500
	unlock(&fs->fidlock);
501
}
502
 
503
static char*
504
Exversion(Export *fs, Fcall *rpc, uchar *)
505
{
506
	if(rpc->msize > Maxrpc)
507
		rpc->msize = Maxrpc;
508
	if(strncmp(rpc->version, "9P", 2) != 0){
509
		rpc->version = "unknown";
510
		return nil;
511
	}
512
 
513
	fs->iounit = rpc->msize - IOHDRSZ;
514
	rpc->version = "9P2000";
515
	return nil;
516
}
517
 
518
static char*
519
Exauth(Export *, Fcall *, uchar *)
520
{
521
	return "vnc: authentication not required";
522
}
523
 
524
static char*
525
Exattach(Export *fs, Fcall *rpc, uchar *)
526
{
527
	Fid *f;
528
	int w;
529
 
530
	w = 0;
531
	if(rpc->aname != nil)
532
		w = strtol(rpc->aname, nil, 10);
533
	if(w < 0 || w > fs->nroots)
534
		error(Ebadspec);
535
	f = Exmkfid(fs, rpc->fid);
536
	if(f == nil)
537
		return Einuse;
538
	if(waserror()){
539
		f->attached = 0;
540
		Exputfid(fs, f);
541
		return up->error;
542
	}
543
	f->chan = cclone(fs->roots[w]);
544
	poperror();
545
	rpc->qid = f->chan->qid;
546
	Exputfid(fs, f);
547
	return nil;
548
}
549
 
550
static char*
551
Exclunk(Export *fs, Fcall *rpc, uchar *)
552
{
553
	Fid *f;
554
 
555
	f = Exgetfid(fs, rpc->fid);
556
	if(f != nil){
557
		f->attached = 0;
558
		Exputfid(fs, f);
559
	}
560
	return nil;
561
}
562
 
563
static char*
564
Exwalk(Export *fs, Fcall *rpc, uchar *)
565
{
566
	Fid *volatile f, *volatile nf;
567
	Walkqid *wq;
568
	Chan *c;
569
	int i, nwname;
570
	int volatile isnew;
571
 
572
	f = Exgetfid(fs, rpc->fid);
573
	if(f == nil)
574
		return Enofid;
575
	nf = nil;
576
	if(waserror()){
577
		Exputfid(fs, f);
578
		if(nf != nil)
579
			Exputfid(fs, nf);
580
		return up->error;
581
	}
582
 
583
	/*
584
	 * optional clone, but don't attach it until the walk succeeds.
585
	 */
586
	if(rpc->fid != rpc->newfid){
587
		nf = Exmkfid(fs, rpc->newfid);
588
		if(nf == nil)
589
			error(Einuse);
590
		nf->attached = 0;
591
		isnew = 1;
592
	}else{
593
		nf = Exgetfid(fs, rpc->fid);
594
		isnew = 0;
595
	}
596
 
597
	/*
598
	 * let the device do the work
599
	 */
600
	c = f->chan;
601
	nwname = rpc->nwname;
602
	wq = (*devtab[c->type]->walk)(c, nf->chan, rpc->wname, nwname);
603
	if(wq == nil)
604
		error(Enonexist);
605
 
606
	poperror();
607
 
608
	/*
609
	 * copy qid array
610
	 */
611
	for(i = 0; i < wq->nqid; i++)
612
		rpc->wqid[i] = wq->qid[i];
613
	rpc->nwqid = wq->nqid;
614
 
615
	/*
616
	 * update the channel if everything walked correctly.
617
	 */
618
	if(isnew && wq->nqid == nwname){
619
		nf->chan = wq->clone;
620
		nf->attached = 1;
621
	}
622
 
623
	free(wq);
624
	Exputfid(fs, f);
625
	Exputfid(fs, nf);
626
	return nil;
627
}
628
 
629
static char*
630
Exopen(Export *fs, Fcall *rpc, uchar *)
631
{
632
	Fid *volatile f;
633
	Chan *c;
634
	int iou;
635
 
636
	f = Exgetfid(fs, rpc->fid);
637
	if(f == nil)
638
		return Enofid;
639
	if(waserror()){
640
		Exputfid(fs, f);
641
		return up->error;
642
	}
643
	c = f->chan;
644
	c = (*devtab[c->type]->open)(c, rpc->mode);
645
	poperror();
646
 
647
	f->chan = c;
648
	f->offset = 0;
649
	rpc->qid = f->chan->qid;
650
	iou = f->chan->iounit;
651
	if(iou > fs->iounit)
652
		iou = fs->iounit;
653
	rpc->iounit = iou;
654
	Exputfid(fs, f);
655
	return nil;
656
}
657
 
658
static char*
659
Excreate(Export *fs, Fcall *rpc, uchar *)
660
{
661
	Fid *f;
662
	Chan *c;
663
	int iou;
664
 
665
	f = Exgetfid(fs, rpc->fid);
666
	if(f == nil)
667
		return Enofid;
668
	if(waserror()){
669
		Exputfid(fs, f);
670
		return up->error;
671
	}
672
	c = f->chan;
673
	(*devtab[c->type]->create)(c, rpc->name, rpc->mode, rpc->perm);
674
	poperror();
675
 
676
	f->chan = c;
677
	rpc->qid = f->chan->qid;
678
	iou = f->chan->iounit;
679
	if(iou > fs->iounit)
680
		iou = fs->iounit;
681
	rpc->iounit = iou;
682
	Exputfid(fs, f);
683
	return nil;
684
}
685
 
686
static char*
687
Exread(Export *fs, Fcall *rpc, uchar *buf)
688
{
689
	Fid *f;
690
	Chan *c;
691
	long off;
692
 
693
	f = Exgetfid(fs, rpc->fid);
694
	if(f == nil)
695
		return Enofid;
696
 
697
	c = f->chan;
698
 
699
	if(waserror()){
700
		Exputfid(fs, f);
701
		return up->error;
702
	}
703
 
704
	rpc->data = (char*)buf;
705
	off = rpc->offset;
706
	c->offset = off;
707
	rpc->count = (*devtab[c->type]->read)(c, rpc->data, rpc->count, off);
708
	poperror();
709
	Exputfid(fs, f);
710
	return nil;
711
}
712
 
713
static char*
714
Exwrite(Export *fs, Fcall *rpc, uchar *)
715
{
716
	Fid *f;
717
	Chan *c;
718
 
719
	f = Exgetfid(fs, rpc->fid);
720
	if(f == nil)
721
		return Enofid;
722
	if(waserror()){
723
		Exputfid(fs, f);
724
		return up->error;
725
	}
726
	c = f->chan;
727
	if(c->qid.type & QTDIR)
728
		error(Eisdir);
729
	rpc->count = (*devtab[c->type]->write)(c, rpc->data, rpc->count, rpc->offset);
730
	poperror();
731
	Exputfid(fs, f);
732
	return nil;
733
}
734
 
735
static char*
736
Exstat(Export *fs, Fcall *rpc, uchar *buf)
737
{
738
	Fid *f;
739
	Chan *c;
740
 
741
	f = Exgetfid(fs, rpc->fid);
742
	if(f == nil)
743
		return Enofid;
744
	if(waserror()){
745
		Exputfid(fs, f);
746
		return up->error;
747
	}
748
	c = f->chan;
749
	rpc->stat = buf;
750
	rpc->nstat = (*devtab[c->type]->stat)(c, rpc->stat, Maxrpc);
751
	poperror();
752
	Exputfid(fs, f);
753
	return nil;
754
}
755
 
756
static char*
757
Exwstat(Export *fs, Fcall *rpc, uchar *)
758
{
759
	Fid *f;
760
	Chan *c;
761
 
762
	f = Exgetfid(fs, rpc->fid);
763
	if(f == nil)
764
		return Enofid;
765
	if(waserror()){
766
		Exputfid(fs, f);
767
		return up->error;
768
	}
769
	c = f->chan;
770
	(*devtab[c->type]->wstat)(c, rpc->stat, rpc->nstat);
771
	poperror();
772
	Exputfid(fs, f);
773
	return nil;
774
}
775
 
776
static char*
777
Exremove(Export *fs, Fcall *rpc, uchar *)
778
{
779
	Fid *f;
780
	Chan *c;
781
 
782
	f = Exgetfid(fs, rpc->fid);
783
	if(f == nil)
784
		return Enofid;
785
	if(waserror()){
786
		Exputfid(fs, f);
787
		return up->error;
788
	}
789
	c = f->chan;
790
	(*devtab[c->type]->remove)(c);
791
	poperror();
792
 
793
	/*
794
	 * chan is already clunked by remove.
795
	 * however, we need to recover the chan,
796
	 * and follow sysremove's lead in making to point to root.
797
	 */
798
	c->type = 0;
799
 
800
	f->attached = 0;
801
	Exputfid(fs, f);
802
	return nil;
803
}