xref: /inferno-os/os/port/exportfs.c (revision cd17ce433410c01516d7ba8f052a6c5b67b0e2d5)
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 #include	"kernel.h"
8 
9 typedef	struct Fid	Fid;
10 typedef	struct Export	Export;
11 typedef	struct Exq	Exq;
12 typedef	struct Uqid Uqid;
13 
14 enum
15 {
16 	Nfidhash	= 32,
17 	Nqidhash = 32,
18 	QIDMASK = ((vlong)1<<48)-1,
19 	MAXFDATA	= 8192,
20 	MAXRPCDEF		= IOHDRSZ+MAXFDATA,	/* initial/default */
21 	MAXRPCMAX	= IOHDRSZ+64*1024,	/* most every allowed */
22 	MSGHDRSZ	= BIT32SZ+BIT8SZ+BIT16SZ
23 };
24 
25 struct Export
26 {
27 	Lock;
28 	Ref	r;
29 	Exq*	work;
30 	Lock	fidlock;
31 	Fid*	fid[Nfidhash];
32 	QLock	qidlock;
33 	Uqid*	qids[Nqidhash];
34 	ulong	pathgen;
35 	Chan*	io;
36 	Chan*	root;
37 	Pgrp*	pgrp;
38 	Egrp*	egrp;
39 	Fgrp*	fgrp;
40 	int	async;
41 	int	readonly;
42 	int	msize;
43 	char*	user;
44 };
45 
46 struct Fid
47 {
48 	Fid*	next;
49 	Fid**	last;
50 	Chan*	chan;
51 	int	fid;
52 	int	ref;		/* fcalls using the fid; locked by Export.Lock */
53 	vlong	offset;	/* last offset used (within directory) */
54 	int	attached;	/* fid attached or cloned but not clunked */
55 	Uqid*	qid;	/* generated qid */
56 };
57 
58 struct Uqid
59 {
60 	Ref;
61 	int	type;
62 	int	dev;
63 	vlong	oldpath;
64 	vlong	newpath;
65 	Uqid*	next;
66 };
67 
68 struct Exq
69 {
70 	Lock;
71 	int	busy;	/* fcall in progress */
72 	int	finished;	/* will do no more work on this request or flushes */
73 	Exq*	next;
74 	int	shut;		/* has been noted for shutdown */
75 	Exq*	flush;	/* queued flush requests */
76 	Exq*	flusht;	/* tail of flush queue */
77 	Export*	export;
78 	Proc*	slave;
79 	Fcall	in, out;
80 	uchar*	buf;
81 	int	bsize;
82 };
83 
84 struct
85 {
86 	Lock	l;
87 	QLock	qwait;
88 	Rendez	rwait;
89 	Exq	*head;		/* work waiting for a slave */
90 	Exq	*tail;
91 }exq;
92 
93 static void	exshutdown(Export*);
94 static int	exflushed(Export*, Exq*);
95 static void	exslave(void*);
96 static void	exfree(Export*);
97 static void	exfreeq(Exq*);
98 static void	exportproc(void*);
99 static void	exreply(Exq*, char*);
100 static int	exisroot(Export*, Chan*);
101 static Uqid*	uqidalloc(Export*, Chan*);
102 static void		freeuqid(Export*, Uqid*);
103 
104 static char*	Exversion(Export*, Fcall*, Fcall*);
105 static char*	Exauth(Export*, Fcall*, Fcall*);
106 static char*	Exattach(Export*, Fcall*, Fcall*);
107 static char*	Exclunk(Export*, Fcall*, Fcall*);
108 static char*	Excreate(Export*, Fcall*, Fcall*);
109 static char*	Exopen(Export*, Fcall*, Fcall*);
110 static char*	Exread(Export*, Fcall*, Fcall*);
111 static char*	Exremove(Export*, Fcall*, Fcall*);
112 static char*	Exstat(Export*, Fcall*, Fcall*);
113 static char*	Exwalk(Export*, Fcall*, Fcall*);
114 static char*	Exwrite(Export*, Fcall*, Fcall*);
115 static char*	Exwstat(Export*, Fcall*, Fcall*);
116 
117 static char	*(*fcalls[Tmax])(Export*, Fcall*, Fcall*);
118 
119 static char	Enofid[]   = "no such fid";
120 static char	Eseekdir[] = "can't seek on a directory";
121 static char	Eopen[]	= "walk of open fid";
122 static char	Emode[] = "open/create -- unknown mode";
123 static char	Edupfid[]	= "fid in use";
124 static char	Eaccess[] = "read/write -- not open in suitable mode";
125 static char	Ecount[] = "read/write -- count too big";
126 int	exdebug = 0;
127 
128 int
129 export(int fd, char *dir, int async)
130 {
131 	Chan *c, *dc;
132 	Pgrp *pg;
133 	Egrp *eg;
134 	Export *fs;
135 
136 	if(waserror())
137 		return -1;
138 	c = fdtochan(up->env->fgrp, fd, ORDWR, 1, 1);
139 	poperror();
140 
141 	if(waserror()){
142 		cclose(c);
143 		return -1;
144 	}
145 	dc = namec(dir, Atodir, 0, 0);
146 	poperror();
147 
148 	fs = malloc(sizeof(Export));
149 	if(fs == nil){
150 		cclose(c);
151 		cclose(dc);
152 		error(Enomem);
153 	}
154 
155 	fs->r.ref = 1;
156 	pg = up->env->pgrp;
157 	fs->pgrp = pg;
158 	incref(pg);
159 	eg = up->env->egrp;
160 	fs->egrp = eg;
161 	if(eg != nil)
162 		incref(eg);
163 	fs->fgrp = newfgrp(nil);
164 	kstrdup(&fs->user, up->env->user);
165 	fs->root = dc;
166 	fs->io = c;
167 	fs->pathgen = 0;
168 	fs->msize = 0;
169 	c->flag |= CMSG;
170 	fs->async = async;
171 
172 	if(async){
173 		if(waserror())
174 			return -1;
175 		kproc("exportfs", exportproc, fs, 0);
176 		poperror();
177 	}else
178 		exportproc(fs);
179 
180 	return 0;
181 }
182 
183 static void
184 exportinit(void)
185 {
186 	lock(&exq.l);
187 	if(fcalls[Tversion] != nil) {
188 		unlock(&exq.l);
189 		return;
190 	}
191 	fcalls[Tversion] = Exversion;
192 	fcalls[Tauth] = Exauth;
193 	fcalls[Tattach] = Exattach;
194 	fcalls[Twalk] = Exwalk;
195 	fcalls[Topen] = Exopen;
196 	fcalls[Tcreate] = Excreate;
197 	fcalls[Tread] = Exread;
198 	fcalls[Twrite] = Exwrite;
199 	fcalls[Tclunk] = Exclunk;
200 	fcalls[Tremove] = Exremove;
201 	fcalls[Tstat] = Exstat;
202 	fcalls[Twstat] = Exwstat;
203 	unlock(&exq.l);
204 }
205 
206 static int
207 exisroot(Export *fs, Chan *c)
208 {
209 	return eqchan(fs->root, c, 1);
210 }
211 
212 static int
213 exreadn(Chan *c, void *buf, int n)
214 {
215 	int nr, t;
216 
217 	if(waserror())
218 		return -1;
219 	for(nr = 0; nr < n;){
220 		t = devtab[c->type]->read(c, (char*)buf+nr, n-nr, 0);
221 		if(t <= 0)
222 			break;
223 		nr += t;
224 	}
225 	poperror();
226 	return nr;
227 }
228 
229 static int
230 exreadmsg(Chan *c, void *a, uint n)
231 {
232 	int m, len;
233 	uchar *buf;
234 
235 	buf = a;
236 	m = exreadn(c, buf, BIT32SZ);
237 	if(m < BIT32SZ){
238 		if(m < 0)
239 			return -1;
240 		return 0;
241 	}
242 	len = GBIT32(buf);
243 	if(len <= BIT32SZ || len > n){
244 		kwerrstr("bad length in Styx message header");
245 		return -1;
246 	}
247 	len -= BIT32SZ;
248 	m = exreadn(c, buf+BIT32SZ, len);
249 	if(m < len){
250 		if(m < 0)
251 			return -1;
252 		return 0;
253 	}
254 	return BIT32SZ+m;
255 }
256 
257 static void
258 exportproc(void *a)
259 {
260 	Exq *q;
261 	int async, msize;
262 	int n, type;
263 	Export *fs = a;
264 
265 	exportinit();
266 
267 	for(;;){
268 
269 		msize = fs->msize;
270 		if(msize == 0)
271 			msize = MAXRPCDEF;
272 		for(n=0;; n++){	/* we don't use smalloc, to avoid memset */
273 			q = mallocz(sizeof(*q)+msize, 0);
274 			if(q != nil || n > 6000)
275 				break;
276 			if(n%600 == 0)
277 				print("exportproc %ld: waiting for memory (%d) for request\n", up->pid, msize);
278 			tsleep(&up->sleep, return0, nil, 100);
279 		}
280 		if(q == nil){
281 			kwerrstr("out of memory: read request");
282 			n = -1;
283 			break;
284 		}
285 		memset(q, 0, sizeof(*q));
286 		q->buf = (uchar*)q + sizeof(*q);
287 		q->bsize = msize;
288 
289 		n = exreadmsg(fs->io, q->buf, msize);	/* TO DO: avoid copy */
290 		if(n <= 0)
291 			break;
292 		if(convM2S(q->buf, n, &q->in) != n){
293 			kwerrstr("bad T-message");
294 			n = -1;
295 			break;
296 		}
297 		type = q->in.type;
298 		if(type < Tversion || type >= Tmax || type&1 || type == Terror){
299 			kwerrstr("invalid T-message type %d", type);
300 			n = -1;
301 			break;
302 		}
303 
304 		if(exdebug)
305 			print("export %ld <- %F\n", up->pid, &q->in);
306 
307 		q->out.type = type+1;
308 		q->out.tag = q->in.tag;
309 
310 		q->export = fs;
311 		incref(&fs->r);
312 
313 		if(fs->readonly){
314 			switch(type){
315 			case Topen:
316 				if((q->in.mode & (ORCLOSE|OTRUNC|3)) == OREAD)
317 					break;
318 				/* FALL THROUGH */
319 			case Tcreate:
320 			case Twrite:
321 			case Tremove:
322 			case Twstat:
323 				q->out.type = Rerror;
324 				q->out.ename = "file system read only";
325 				exreply(q, "exportproc");
326 				exfreeq(q);
327 				continue;
328 			}
329 		}
330 
331 		if(q->in.type == Tflush){
332 			if(exflushed(fs, q)){
333 				/* not yet started or not found (flush arrived after reply); we reply */
334 				if(exdebug)
335 					print("export: flush %d\n", q->in.oldtag);
336 				exreply(q, "exportproc");
337 				exfreeq(q);
338 			}
339 			continue;
340 		}
341 
342 		lock(&exq.l);
343 		if(exq.head == nil)
344 			exq.head = q;
345 		else
346 			exq.tail->next = q;
347 		q->next = nil;
348 		exq.tail = q;
349 		unlock(&exq.l);
350 		if(exq.qwait.head == nil)
351 			kproc("exslave", exslave, nil, 0);
352 		wakeup(&exq.rwait);
353 	}
354 
355 	if(exdebug){
356 		if(n < 0)
357 			print("exportproc %ld shut down: %s\n", up->pid, up->env->errstr);
358 		else
359 			print("exportproc %ld shut down\n", up->pid);
360 	}
361 
362 	free(q);
363 	exshutdown(fs);
364 	async = fs->async;
365 	exfree(fs);
366 
367 	if(async)
368 		pexit("mount shut down", 0);
369 }
370 
371 static int
372 exflushed(Export *fs, Exq *fq)
373 {
374 	Exq *q, **last;
375 	ulong pid;
376 
377 	/* not yet started? */
378 	lock(&exq.l);
379 	for(last = &exq.head; (q = *last) != nil; last = &q->next)
380 		if(q->export == fs && q->in.tag == fq->in.oldtag){
381 			*last = q->next;
382 			unlock(&exq.l);
383 			/* not yet started: discard, and Rflush */
384 			exfreeq(q);
385 			return 1;
386 		}
387 	unlock(&exq.l);
388 
389 	/* tricky case: in progress */
390 	lock(fs);
391 	for(q = fs->work; q != nil; q = q->next)
392 		if(q->in.tag == fq->in.oldtag){
393 			pid = 0;
394 			lock(q);
395 			if(q->finished){
396 				/* slave replied and emptied its flush queue; we can Rflush now */
397 				unlock(q);
398 				return 1;
399 			}
400 			/* append to slave's flush queue */
401 			fq->next = nil;
402 			if(q->flush != nil)
403 				q->flusht->next = fq;
404 			else
405 				q->flush = fq;
406 			q->flusht = fq;
407 			if(q->busy){
408 				pid = q->slave->pid;
409 				swiproc(q->slave, 0);
410 			}
411 			unlock(q);
412 			unlock(fs);
413 			if(exdebug && pid)
414 				print("export: swiproc %ld to flush %d\n", pid, fq->in.oldtag);
415 			return 0;
416 		}
417 	unlock(fs);
418 
419 	/* not found */
420 	return 1;
421 }
422 
423 static void
424 exfreeq(Exq *q)
425 {
426 	Exq *fq;
427 
428 	while((fq = q->flush) != nil){
429 		q->flush = fq->next;
430 		exfree(fq->export);
431 		free(fq);
432 	}
433 	exfree(q->export);
434 	free(q);
435 }
436 
437 static void
438 exshutdown(Export *fs)
439 {
440 	Exq *q, **last;
441 
442 	/* work not started */
443 	lock(&exq.l);
444 	for(last = &exq.head; (q = *last) != nil;)
445 		if(q->export == fs){
446 			*last = q->next;
447 			exfreeq(q);
448 		}else
449 			last = &q->next;
450 	unlock(&exq.l);
451 
452 	/* tell slaves to abandon work in progress */
453 	lock(fs);
454 	while((q = fs->work) != nil){
455 		fs->work = q->next;
456 		lock(q);
457 		q->shut = 1;
458 		swiproc(q->slave, 0);	/* whether busy or not */
459 		unlock(q);
460 	}
461 	unlock(fs);
462 }
463 
464 static void
465 exfreefids(Export *fs)
466 {
467 	Fid *f, *n;
468 	int i;
469 
470 	for(i = 0; i < Nfidhash; i++){
471 		for(f = fs->fid[i]; f != nil; f = n){
472 			n = f->next;
473 			f->attached = 0;
474 			if(f->ref == 0) {
475 				if(f->chan != nil)
476 					cclose(f->chan);
477 				freeuqid(fs, f->qid);
478 				free(f);
479 			} else
480 				print("exfreefids: busy fid\n");
481 		}
482 	}
483 }
484 
485 static void
486 exfree(Export *fs)
487 {
488 	if(exdebug)
489 		print("export p/s %ld free %p ref %ld\n", up->pid, fs, fs->r.ref);
490 	if(decref(&fs->r) != 0)
491 		return;
492 	closepgrp(fs->pgrp);
493 	closeegrp(fs->egrp);
494 	closefgrp(fs->fgrp);
495 	cclose(fs->root);
496 	cclose(fs->io);
497 	exfreefids(fs);
498 	free(fs->user);
499 	free(fs);
500 }
501 
502 static int
503 exwork(void*)
504 {
505 	return exq.head != nil;
506 }
507 
508 static void
509 exslave(void*)
510 {
511 	Export *fs;
512 	Exq *q, *t, *fq, **last;
513 	char *err;
514 	int nstat;
515 
516 	for(;;){
517 		qlock(&exq.qwait);
518 		if(waserror()){
519 			qunlock(&exq.qwait);
520 			continue;
521 		}
522 		sleep(&exq.rwait, exwork, nil);
523 		poperror();
524 
525 		lock(&exq.l);
526 		q = exq.head;
527 		if(q == nil) {
528 			unlock(&exq.l);
529 			qunlock(&exq.qwait);
530 			continue;
531 		}
532 		exq.head = q->next;
533 
534 		qunlock(&exq.qwait);
535 
536 		/*
537 		 * put the job on the work queue before it's
538 		 * visible as off of the head queue, so it's always
539 		 * findable for flushes and shutdown
540 		 */
541 		notkilled();
542 		q->slave = up;
543 		q->busy = 1;	/* fcall in progress: interruptible */
544 		fs = q->export;
545 		lock(fs);
546 		q->next = fs->work;
547 		fs->work = q;
548 		unlock(fs);
549 		unlock(&exq.l);
550 
551 		up->env->pgrp = q->export->pgrp;
552 		up->env->egrp = q->export->egrp;
553 		up->env->fgrp = q->export->fgrp;
554 		kstrdup(&up->env->user, q->export->user);
555 
556 		if(exdebug > 1)
557 			print("exslave %ld dispatch %F\n", up->pid, &q->in);
558 
559 		if(waserror()){
560 			print("exslave %ld err %s\n", up->pid, up->env->errstr);	/* shouldn't happen */
561 			err = up->env->errstr;
562 		}else{
563 			if(q->in.type >= Tmax || !fcalls[q->in.type]){
564 				snprint(up->genbuf, sizeof(up->genbuf), "unknown message: %F", &q->in);
565 				err = up->genbuf;
566 			}else{
567 				switch(q->in.type){
568 				case Tread:
569 					q->out.data = (char*)q->buf + IOHDRSZ;
570 					break;
571 				case Tstat:
572 					q->out.stat = q->buf + MSGHDRSZ + BIT16SZ;	/* leaves it just where we want it */
573 					nstat = q->bsize;
574 					if(nstat > STATMAX)
575 						nstat = STATMAX;
576 					nstat -= MSGHDRSZ+BIT16SZ;
577 					q->out.nstat = nstat;
578 					break;
579 				}
580 				err = (*fcalls[q->in.type])(fs, &q->in, &q->out);
581 			}
582 			poperror();
583 		}
584 
585 		/*
586 		 * if the fcall completed without error we must reply,
587 		 * even if a flush is pending (because the underlying server
588 		 * might have changed state), unless the export has shut down completely.
589 		 * must also reply to each flush in order, and only after the original reply (if sent).
590 		 */
591 		lock(q);
592 		notkilled();
593 		q->busy = 0;	/* operation complete */
594 		if(!q->shut){
595 			if(q->flush == nil || err == nil){
596 				unlock(q);
597 				q->out.type = q->in.type+1;
598 				q->out.tag = q->in.tag;
599 				if(err){
600 					q->out.type = Rerror;
601 					q->out.ename = err;
602 				}
603 				exreply(q, "exslave");
604 				lock(q);
605 			}
606 			while((fq = q->flush) != nil && !q->shut){
607 				q->flush = fq->next;
608 				unlock(q);
609 				exreply(fq, "exslave");
610 				exfreeq(fq);
611 				lock(q);
612 			}
613 		}
614 		q->finished = 1;	/* promise not to send any more */
615 		unlock(q);
616 
617 		lock(fs);
618 		for(last = &fs->work; (t = *last) != nil; last = &t->next)
619 			if(t == q){
620 				*last = q->next;
621 				break;
622 			}
623 		unlock(fs);
624 
625 		notkilled();
626 		exfreeq(q);
627 	}
628 	print("exslave %ld shut down", up->pid);	/* not reached */
629 	pexit("exslave shut down", 0);
630 }
631 
632 static void
633 exreply(Exq *q, char *who)
634 {
635 	Export *fs;
636 	Fcall *r;
637 	int n;
638 
639 	fs = q->export;
640 	r = &q->out;
641 
642 	n = convS2M(r, q->buf, q->bsize);
643 	if(n == 0){
644 		r->type = Rerror;
645 		if(fs->msize == 0)
646 			r->ename = "Tversion not seen";
647 		else
648 			r->ename = "failed to convert R-message";
649 		n = convS2M(r, q->buf, q->bsize);
650 	}
651 
652 	if(exdebug)
653 		print("%s %ld -> %F\n", who, up->pid, r);
654 
655 	if(!waserror()){
656 		devtab[fs->io->type]->write(fs->io, q->buf, n, 0);
657 		poperror();
658 	}
659 }
660 
661 static int
662 exiounit(Export *fs, Chan *c)
663 {
664 	int iounit;
665 
666 	iounit = fs->msize-IOHDRSZ;
667 	if(c->iounit != 0 && c->iounit < fs->msize)
668 		iounit = c->iounit;
669 	return iounit;
670 }
671 
672 static Qid
673 Exrmtqid(Chan *c, Uqid *qid)
674 {
675 	Qid q;
676 
677 	q.path = qid->newpath;
678 	q.vers = c->qid.vers;
679 	q.type = c->qid.type;
680 	return q;
681 }
682 
683 static Fid*
684 Exmkfid(Export *fs, ulong fid)
685 {
686 	ulong h;
687 	Fid *f, *nf;
688 
689 	nf = malloc(sizeof(Fid));
690 	if(nf == nil)
691 		return nil;
692 	lock(&fs->fidlock);
693 	h = fid % Nfidhash;
694 	for(f = fs->fid[h]; f != nil; f = f->next){
695 		if(f->fid == fid){
696 			unlock(&fs->fidlock);
697 			free(nf);
698 			return nil;
699 		}
700 	}
701 
702 	nf->next = fs->fid[h];
703 	if(nf->next != nil)
704 		nf->next->last = &nf->next;
705 	nf->last = &fs->fid[h];
706 	fs->fid[h] = nf;
707 
708 	nf->fid = fid;
709 	nf->ref = 1;
710 	nf->attached = 1;
711 	nf->offset = 0;
712 	nf->chan = nil;
713 	nf->qid = nil;
714 	unlock(&fs->fidlock);
715 	return nf;
716 }
717 
718 static Fid*
719 Exgetfid(Export *fs, ulong fid)
720 {
721 	Fid *f;
722 	ulong h;
723 
724 	lock(&fs->fidlock);
725 	h = fid % Nfidhash;
726 	for(f = fs->fid[h]; f; f = f->next) {
727 		if(f->fid == fid){
728 			if(f->attached == 0)
729 				break;
730 			f->ref++;
731 			unlock(&fs->fidlock);
732 			return f;
733 		}
734 	}
735 	unlock(&fs->fidlock);
736 	return nil;
737 }
738 
739 static void
740 Exputfid(Export *fs, Fid *f)
741 {
742 	Chan *c;
743 
744 	lock(&fs->fidlock);
745 	f->ref--;
746 	if(f->ref == 0 && f->attached == 0){
747 		c = f->chan;
748 		f->chan = nil;
749 		*f->last = f->next;
750 		if(f->next != nil)
751 			f->next->last = f->last;
752 		unlock(&fs->fidlock);
753 		if(c != nil)
754 			cclose(c);
755 		freeuqid(fs, f->qid);
756 		free(f);
757 		return;
758 	}
759 	unlock(&fs->fidlock);
760 }
761 
762 static Chan*
763 exmount(Chan *c, Mhead **mp, int doname)
764 {
765 	Chan *nc;
766 	Cname *oname;
767 
768 	nc = nil;
769 	if((c->flag & COPEN) == 0 && findmount(&nc, mp, c->type, c->dev, c->qid)){
770 		if(waserror()){
771 			cclose(nc);
772 			nexterror();
773 		}
774 		nc = cunique(nc);
775 		poperror();
776 		if(doname){
777 			oname = c->name;
778 			incref(oname);
779 			cnameclose(nc->name);
780 			nc->name = oname;
781 		}
782 		return nc;
783 	}
784 	incref(c);
785 	return c;
786 }
787 
788 static char*
789 Exversion(Export *fs, Fcall *t, Fcall *r)
790 {
791 	char *p;
792 	static char version[] = VERSION9P;
793 	int iounit;
794 
795 	r->msize = t->msize;
796 	if(r->msize > MAXRPCMAX)
797 		r->msize = MAXRPCMAX;
798 	iounit = fs->io->iounit;
799 	if(iounit != 0 && iounit > 64 && iounit < r->msize)
800 		r->msize = iounit;
801 	if(r->msize < 64)
802 		return "message size too small";
803 	if((p = strchr(t->version, '.')) != nil)
804 		*p = 0;
805 	if(strncmp(t->version, "9P", 2) ==0 && strcmp(version, t->version) <= 0){
806 		r->version = version;
807 		fs->msize = r->msize;
808 	}else
809 		r->version = "unknown";
810 	return nil;
811 }
812 
813 static char*
814 Exauth(Export *fs, Fcall *t, Fcall *r)
815 {
816 	USED(fs);
817 	USED(t);
818 	USED(r);
819 	return "authentication not required";
820 }
821 
822 static char*
823 Exattach(Export *fs, Fcall *t, Fcall *r)
824 {
825 	Fid *f;
826 
827 	f = Exmkfid(fs, t->fid);
828 	if(f == nil)
829 		return Edupfid;
830 	if(waserror()){
831 		f->attached = 0;
832 		Exputfid(fs, f);
833 		return up->env->errstr;
834 	}
835 	f->chan = cclone(fs->root);
836 	f->qid = uqidalloc(fs, f->chan);
837 	poperror();
838 	r->qid = Exrmtqid(f->chan, f->qid);
839 	Exputfid(fs, f);
840 	return nil;
841 }
842 
843 static char*
844 Exclunk(Export *fs, Fcall *t, Fcall *r)
845 {
846 	Fid *f;
847 
848 	USED(r);
849 	f = Exgetfid(fs, t->fid);
850 	if(f == nil)
851 		return Enofid;
852 	f->attached = 0;
853 	Exputfid(fs, f);
854 	return nil;
855 }
856 
857 static int
858 safewalk(Chan **cp, char **names, int nnames, int nomount, int *nerror)
859 {
860 	int r;
861 
862 	/* walk can raise error */
863 	if(waserror())
864 		return -1;
865 	r = walk(cp, names, nnames, nomount, nerror);
866 	poperror();
867 	return r;
868 }
869 
870 static char*
871 Exwalk(Export *fs, Fcall *t, Fcall *r)
872 {
873 	Fid *f, *nf;
874 	Chan *c;
875 	char *name;
876 	Uqid *qid;
877 	int i;
878 
879 	f = Exgetfid(fs, t->fid);
880 	if(f == nil)
881 		return Enofid;
882 	if(f->chan->flag & COPEN){
883 		Exputfid(fs, f);
884 		return Eopen;
885 	}
886 
887 	if(waserror())
888 		return up->env->errstr;
889 	c = cclone(f->chan);
890 	poperror();
891 	qid = f->qid;
892 	incref(qid);
893 	r->nwqid = 0;
894 	if(t->nwname > 0){
895 		for(i=0; i<t->nwname; i++){
896 			name = t->wname[i];
897 			if(!exisroot(fs, c) || *name != '\0' && strcmp(name, "..") != 0){
898 				if(safewalk(&c, &name, 1, 0, nil) < 0){
899 					/* leave the original state on error */
900 					cclose(c);
901 					freeuqid(fs, qid);
902 					Exputfid(fs, f);
903 					if(i == 0)
904 						return up->env->errstr;
905 					return nil;
906 				}
907 				freeuqid(fs, qid);
908 				qid = uqidalloc(fs, c);
909 			}
910 			r->wqid[r->nwqid++] = Exrmtqid(c, qid);
911 		}
912 	}
913 
914 	if(t->newfid != t->fid){
915 		nf = Exmkfid(fs, t->newfid);
916 		if(nf == nil){
917 			cclose(c);
918 			freeuqid(fs, qid);
919 			Exputfid(fs, f);
920 			return Edupfid;
921 		}
922 		nf->chan = c;
923 		nf->qid = qid;
924 		Exputfid(fs, nf);
925 	}else{
926 		cclose(f->chan);
927 		f->chan = c;
928 		freeuqid(fs, f->qid);
929 		f->qid = qid;
930 	}
931 	Exputfid(fs, f);
932 	return nil;
933 }
934 
935 static char*
936 Exopen(Export *fs, Fcall *t, Fcall *r)
937 {
938 	Fid *f;
939 	Chan *c;
940 	Uqid *qid;
941 	Mhead *m;
942 
943 	f = Exgetfid(fs, t->fid);
944 	if(f == nil)
945 		return Enofid;
946 	if(f->chan->flag & COPEN){
947 		Exputfid(fs, f);
948 		return Emode;
949 	}
950 	m = nil;
951 	c = exmount(f->chan, &m, 1);
952 	if(waserror()){
953 		cclose(c);
954 		Exputfid(fs, f);
955 		return up->env->errstr;
956 	}
957 
958 	/* only save the mount head if it's a multiple element union */
959 	if(m && m->mount && m->mount->next)
960 		c->umh = m;
961 	else
962 		putmhead(m);
963 
964 	c = devtab[c->type]->open(c, t->mode);
965 	if(t->mode & ORCLOSE)
966 		c->flag |= CRCLOSE;
967 
968 	qid = uqidalloc(fs, c);
969 	poperror();
970 	freeuqid(fs, f->qid);
971 	cclose(f->chan);
972 	f->chan = c;
973 	f->qid = qid;
974 	f->offset = 0;
975 	r->qid = Exrmtqid(c, f->qid);
976 	r->iounit = exiounit(fs, c);
977 	Exputfid(fs, f);
978 	return nil;
979 }
980 
981 static char*
982 Excreate(Export *fs, Fcall *t, Fcall *r)
983 {
984 	Fid *f;
985 	volatile struct {Chan *c;} c, dc;
986 	Cname *oname;
987 	Uqid *qid;
988 	Mhead *m;
989 
990 	f = Exgetfid(fs, t->fid);
991 	if(f == nil)
992 		return Enofid;
993 	if(f->chan->flag & COPEN){
994 		Exputfid(fs, f);
995 		return Emode;
996 	}
997 	if(waserror()){
998 		Exputfid(fs, f);
999 		return up->env->errstr;
1000 	}
1001 	validname(t->name, 0);
1002 	if(t->name[0] == '.' && (t->name[1] == '\0' || t->name[1] == '.' && t->name[2] == '\0'))
1003 		error(Efilename);	/* underlying server should check, but stop it here */
1004 
1005 	m = nil;
1006 	c.c = exmount(f->chan, &m, 1);
1007 	if(waserror()){
1008 		cclose(c.c);
1009 		if(m != nil)
1010 			putmhead(m);
1011 		nexterror();
1012 	}
1013 	if(m != nil){
1014 		oname = c.c->name;
1015 		incref(oname);
1016 		if(waserror()){
1017 			cnameclose(oname);
1018 			nexterror();
1019 		}
1020 		dc.c = createdir(c.c, m);
1021 		if(waserror()){
1022 			cclose(dc.c);
1023 			nexterror();
1024 		}
1025 		c.c = cunique(dc.c);
1026 		poperror();
1027 		cnameclose(c.c->name);
1028 		poperror();
1029 		c.c->name = oname;
1030 	}
1031 	devtab[c.c->type]->create(c.c, t->name, t->mode, t->perm);
1032 	c.c->name = addelem(c.c->name, t->name);
1033 	if(t->mode & ORCLOSE)
1034 		c.c->flag |= CRCLOSE;
1035 	qid = uqidalloc(fs, c.c);
1036 	poperror();
1037 	if(m != nil)
1038 		putmhead(m);
1039 
1040 	poperror();
1041 	cclose(f->chan);
1042 	f->chan = c.c;
1043 	freeuqid(fs, f->qid);
1044 	f->qid = qid;
1045 	r->qid = Exrmtqid(c.c, f->qid);
1046 	r->iounit = exiounit(fs, c.c);
1047 	Exputfid(fs, f);
1048 	return nil;
1049 }
1050 
1051 static char*
1052 Exread(Export *fs, Fcall *t, Fcall *r)
1053 {
1054 	Fid *f;
1055 	Chan *c;
1056 	vlong off;
1057 	int dir, n, seek;
1058 
1059 	f = Exgetfid(fs, t->fid);
1060 	if(f == nil)
1061 		return Enofid;
1062 
1063 	if(waserror()) {
1064 		Exputfid(fs, f);
1065 		return up->env->errstr;
1066 	}
1067 	c = f->chan;
1068 	if((c->flag & COPEN) == 0)
1069 		error(Emode);
1070 	if(c->mode != OREAD && c->mode != ORDWR)
1071 		error(Eaccess);
1072 	if(t->count < 0 || t->count > fs->msize-IOHDRSZ)
1073 		error(Ecount);
1074 	if(t->offset < 0)
1075 		error(Enegoff);
1076 	dir = c->qid.type & QTDIR;
1077 	if(dir && t->offset != f->offset){
1078 		if(t->offset != 0)
1079 			error(Eseekdir);
1080 		f->offset = 0;
1081 		c->uri = 0;
1082 		c->dri = 0;
1083 	}
1084 
1085 	for(;;){
1086 		n = t->count;
1087 		seek = 0;
1088 		off = t->offset;
1089 		if(dir && f->offset != off){
1090 			off = f->offset;
1091 			n = t->offset - off;
1092 			if(n > MAXFDATA)
1093 				n = MAXFDATA;
1094 			seek = 1;
1095 		}
1096 		if(dir && c->umh != nil){
1097 			if(0)
1098 				print("union read %d uri %d dri %d\n", seek, c->uri, c->dri);
1099 			n = unionread(c, r->data, n);
1100 		}
1101 		else{
1102 			c->offset = off;
1103 			n = devtab[c->type]->read(c, r->data, n, off);
1104 			lock(c);
1105 			c->offset += n;
1106 			unlock(c);
1107 		}
1108 		f->offset = off + n;
1109 		if(n == 0 || !seek)
1110 			break;
1111 	}
1112 	r->count = n;
1113 
1114 	poperror();
1115 	Exputfid(fs, f);
1116 	return nil;
1117 }
1118 
1119 static char*
1120 Exwrite(Export *fs, Fcall *t, Fcall *r)
1121 {
1122 	Fid *f;
1123 	Chan *c;
1124 
1125 	f = Exgetfid(fs, t->fid);
1126 	if(f == nil)
1127 		return Enofid;
1128 	if(waserror()){
1129 		Exputfid(fs, f);
1130 		return up->env->errstr;
1131 	}
1132 	c = f->chan;
1133 	if((c->flag & COPEN) == 0)
1134 		error(Emode);
1135 	if(c->mode != OWRITE && c->mode != ORDWR)
1136 		error(Eaccess);
1137 	if(c->qid.type & QTDIR)
1138 		error(Eisdir);
1139 	if(t->count < 0 || t->count > fs->msize-IOHDRSZ)
1140 		error(Ecount);
1141 	if(t->offset < 0)
1142 		error(Enegoff);
1143 	r->count = devtab[c->type]->write(c, t->data, t->count, t->offset);
1144 	poperror();
1145 	Exputfid(fs, f);
1146 	return nil;
1147 }
1148 
1149 static char*
1150 Exstat(Export *fs, Fcall *t, Fcall *r)
1151 {
1152 	Fid *f;
1153 	Chan *c;
1154 	int n;
1155 
1156 	f = Exgetfid(fs, t->fid);
1157 	if(f == nil)
1158 		return Enofid;
1159 	c = exmount(f->chan, nil, 1);
1160 	if(waserror()){
1161 		cclose(c);
1162 		Exputfid(fs, f);
1163 		return up->env->errstr;
1164 	}
1165 	n = devtab[c->type]->stat(c, r->stat, r->nstat);
1166 	if(n <= BIT16SZ)
1167 		error(Eshortstat);
1168 	r->nstat = n;
1169 	poperror();
1170 	cclose(c);
1171 	Exputfid(fs, f);
1172 	return nil;
1173 }
1174 
1175 static char*
1176 Exwstat(Export *fs, Fcall *t, Fcall *r)
1177 {
1178 	Fid *f;
1179 	Chan *c;
1180 
1181 	USED(r);
1182 	f = Exgetfid(fs, t->fid);
1183 	if(f == nil)
1184 		return Enofid;
1185 	if(waserror()){
1186 		Exputfid(fs, f);
1187 		return up->env->errstr;
1188 	}
1189 	validstat(t->stat, t->nstat);	/* check name */
1190 
1191 	c = exmount(f->chan, nil, 0);
1192 	if(waserror()){
1193 		cclose(c);
1194 		nexterror();
1195 	}
1196 	devtab[c->type]->wstat(c, t->stat, t->nstat);
1197 	poperror();
1198 
1199 	cclose(c);
1200 	poperror();
1201 	Exputfid(fs, f);
1202 	return nil;
1203 }
1204 
1205 static char*
1206 Exremove(Export *fs, Fcall *t, Fcall *r)
1207 {
1208 	Fid *f;
1209 	Chan *c;
1210 
1211 	USED(r);
1212 	f = Exgetfid(fs, t->fid);
1213 	if(f == nil)
1214 		return Enofid;
1215 	if(waserror()){
1216 		f->attached = 0;
1217 		Exputfid(fs, f);
1218 		return up->env->errstr;
1219 	}
1220 	c = exmount(f->chan, nil, 0);
1221 	if(waserror()){
1222 		c->type = 0;	/* see below */
1223 		cclose(c);
1224 		nexterror();
1225 	}
1226 	devtab[c->type]->remove(c);
1227 	poperror();
1228 	poperror();
1229 
1230 	/*
1231 	 * chan is already clunked by remove.
1232 	 * however, we need to recover the chan,
1233 	 * and follow sysremove's lead in making it point to root.
1234 	 */
1235 	c->type = 0;
1236 
1237 	cclose(c);
1238 	f->attached = 0;
1239 	Exputfid(fs, f);
1240 	return nil;
1241 }
1242 
1243 /*
1244  * unique path generation
1245  */
1246 
1247 static int
1248 uqidhash(vlong path)
1249 {
1250 	ulong p;
1251 	p = (ulong)path;
1252 	return ((p>>16) ^ (p>>8) ^ p) & (Nqidhash-1);
1253 }
1254 
1255 static Uqid **
1256 uqidlook(Uqid **tab, Chan *c, vlong path)
1257 {
1258 	Uqid **hp, *q;
1259 
1260 	for(hp = &tab[uqidhash(path)]; (q = *hp) != nil; hp = &q->next)
1261 		if(q->type == c->type && q->dev == c->dev && q->oldpath == path)
1262 			break;
1263 	return hp;
1264 }
1265 
1266 static int
1267 uqidexists(Uqid **tab, vlong path)
1268 {
1269 	int i;
1270 	Uqid *q;
1271 
1272 	for(i=0; i<Nqidhash; i++)
1273 		for(q = tab[i]; q != nil; q = q->next)
1274 			if(q->newpath == path)
1275 				return 1;
1276 	return 0;
1277 }
1278 
1279 static Uqid *
1280 uqidalloc(Export *fs, Chan *c)
1281 {
1282 	Uqid **hp, *q;
1283 
1284 	qlock(&fs->qidlock);
1285 	hp = uqidlook(fs->qids, c, c->qid.path);
1286 	if((q = *hp) != nil){
1287 		incref(q);
1288 		qunlock(&fs->qidlock);
1289 		return q;
1290 	}
1291 	q = mallocz(sizeof(*q), 1);
1292 	if(q == nil){
1293 		qunlock(&fs->qidlock);
1294 		error(Enomem);
1295 	}
1296 	q->ref = 1;
1297 	q->type = c->type;
1298 	q->dev = c->dev;
1299 	q->oldpath = c->qid.path;
1300 	q->newpath = c->qid.path;
1301 	while(uqidexists(fs->qids, q->newpath)){
1302 		if(++fs->pathgen >= (1<<16))
1303 			fs->pathgen = 1;
1304 		q->newpath = ((vlong)fs->pathgen<<48) | (q->newpath & QIDMASK);
1305 	}
1306 	q->next = nil;
1307 	*hp = q;
1308 	qunlock(&fs->qidlock);
1309 	return q;
1310 }
1311 
1312 static void
1313 freeuqid(Export *fs, Uqid *q)
1314 {
1315 	Uqid **hp;
1316 
1317 	if(q == nil)
1318 		return;
1319 	qlock(&fs->qidlock);
1320 	if(decref(q) == 0){
1321 		hp = &fs->qids[uqidhash(q->oldpath)];
1322 		for(; *hp != nil; hp = &(*hp)->next)
1323 			if(*hp == q){
1324 				*hp = q->next;
1325 				free(q);
1326 				break;
1327 			}
1328 	}
1329 	qunlock(&fs->qidlock);
1330 }
1331