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