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