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