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