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