xref: /plan9-contrib/sys/src/cmd/exportfs/exportfs.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
1 
2 /*
3  * exportfs - Export a plan 9 name space across a network
4  */
5 #include <u.h>
6 #include <libc.h>
7 #include <auth.h>
8 #include <fcall.h>
9 #define Extern
10 #include "exportfs.h"
11 
12 #define	QIDMODE	(CHDIR|CHAPPEND|CHEXCL|CHMOUNT)
13 ulong newqid = ~QIDMODE;
14 
15 void (*fcalls[])(Fsrpc*) =
16 {
17 	[Tnop]		Xnop,
18 	[Tsession]	Xsession,
19 	[Tflush]	Xflush,
20 	[Tattach]	Xattach,
21 	[Tclone]	Xclone,
22 	[Twalk]		Xwalk,
23 	[Topen]		slave,
24 	[Tcreate]	Xcreate,
25 	[Tclunk]	Xclunk,
26 	[Tread]		slave,
27 	[Twrite]	slave,
28 	[Tremove]	Xremove,
29 	[Tstat]		Xstat,
30 	[Twstat]	Xwstat,
31 	[Tclwalk]	Xclwalk,
32 };
33 
34 /* accounting and debugging counters */
35 int	filecnt;
36 int	freecnt;
37 int	qidcnt;
38 int	qfreecnt;
39 int	ncollision;
40 
41 int	fflag;
42 int	netfd;
43 int	filter(int);
44 
45 void
46 usage(void)
47 {
48 	fprint(2, "usage: %s [-as] [-c ctlfile]\n", argv0);
49 	fatal("usage");
50 }
51 
52 void
53 main(int argc, char **argv)
54 {
55 	char buf[128];
56 	Fsrpc *r;
57 	int n, srv;
58 	char *dbfile;
59 	char user[NAMELEN];
60 
61 	dbfile = "/tmp/exportdb";
62 	srv = 0;
63 
64 	ARGBEGIN{
65 	case 'a':
66 	//	fprint(2, "srvauth\n");
67 		if(srvauth(0, user) < 0)
68 			fatal("srvauth");
69 	//	fprint(2, "newns\n");
70 		if(newns(user, 0) < 0)
71 			fatal("newns");
72 		putenv("service", "exportfs");
73 		break;
74 
75 	case 'd':
76 		dbg++;
77 		break;
78 
79 	case 'f':
80 		dbfile = ARGF();
81 		break;
82 
83 	case 'F':
84 		fflag++;
85 		break;
86 
87 	case 's':
88 		srv++;
89 		break;
90 
91 	default:
92 		usage();
93 	}ARGEND
94 	USED(argc, argv);
95 
96 	if(dbg) {
97 		n = create(dbfile, OWRITE|OTRUNC, 0666);
98 		dup(n, DFD);
99 		close(n);
100 	}
101 
102 	DEBUG(DFD, "exportfs: started\n");
103 
104 	rfork(RFNOTEG);
105 
106 	Workq = mallocz(sizeof(Fsrpc)*Nr_workbufs, 1);
107 	fhash = mallocz(sizeof(Fid*)*FHASHSIZE, 1);
108 
109 	if(Workq == 0 || fhash == 0)
110 		fatal("no initial memory");
111 
112 
113 	fmtinstall('F', fcallconv);
114 
115 	/*
116 	 * Get tree to serve from network connection,
117 	 * check we can get there and ack the connection
118  	 */
119 	if(srv) {
120 		chdir("/");
121 		DEBUG(DFD, "invoked as server for /");
122 	}
123 	else {
124 		buf[0] = 0;
125 		n = read(0, buf, sizeof(buf));
126 		if(n < 0) {
127 			errstr(buf);
128 			fprint(0, "read(0): %s", buf);
129 			DEBUG(DFD, "read(0): %s", buf);
130 			exits(buf);
131 		}
132 		buf[n] = 0;
133 		if(chdir(buf) < 0) {
134 			char ebuf[128];
135 			errstr(ebuf);
136 			fprint(0, "chdir(%d:\"%s\"): %s", n, buf, ebuf);
137 			DEBUG(DFD, "chdir(%d:\"%s\"): %s", n, buf, ebuf);
138 			exits(ebuf);
139 		}
140 	}
141 
142 	DEBUG(DFD, "initing root\n");
143 	initroot();
144 
145 	DEBUG(DFD, "exportfs: %s\n", buf);
146 
147 	if(srv == 0 && write(0, "OK", 2) != 2)
148 		fatal("open ack write");
149 
150 	/*
151 	 * push the fcall line discipline
152 	 */
153 	netfd = 0;
154 	if(fflag)
155 		netfd = filter(netfd);
156 
157 	/*
158 	 * Start serving file requests from the network
159 	 */
160 	for(;;) {
161 		r = getsbuf();
162 		if(r == 0)
163 			fatal("Out of service buffers");
164 
165 		do
166 			n = read9p(netfd, r->buf, sizeof(r->buf));
167 		while(n == 0);
168 
169 		if(n < 0)
170 			fatal("server read");
171 
172 
173 		if(convM2S(r->buf, &r->work, n) == 0)
174 			fatal("format error");
175 
176 		DEBUG(DFD, "%F\n", &r->work);
177 		(fcalls[r->work.type])(r);
178 	}
179 }
180 
181 void
182 reply(Fcall *r, Fcall *t, char *err)
183 {
184 	char data[MAXFDATA+MAXMSG];
185 	int n;
186 
187 	t->tag = r->tag;
188 	t->fid = r->fid;
189 	if(err) {
190 		t->type = Rerror;
191 		strncpy(t->ename, err, ERRLEN);
192 	}
193 	else
194 		t->type = r->type + 1;
195 
196 	DEBUG(DFD, "\t%F\n", t);
197 
198 	n = convS2M(t, data);
199 	if(write9p(netfd, data, n)!=n)
200 		fatal("mount write");
201 }
202 
203 Fid *
204 getfid(int nr)
205 {
206 	Fid *f;
207 
208 	for(f = fidhash(nr); f; f = f->next)
209 		if(f->nr == nr)
210 			return f;
211 
212 	return 0;
213 }
214 
215 int
216 freefid(int nr)
217 {
218 	Fid *f, **l;
219 	char buf[128];
220 
221 	l = &fidhash(nr);
222 	for(f = *l; f; f = f->next) {
223 		if(f->nr == nr) {
224 			if(f->mpend)
225 				f->mpend->busy = 0;
226 			if(f->mid) {
227 				sprint(buf, "/mnt/exportfs/%d", f->mid);
228 				unmount(0, buf);
229 				psmap[f->mid] = 0;
230 			}
231 			freefile(f->f);
232 			f->f = nil;
233 			*l = f->next;
234 			f->next = fidfree;
235 			fidfree = f;
236 			return 1;
237 		}
238 		l = &f->next;
239 	}
240 
241 	return 0;
242 }
243 
244 Fid *
245 newfid(int nr)
246 {
247 	Fid *new, **l;
248 	int i;
249 
250 	l = &fidhash(nr);
251 	for(new = *l; new; new = new->next)
252 		if(new->nr == nr)
253 			return 0;
254 
255 	if(fidfree == 0) {
256 		fidfree = mallocz(sizeof(Fid) * Fidchunk, 1);
257 		if(fidfree == 0)
258 			fatal("out of memory");
259 
260 		for(i = 0; i < Fidchunk-1; i++)
261 			fidfree[i].next = &fidfree[i+1];
262 
263 		fidfree[Fidchunk-1].next = 0;
264 	}
265 
266 	new = fidfree;
267 	fidfree = new->next;
268 
269 	memset(new, 0, sizeof(Fid));
270 	new->next = *l;
271 	*l = new;
272 	new->nr = nr;
273 	new->fid = -1;
274 	new->mpend = 0;
275 	new->mid = 0;
276 
277 	return new;
278 }
279 
280 Fsrpc *
281 getsbuf(void)
282 {
283 	static int ap;
284 	int look, rounds;
285 	Fsrpc *wb;
286 
287 	for(rounds = 0; rounds < 10; rounds++) {
288 		for(look = 0; look < Nr_workbufs; look++) {
289 			if(++ap == Nr_workbufs)
290 				ap = 0;
291 			if(Workq[ap].busy == 0)
292 				break;
293 		}
294 
295 		if(look == Nr_workbufs){
296 			sleep(10 * rounds);
297 			continue;
298 		}
299 
300 		wb = &Workq[ap];
301 		wb->pid = 0;
302 		wb->canint = 0;
303 		wb->flushtag = NOTAG;
304 		wb->busy = 1;
305 
306 		return wb;
307 	}
308 	fatal("No more work buffers");
309 	return nil;
310 }
311 
312 void
313 freefile(File *f)
314 {
315 	File *parent, *child;
316 
317 Loop:
318 	f->ref--;
319 	if(f->ref > 0)
320 		return;
321 	freecnt++;
322 	if(f->ref < 0) abort();
323 	DEBUG(DFD, "free %s\n", f->name);
324 	/* delete from parent */
325 	parent = f->parent;
326 	if(parent->child == f)
327 		parent->child = f->childlist;
328 	else{
329 		for(child=parent->child; child->childlist!=f; child=child->childlist)
330 			if(child->childlist == nil)
331 				fatal("bad child list");
332 		child->childlist = f->childlist;
333 	}
334 	freeqid(f->qidt);
335 	free(f);
336 	f = parent;
337 	if(f != nil)
338 		goto Loop;
339 }
340 
341 File *
342 file(File *parent, char *name)
343 {
344 	Dir dir;
345 	char buf[128];
346 	File *f;
347 
348 	DEBUG(DFD, "\tfile: 0x%p %s name %s\n", parent, parent->name, name);
349 
350 	makepath(buf, parent, name);
351 	if(dirstat(buf, &dir) < 0)
352 		return nil;
353 
354 	for(f = parent->child; f; f = f->childlist)
355 		if(strcmp(name, f->name) == 0)
356 			break;
357 
358 	if(f == nil){
359 		f = mallocz(sizeof(File), 1);
360 		if(f == 0)
361 			fatal("no memory");
362 		strcpy(f->name, name);
363 
364 		f->parent = parent;
365 		f->childlist = parent->child;
366 		parent->child = f;
367 		parent->ref++;
368 		f->ref = 0;
369 		filecnt++;
370 	}
371 	f->ref++;
372 	f->qid.vers = dir.qid.vers;
373 	f->qidt = uniqueqid(&dir);
374 	f->qid.path = f->qidt->uniqpath;
375 
376 	f->inval = 0;
377 
378 
379 	return f;
380 }
381 
382 void
383 initroot(void)
384 {
385 	Dir dir;
386 
387 	root = mallocz(sizeof(File), 1);
388 	if(root == 0)
389 		fatal("no memory");
390 
391 	strcpy(root->name, ".");
392 	if(dirstat(root->name, &dir) < 0)
393 		fatal("root stat");
394 
395 	root->ref = 1;
396 	root->qid.vers = dir.qid.vers;
397 	root->qidt = uniqueqid(&dir);
398 	root->qid.path = root->qidt->uniqpath;
399 
400 	psmpt = mallocz(sizeof(File), 1);
401 	if(psmpt == 0)
402 		fatal("no memory");
403 
404 	strcpy(psmpt->name, "/");
405 	if(dirstat(psmpt->name, &dir) < 0)
406 		return;
407 
408 	psmpt->ref = 1;
409 	psmpt->qid.vers = dir.qid.vers;
410 	psmpt->qidt = uniqueqid(&dir);
411 	psmpt->qid.path = psmpt->qidt->uniqpath;
412 
413 	psmpt = file(psmpt, "mnt");
414 	if(psmpt == 0)
415 		return;
416 	psmpt = file(psmpt, "exportfs");
417 }
418 
419 void
420 makepath(char *s, File *p, char *name)
421 {
422 	int i;
423 	char *c, *seg[256];
424 
425 	seg[0] = name;
426 	for(i = 1; i < 100 && p; i++, p = p->parent)
427 		seg[i] = p->name;
428 
429 	while(i--) {
430 		for(c = seg[i]; *c; c++)
431 			*s++ = *c;
432 		*s++ = '/';
433 	}
434 	while(s[-1] == '/')
435 		s--;
436 	*s = '\0';
437 }
438 
439 int
440 qidhash(ulong path)
441 {
442 	int h, n;
443 
444 	h = 0;
445 	for(n=0; n<32; n+=Nqidbits){
446 		h ^= path;
447 		path >>= Nqidbits;
448 	}
449 	return h & (Nqidtab-1);
450 }
451 
452 void
453 freeqid(Qidtab *q)
454 {
455 	ulong h;
456 	Qidtab *l;
457 
458 	q->ref--;
459 	if(q->ref > 0)
460 		return;
461 	qfreecnt++;
462 	h = qidhash(q->path);
463 	if(qidtab[h] == q)
464 		qidtab[h] = q->next;
465 	else{
466 		for(l=qidtab[h]; l->next!=q; l=l->next)
467 			if(l->next == nil)
468 				fatal("bad qid list");
469 		l->next = q->next;
470 	}
471 	free(q);
472 }
473 
474 Qidtab*
475 qidlookup(Dir *d)
476 {
477 	ulong h;
478 	Qidtab *q;
479 
480 	h = qidhash(d->qid.path);
481 	for(q=qidtab[h]; q!=nil; q=q->next)
482 		if(q->type==d->type && q->dev==d->dev && q->path==d->qid.path)
483 			return q;
484 	return nil;
485 }
486 
487 int
488 qidexists(ulong path)
489 {
490 	int h;
491 	Qidtab *q;
492 
493 	for(h=0; h<Nqidtab; h++)
494 		for(q=qidtab[h]; q!=nil; q=q->next)
495 			if(q->uniqpath == path)
496 				return 1;
497 	return 0;
498 }
499 
500 Qidtab*
501 uniqueqid(Dir *d)
502 {
503 	ulong h, path;
504 	Qidtab *q;
505 
506 	q = qidlookup(d);
507 	if(q != nil){
508 		q->ref++;
509 		return q;
510 	}
511 	path = d->qid.path;
512 	while(qidexists(path)){
513 		/* collision: find a new one */
514 		ncollision++;
515 		DEBUG(DFD, "collision on %s\n", d->name);
516 		path = newqid--;
517 		if(newqid == 0)
518 			newqid = ~QIDMODE;
519 		path |= d->qid.path & (CHDIR|CHAPPEND|CHEXCL|CHMOUNT);
520 	}
521 	q = mallocz(sizeof(Qidtab), 1);
522 	if(q == nil)
523 		fatal("no memory for qid table");
524 	qidcnt++;
525 	q->ref = 1;
526 	q->type = d->type;
527 	q->dev = d->dev;
528 	q->path = d->qid.path;
529 	q->uniqpath = path;
530 	h = qidhash(d->qid.path);
531 	q->next = qidtab[h];
532 	qidtab[h] = q;
533 	return q;
534 }
535 
536 void
537 fatal(char *s)
538 {
539 	char buf[128];
540 	Proc *m;
541 
542 	sprint(buf, "exportfs: %r: %s", s);
543 
544 	/* Clear away the slave children */
545 	for(m = Proclist; m; m = m->next)
546 		postnote(PNPROC, m->pid, "kill");
547 
548 	DEBUG(DFD, "%s\n", buf);
549 	exits(buf);
550 }
551 
552 /* Network on fd1, mount driver on fd0 */
553 int
554 filter(int fd)
555 {
556 	int p[2];
557 
558 	if(pipe(p) < 0)
559 		fatal("pipe");
560 
561 	switch(rfork(RFNOWAIT|RFPROC|RFFDG)) {
562 	case -1:
563 		fatal("rfork record module");
564 	case 0:
565 		dup(fd, 1);
566 		close(fd);
567 		dup(p[0], 0);
568 		close(p[0]);
569 		close(p[1]);
570 		execl("/bin/aux/fcall", "fcall", 0);
571 		fatal("exec record module");
572 	default:
573 		close(fd);
574 		close(p[0]);
575 	}
576 	return p[1];
577 }
578