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