xref: /plan9-contrib/sys/src/cmd/exportfs/exportsrv.c (revision 61b17cc6b921f626d2c0a0d5982f2e19948a8fce)
1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <fcall.h>
5 #define Extern	extern
6 #include "exportfs.h"
7 
8 extern char *netdir, *local, *remote;
9 
10 char Ebadfid[] = "bad fid";
11 char Enotdir[] = "not a directory";
12 char Edupfid[] = "fid already in use";
13 char Eopen[] = "fid already opened";
14 char Emip[] = "mount in progress";
15 char Enopsmt[] = "out of pseudo mount points";
16 char Enomem[] = "no memory";
17 char Eversion[] = "bad 9P2000 version";
18 char Ereadonly[] = "file system read only";
19 char Enoprocs[] = "out of processes";
20 
21 ulong messagesize;
22 int readonly;
23 
24 void
Xversion(Fsrpc * t)25 Xversion(Fsrpc *t)
26 {
27 	Fcall rhdr;
28 
29 	if(t->work.msize < 256){
30 		reply(&t->work, &rhdr, "version: message size too small");
31 		putsbuf(t);
32 		return;
33 	}
34 	if(t->work.msize > messagesize)
35 		t->work.msize = messagesize;
36 	messagesize = t->work.msize;
37 	if(strncmp(t->work.version, "9P2000", 6) != 0){
38 		reply(&t->work, &rhdr, Eversion);
39 		putsbuf(t);
40 		return;
41 	}
42 	rhdr.version = "9P2000";
43 	rhdr.msize = t->work.msize;
44 	reply(&t->work, &rhdr, 0);
45 	putsbuf(t);
46 }
47 
48 void
Xauth(Fsrpc * t)49 Xauth(Fsrpc *t)
50 {
51 	Fcall rhdr;
52 
53 	reply(&t->work, &rhdr, "exportfs: authentication not required");
54 	putsbuf(t);
55 }
56 
57 void
Xflush(Fsrpc * t)58 Xflush(Fsrpc *t)
59 {
60 	Fcall rhdr;
61 	Fsrpc *w;
62 	Proc *m;
63 
64 	for(m = Proclist; m != nil; m = m->next){
65 		w = m->busy;
66 		if(w == nil || w->work.tag != t->work.oldtag)
67 			continue;
68 
69 		lock(m);
70 		w = m->busy;
71 		if(w != nil && w->work.tag == t->work.oldtag) {
72 			w->flushtag = t->work.tag;
73 			DEBUG(DFD, "\tset flushtag %d\n", t->work.tag);
74 			postnote(PNPROC, m->pid, "flush");
75 			unlock(m);
76 			putsbuf(t);
77 			return;
78 		}
79 		unlock(m);
80 	}
81 
82 	reply(&t->work, &rhdr, 0);
83 	DEBUG(DFD, "\tflush reply\n");
84 	putsbuf(t);
85 }
86 
87 void
Xattach(Fsrpc * t)88 Xattach(Fsrpc *t)
89 {
90 	int i, nfd;
91 	Fcall rhdr;
92 	Fid *f;
93 	char buf[128];
94 
95 	f = newfid(t->work.fid);
96 	if(f == nil) {
97 		reply(&t->work, &rhdr, Ebadfid);
98 		putsbuf(t);
99 		return;
100 	}
101 
102 	if(srvfd >= 0){
103 		if(psmpt == nil)
104 			goto Nomount;
105 		for(i=0; i<Npsmpt; i++)
106 			if(psmap[i] == 0)
107 				break;
108 		if(i >= Npsmpt)
109 			goto Nomount;
110 		snprint(buf, sizeof(buf), "%d", i);
111 		f->f = file(psmpt, buf);
112 		if(f->f == nil)
113 			goto Nomount;
114 		snprint(buf, sizeof(buf), "/mnt/exportfs/%d", i);
115 		nfd = dup(srvfd, -1);
116 		if(amount(nfd, buf, MREPL|MCREATE, t->work.aname) < 0){
117 			errstr(buf, sizeof buf);
118 			reply(&t->work, &rhdr, buf);
119 			freefid(t->work.fid);
120 			putsbuf(t);
121 			close(nfd);
122 			return;
123 		}
124 		psmap[i] = 1;
125 		f->mid = i;
126 	}else{
127 		f->f = root;
128 		f->f->ref++;
129 	}
130 
131 	rhdr.qid = f->f->qid;
132 	reply(&t->work, &rhdr, 0);
133 	putsbuf(t);
134 	return;
135 
136 Nomount:
137 	reply(&t->work, &rhdr, Enopsmt);
138 	freefid(t->work.fid);
139 	putsbuf(t);
140 }
141 
142 Fid*
clonefid(Fid * f,int new)143 clonefid(Fid *f, int new)
144 {
145 	Fid *n;
146 
147 	n = newfid(new);
148 	if(n == nil) {
149 		n = getfid(new);
150 		if(n == nil)
151 			fatal("inconsistent fids");
152 		if(n->fid >= 0)
153 			close(n->fid);
154 		freefid(new);
155 		n = newfid(new);
156 		if(n == nil)
157 			fatal("inconsistent fids2");
158 	}
159 	n->f = f->f;
160 	n->f->ref++;
161 	return n;
162 }
163 
164 void
Xwalk(Fsrpc * t)165 Xwalk(Fsrpc *t)
166 {
167 	char err[ERRMAX], *e;
168 	Fcall rhdr;
169 	Fid *f, *nf;
170 	File *wf;
171 	int i;
172 
173 	f = getfid(t->work.fid);
174 	if(f == nil) {
175 		reply(&t->work, &rhdr, Ebadfid);
176 		putsbuf(t);
177 		return;
178 	}
179 
180 	nf = nil;
181 	if(t->work.newfid != t->work.fid){
182 		nf = clonefid(f, t->work.newfid);
183 		f = nf;
184 	}
185 
186 	rhdr.nwqid = 0;
187 	e = nil;
188 	for(i=0; i<t->work.nwname; i++){
189 
190 		if(strcmp(t->work.wname[i], "..") == 0) {
191 			if(f->f->parent != nil)
192 				wf = f->f->parent;
193 			else
194 				wf = f->f;
195 			wf->ref++;
196 		}else{
197 			wf = file(f->f, t->work.wname[i]);
198 			if(wf == nil){
199 				errstr(err, sizeof err);
200 				e = err;
201 				break;
202 			}
203 		}
204 
205 		freefile(f->f);
206 		rhdr.wqid[rhdr.nwqid++] = wf->qid;
207 		f->f = wf;
208 		continue;
209 	}
210 
211 	if(nf!=nil && (e!=nil || rhdr.nwqid!=t->work.nwname))
212 		freefid(t->work.newfid);
213 	if(rhdr.nwqid > 0)
214 		e = nil;
215 	reply(&t->work, &rhdr, e);
216 	putsbuf(t);
217 }
218 
219 void
Xclunk(Fsrpc * t)220 Xclunk(Fsrpc *t)
221 {
222 	Fcall rhdr;
223 	Fid *f;
224 
225 	f = getfid(t->work.fid);
226 	if(f == nil) {
227 		reply(&t->work, &rhdr, Ebadfid);
228 		putsbuf(t);
229 		return;
230 	}
231 
232 	if(f->fid >= 0)
233 		close(f->fid);
234 
235 	freefid(t->work.fid);
236 	reply(&t->work, &rhdr, 0);
237 	putsbuf(t);
238 }
239 
240 void
Xstat(Fsrpc * t)241 Xstat(Fsrpc *t)
242 {
243 	char err[ERRMAX], *path;
244 	Fcall rhdr;
245 	Fid *f;
246 	Dir *d;
247 	int s;
248 	uchar *statbuf;
249 
250 	f = getfid(t->work.fid);
251 	if(f == nil) {
252 		reply(&t->work, &rhdr, Ebadfid);
253 		putsbuf(t);
254 		return;
255 	}
256 	if(f->fid >= 0)
257 		d = dirfstat(f->fid);
258 	else {
259 		path = makepath(f->f, "");
260 		d = dirstat(path);
261 		free(path);
262 	}
263 
264 	if(d == nil) {
265 		errstr(err, sizeof err);
266 		reply(&t->work, &rhdr, err);
267 		putsbuf(t);
268 		return;
269 	}
270 
271 	d->qid.path = f->f->qidt->uniqpath;
272 	s = sizeD2M(d);
273 	statbuf = emallocz(s);
274 	s = convD2M(d, statbuf, s);
275 	free(d);
276 	rhdr.nstat = s;
277 	rhdr.stat = statbuf;
278 	reply(&t->work, &rhdr, 0);
279 	free(statbuf);
280 	putsbuf(t);
281 }
282 
283 static int
getiounit(int fd)284 getiounit(int fd)
285 {
286 	int n;
287 
288 	n = iounit(fd);
289 	if(n > messagesize-IOHDRSZ)
290 		n = messagesize-IOHDRSZ;
291 	return n;
292 }
293 
294 void
Xcreate(Fsrpc * t)295 Xcreate(Fsrpc *t)
296 {
297 	char err[ERRMAX], *path;
298 	Fcall rhdr;
299 	Fid *f;
300 	File *nf;
301 
302 	if(readonly) {
303 		reply(&t->work, &rhdr, Ereadonly);
304 		putsbuf(t);
305 		return;
306 	}
307 	f = getfid(t->work.fid);
308 	if(f == nil) {
309 		reply(&t->work, &rhdr, Ebadfid);
310 		putsbuf(t);
311 		return;
312 	}
313 
314 
315 	path = makepath(f->f, t->work.name);
316 	f->fid = create(path, t->work.mode, t->work.perm);
317 	free(path);
318 	if(f->fid < 0) {
319 		errstr(err, sizeof err);
320 		reply(&t->work, &rhdr, err);
321 		putsbuf(t);
322 		return;
323 	}
324 
325 	nf = file(f->f, t->work.name);
326 	if(nf == nil) {
327 		errstr(err, sizeof err);
328 		reply(&t->work, &rhdr, err);
329 		putsbuf(t);
330 		return;
331 	}
332 
333 	f->mode = t->work.mode;
334 	freefile(f->f);
335 	f->f = nf;
336 	rhdr.qid = f->f->qid;
337 	rhdr.iounit = getiounit(f->fid);
338 	reply(&t->work, &rhdr, 0);
339 	putsbuf(t);
340 }
341 
342 void
Xremove(Fsrpc * t)343 Xremove(Fsrpc *t)
344 {
345 	char err[ERRMAX], *path;
346 	Fcall rhdr;
347 	Fid *f;
348 
349 	if(readonly) {
350 		reply(&t->work, &rhdr, Ereadonly);
351 		putsbuf(t);
352 		return;
353 	}
354 	f = getfid(t->work.fid);
355 	if(f == nil) {
356 		reply(&t->work, &rhdr, Ebadfid);
357 		putsbuf(t);
358 		return;
359 	}
360 
361 	path = makepath(f->f, "");
362 	DEBUG(DFD, "\tremove: %s\n", path);
363 	if(remove(path) < 0) {
364 		free(path);
365 		errstr(err, sizeof err);
366 		reply(&t->work, &rhdr, err);
367 		putsbuf(t);
368 		return;
369 	}
370 	free(path);
371 
372 	f->f->inval = 1;
373 	if(f->fid >= 0)
374 		close(f->fid);
375 	freefid(t->work.fid);
376 
377 	reply(&t->work, &rhdr, 0);
378 	putsbuf(t);
379 }
380 
381 void
Xwstat(Fsrpc * t)382 Xwstat(Fsrpc *t)
383 {
384 	char err[ERRMAX], *path;
385 	Fcall rhdr;
386 	Fid *f;
387 	int s;
388 	char *strings;
389 	Dir d;
390 
391 	if(readonly) {
392 		reply(&t->work, &rhdr, Ereadonly);
393 		putsbuf(t);
394 		return;
395 	}
396 	f = getfid(t->work.fid);
397 	if(f == nil) {
398 		reply(&t->work, &rhdr, Ebadfid);
399 		putsbuf(t);
400 		return;
401 	}
402 	strings = emallocz(t->work.nstat);	/* ample */
403 	if(convM2D(t->work.stat, t->work.nstat, &d, strings) <= BIT16SZ){
404 		rerrstr(err, sizeof err);
405 		reply(&t->work, &rhdr, err);
406 		putsbuf(t);
407 		free(strings);
408 		return;
409 	}
410 
411 	if(f->fid >= 0)
412 		s = dirfwstat(f->fid, &d);
413 	else {
414 		path = makepath(f->f, "");
415 		s = dirwstat(path, &d);
416 		free(path);
417 	}
418 	if(s < 0) {
419 		rerrstr(err, sizeof err);
420 		reply(&t->work, &rhdr, err);
421 	}
422 	else {
423 		/* wstat may really be rename */
424 		if(strcmp(d.name, f->f->name)!=0 && d.name[0]!=0){
425 			free(f->f->name);
426 			f->f->name = estrdup(d.name);
427 		}
428 		reply(&t->work, &rhdr, 0);
429 	}
430 	free(strings);
431 	putsbuf(t);
432 }
433 
434 /*
435  * based on libthread's threadsetname, but drags in less library code.
436  * actually just sets the arguments displayed.
437  */
438 void
procsetname(char * fmt,...)439 procsetname(char *fmt, ...)
440 {
441 	int fd;
442 	char *cmdname;
443 	char buf[128];
444 	va_list arg;
445 
446 	va_start(arg, fmt);
447 	cmdname = vsmprint(fmt, arg);
448 	va_end(arg);
449 	if (cmdname == nil)
450 		return;
451 	snprint(buf, sizeof buf, "#p/%d/args", getpid());
452 	if((fd = open(buf, OWRITE)) >= 0){
453 		write(fd, cmdname, strlen(cmdname)+1);
454 		close(fd);
455 	}
456 	free(cmdname);
457 }
458 
459 void
slave(Fsrpc * f)460 slave(Fsrpc *f)
461 {
462 	static int nproc;
463 	Proc *m, **l;
464 	Fcall rhdr;
465 	int omode;
466 	int pid;
467 
468 	if(readonly){
469 		switch(f->work.type){
470 		case Twrite:
471 			reply(&f->work, &rhdr, Ereadonly);
472 			putsbuf(f);
473 			return;
474 		case Topen:
475 			omode = f->work.mode & 3;
476 			if(omode != OREAD && omode != OEXEC || (f->work.mode&(OTRUNC|ORCLOSE)) != 0){
477 				reply(&f->work, &rhdr, Ereadonly);
478 				putsbuf(f);
479 				return;
480 			}
481 		}
482 	}
483 	for(;;) {
484 		for(l = &Proclist; (m = *l) != nil; l = &m->next) {
485 			if(m->busy != nil)
486 				continue;
487 
488 			m->busy = f;
489 			while(rendezvous(m, f) == (void*)~0)
490 				;
491 
492 			/* swept a slave proc */
493 			if(f == nil){
494 				*l = m->next;
495 				free(m);
496 				nproc--;
497 				break;
498 			}
499 			f = nil;
500 
501 			/*
502 			 * as long as the number of slave procs
503 			 * is small, dont bother sweeping.
504 			 */
505 			if(nproc < 16)
506 				break;
507 		}
508 		if(f == nil)
509 			return;
510 
511 		m = emallocz(sizeof(Proc));
512 		pid = rfork(RFPROC|RFMEM|RFNOWAIT);
513 		switch(pid) {
514 		case -1:
515 			reply(&f->work, &rhdr, Enoprocs);
516 			putsbuf(f);
517 			free(m);
518 			return;
519 
520 		case 0:
521 			if(local[0] != '\0'){
522 				if(netdir[0] != '\0')
523 					procsetname("%s: %s -> %s", netdir, local, remote);
524 				else
525 					procsetname("%s -> %s", local, remote);
526 			}
527 			blockingslave(m);
528 			_exits(0);
529 
530 		default:
531 			m->pid = pid;
532 			m->next = Proclist;
533 			Proclist = m;
534 			nproc++;
535 		}
536 	}
537 }
538 
539 void
blockingslave(Proc * m)540 blockingslave(Proc *m)
541 {
542 	Fsrpc *p;
543 	Fcall rhdr;
544 
545 	notify(flushaction);
546 
547 	for(;;) {
548 		p = rendezvous(m, nil);
549 		if(p == (void*)~0)	/* Interrupted */
550 			continue;
551 		if(p == nil)		/* Swept */
552 			break;
553 
554 		DEBUG(DFD, "\tslave: %d %F\n", m->pid, &p->work);
555 		if(p->flushtag != NOTAG)
556 			goto flushme;
557 
558 		switch(p->work.type) {
559 		case Tread:
560 			slaveread(p);
561 			break;
562 
563 		case Twrite:
564 			slavewrite(p);
565 			break;
566 
567 		case Topen:
568 			slaveopen(p);
569 			break;
570 
571 		default:
572 			reply(&p->work, &rhdr, "exportfs: slave type error");
573 		}
574 flushme:
575 		lock(m);
576 		m->busy = nil;
577 		unlock(m);
578 
579 		/* no more flushes can come in now */
580 		if(p->flushtag != NOTAG) {
581 			p->work.type = Tflush;
582 			p->work.tag = p->flushtag;
583 			reply(&p->work, &rhdr, 0);
584 		}
585 		putsbuf(p);
586 	}
587 }
588 
589 int
openmount(int sfd)590 openmount(int sfd)
591 {
592 	int p[2], fd, i, n;
593 	char *arg[10], fdbuf[20], mbuf[20];
594 	Dir *dir;
595 
596 	if(pipe(p) < 0)
597 		return -1;
598 
599 	switch(rfork(RFPROC|RFMEM|RFNOWAIT|RFNAMEG|RFFDG|RFREND)){
600 	case -1:
601 		close(p[0]);
602 		close(p[1]);
603 		return -1;
604 
605 	default:
606 		close(sfd);
607 		close(p[0]);
608 		return p[1];
609 
610 	case 0:
611 		break;
612 	}
613 
614 	dup(p[0], 0);
615 	dup(p[0], 1);
616 
617 	/* close all remaining file descriptors except sfd */
618 	if((fd = open("/fd", OREAD)) < 0)
619 		_exits("open /fd failed");
620 	n = dirreadall(fd, &dir);
621 	for(i=0; i<n; i++){
622 		if(strstr(dir[i].name, "ctl"))
623 			continue;
624 		fd = atoi(dir[i].name);
625 		if(fd > 2 && fd != sfd)
626 			close(fd);
627 	}
628 	free(dir);
629 
630 	arg[0] = argv0; /* "/bin/exportfs" */
631 	snprint(fdbuf, sizeof fdbuf, "-S/fd/%d", sfd);
632 	arg[1] = fdbuf;
633 	snprint(mbuf, sizeof mbuf, "-m%lud", messagesize-IOHDRSZ);
634 	arg[2] = mbuf;
635 	arg[3] = nil;
636 
637 	exec(arg[0], arg);
638 	_exits("whoops: exec failed");
639 	return -1;
640 }
641 
642 void
slaveopen(Fsrpc * p)643 slaveopen(Fsrpc *p)
644 {
645 	char err[ERRMAX], *path;
646 	Fcall *work, rhdr;
647 	Fid *f;
648 	Dir *d;
649 
650 	work = &p->work;
651 
652 	f = getfid(work->fid);
653 	if(f == nil) {
654 		reply(work, &rhdr, Ebadfid);
655 		return;
656 	}
657 	if(f->fid >= 0) {
658 		close(f->fid);
659 		f->fid = -1;
660 	}
661 
662 	path = makepath(f->f, "");
663 	DEBUG(DFD, "\topen: %s %d\n", path, work->mode);
664 	f->fid = open(path, work->mode);
665 	free(path);
666 	if(f->fid < 0 || (d = dirfstat(f->fid)) == nil) {
667 	Error:
668 		errstr(err, sizeof err);
669 		reply(work, &rhdr, err);
670 		return;
671 	}
672 	f->f->qid = d->qid;
673 	free(d);
674 	if(f->f->qid.type & QTMOUNT){	/* fork new exportfs for this */
675 		f->fid = openmount(f->fid);
676 		if(f->fid < 0)
677 			goto Error;
678 	}
679 
680 	DEBUG(DFD, "\topen: fd %d\n", f->fid);
681 	f->mode = work->mode;
682 	f->offset = 0;
683 	rhdr.iounit = getiounit(f->fid);
684 	rhdr.qid = f->f->qid;
685 	reply(work, &rhdr, 0);
686 }
687 
688 void
slaveread(Fsrpc * p)689 slaveread(Fsrpc *p)
690 {
691 	Fid *f;
692 	int n, r;
693 	Fcall *work, rhdr;
694 	char *data, err[ERRMAX];
695 
696 	work = &p->work;
697 
698 	f = getfid(work->fid);
699 	if(f == nil) {
700 		reply(work, &rhdr, Ebadfid);
701 		return;
702 	}
703 
704 	n = (work->count > messagesize-IOHDRSZ) ? messagesize-IOHDRSZ : work->count;
705 	data = malloc(n);
706 	if(data == nil) {
707 		reply(work, &rhdr, Enomem);
708 		return;
709 	}
710 
711 	/* can't just call pread, since directories must update the offset */
712 	if(patternfile != nil && (f->f->qid.type&QTDIR))
713 		r = preaddir(f, (uchar*)data, n, work->offset);
714 	else
715 		r = pread(f->fid, data, n, work->offset);
716 	if(r < 0) {
717 		free(data);
718 		errstr(err, sizeof err);
719 		reply(work, &rhdr, err);
720 		return;
721 	}
722 	DEBUG(DFD, "\tread: fd=%d %d bytes\n", f->fid, r);
723 
724 	rhdr.data = data;
725 	rhdr.count = r;
726 	reply(work, &rhdr, 0);
727 	free(data);
728 }
729 
730 void
slavewrite(Fsrpc * p)731 slavewrite(Fsrpc *p)
732 {
733 	char err[ERRMAX];
734 	Fcall *work, rhdr;
735 	Fid *f;
736 	int n;
737 
738 	work = &p->work;
739 
740 	f = getfid(work->fid);
741 	if(f == nil) {
742 		reply(work, &rhdr, Ebadfid);
743 		return;
744 	}
745 
746 	n = (work->count > messagesize-IOHDRSZ) ? messagesize-IOHDRSZ : work->count;
747 	n = pwrite(f->fid, work->data, n, work->offset);
748 	if(n < 0) {
749 		errstr(err, sizeof err);
750 		reply(work, &rhdr, err);
751 		return;
752 	}
753 
754 	DEBUG(DFD, "\twrite: %d bytes fd=%d\n", n, f->fid);
755 
756 	rhdr.count = n;
757 	reply(work, &rhdr, 0);
758 }
759 
760 void
reopen(Fid * f)761 reopen(Fid *f)
762 {
763 	USED(f);
764 	fatal("reopen");
765 }
766 
767 void
flushaction(void * a,char * cause)768 flushaction(void *a, char *cause)
769 {
770 	USED(a);
771 	if(strncmp(cause, "sys:", 4) == 0 && !strstr(cause, "pipe")) {
772 		fprint(2, "exportsrv: note: %s\n", cause);
773 		exits("noted");
774 	}
775 	if(strncmp(cause, "kill", 4) == 0)
776 		noted(NDFLT);
777 
778 	noted(NCONT);
779 }
780