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