xref: /plan9-contrib/sys/src/cmd/exportfs/exportfs.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1 /*
2  * exportfs - Export a plan 9 name space across a network
3  */
4 #include <u.h>
5 #include <libc.h>
6 #include <auth.h>
7 #include <fcall.h>
8 #define Extern
9 #include "exportfs.h"
10 
11 void (*fcalls[])(Fsrpc*) =
12 {
13 	[Tnop]		Xnop,
14 	[Tsession]	Xsession,
15 	[Tflush]	Xflush,
16 	[Tattach]	Xattach,
17 	[Tclone]	Xclone,
18 	[Twalk]		Xwalk,
19 	[Topen]		slave,
20 	[Tcreate]	Xcreate,
21 	[Tclunk]	Xclunk,
22 	[Tread]		slave,
23 	[Twrite]	slave,
24 	[Tremove]	Xremove,
25 	[Tstat]		Xstat,
26 	[Twstat]	Xwstat,
27 	[Tclwalk]	Xclwalk,
28 };
29 
30 int nonone;
31 
32 void
33 usage(void)
34 {
35 	fprint(2, "usage: %s [-as] [-c ctlfile]\n", argv0);
36 	fatal("usage");
37 }
38 
39 void
40 main(int argc, char **argv)
41 {
42 	char buf[128];
43 	Fsrpc *r;
44 	int n, srv;
45 	char *dbfile;
46 	char *ctlfile;
47 	char user[NAMELEN];
48 
49 	dbfile = "/tmp/exportdb";
50 	ctlfile = 0;
51 	srv = 0;
52 
53 	ARGBEGIN{
54 	case 'a':
55 		if(srvauth(0, user) < 0)
56 			fatal("srvauth");
57 		if(newns(user, 0) < 0)
58 			fatal("newns");
59 		putenv("service", "exportfs");
60 		break;
61 
62 	case 'c':
63 		ctlfile = ARGF();
64 		break;
65 
66 	case 'd':
67 		dbg++;
68 		break;
69 
70 	case 'f':
71 		dbfile = ARGF();
72 		break;
73 
74 	case 's':
75 		srv++;
76 		break;
77 
78 	default:
79 		usage();
80 	}ARGEND
81 	USED(argc, argv);
82 
83 	if(dbg) {
84 		n = create(dbfile, OWRITE|OTRUNC, 0666);
85 		dup(n, 2);
86 		close(n);
87 	}
88 
89 	DEBUG(2, "exportfs: started\n");
90 
91 	rfork(RFNOTEG);
92 
93 	Workq = malloc(sizeof(Fsrpc)*Nr_workbufs);
94 	fhash = malloc(sizeof(Fid*)*FHASHSIZE);
95 
96 	if(Workq == 0 || fhash == 0)
97 		fatal("no initial memory");
98 
99 	memset(Workq, 0, sizeof(Fsrpc)*Nr_workbufs);
100 
101 	fmtinstall('F', fcallconv);
102 
103 	/*
104 	 * Get tree to serve from network connection,
105 	 * check we can get there and ack the connection
106  	 */
107 	if(srv) {
108 		chdir("/");
109 		DEBUG(2, "invoked as server for /");
110 	}
111 	else {
112 		buf[0] = 0;
113 		n = read(0, buf, sizeof(buf));
114 		if(n < 0) {
115 			errstr(buf);
116 			fprint(0, "read(0): %s", buf);
117 			DEBUG(2, "read(0): %s", buf);
118 			exits(buf);
119 		}
120 		buf[n] = 0;
121 		if(chdir(buf) < 0) {
122 			char ebuf[128];
123 			errstr(ebuf);
124 			fprint(0, "chdir(%d:\"%s\"): %s", n, buf, ebuf);
125 			DEBUG(2, "chdir(%d:\"%s\"): %s", n, buf, ebuf);
126 			exits(ebuf);
127 		}
128 	}
129 
130 	/*
131 	 * take ownership of the network connection and
132 	 * push the fcall line discipline
133 	 */
134 	if(ctlfile)
135 		pushfcall(ctlfile);
136 
137 	DEBUG(2, "initing root\n");
138 	initroot();
139 
140 	DEBUG(2, "exportfs: %s\n", buf);
141 
142 	if(srv == 0 && write(0, "OK", 2) != 2)
143 		fatal("open ack write");
144 
145 	/*
146 	 * Start serving file requests from the network
147 	 */
148 	for(;;) {
149 		r = getsbuf();
150 		if(r == 0)
151 			fatal("Out of service buffers");
152 
153 		do
154 			n = read9p(0, r->buf, sizeof(r->buf));
155 		while(n == 0);
156 
157 		if(n < 0)
158 			fatal("server read");
159 
160 
161 		if(convM2S(r->buf, &r->work, n) == 0)
162 			fatal("format error");
163 
164 		DEBUG(2, "%F\n", &r->work, &r->work);
165 		(fcalls[r->work.type])(r);
166 	}
167 }
168 
169 void
170 reply(Fcall *r, Fcall *t, char *err)
171 {
172 	char data[MAXFDATA+MAXMSG];
173 	int n;
174 
175 	t->tag = r->tag;
176 	t->fid = r->fid;
177 	if(err) {
178 		t->type = Rerror;
179 		strncpy(t->ename, err, ERRLEN);
180 	}
181 	else
182 		t->type = r->type + 1;
183 
184 	DEBUG(2, "\t%F\n", t);
185 
186 	n = convS2M(t, data);
187 	if(write9p(0, data, n)!=n)
188 		fatal("mount write");
189 }
190 
191 Fid *
192 getfid(int nr)
193 {
194 	Fid *f;
195 
196 	for(f = fidhash(nr); f; f = f->next)
197 		if(f->nr == nr)
198 			return f;
199 
200 	return 0;
201 }
202 
203 int
204 freefid(int nr)
205 {
206 	Fid *f, **l;
207 	char buf[128];
208 
209 	l = &fidhash(nr);
210 	for(f = *l; f; f = f->next) {
211 		if(f->nr == nr) {
212 			if(f->mpend)
213 				f->mpend->busy = 0;
214 			if(f->mid) {
215 				sprint(buf, "/mnt/exportfs/%d", f->mid);
216 				unmount(0, buf);
217 				psmap[f->mid] = 0;
218 			}
219 			*l = f->next;
220 			f->next = fidfree;
221 			fidfree = f;
222 			return 1;
223 		}
224 		l = &f->next;
225 	}
226 
227 	return 0;
228 }
229 
230 Fid *
231 newfid(int nr)
232 {
233 	Fid *new, **l;
234 	int i;
235 
236 	l = &fidhash(nr);
237 	for(new = *l; new; new = new->next)
238 		if(new->nr == nr)
239 			return 0;
240 
241 	if(fidfree == 0) {
242 		fidfree = malloc(sizeof(Fid) * Fidchunk);
243 		if(fidfree == 0)
244 			fatal("out of memory");
245 
246 		for(i = 0; i < Fidchunk-1; i++)
247 			fidfree[i].next = &fidfree[i+1];
248 
249 		fidfree[Fidchunk-1].next = 0;
250 	}
251 
252 	new = fidfree;
253 	fidfree = new->next;
254 
255 	memset(new, 0, sizeof(Fid));
256 	new->next = *l;
257 	*l = new;
258 	new->nr = nr;
259 	new->fid = -1;
260 	new->mpend = 0;
261 	new->mid = 0;
262 
263 	return new;
264 }
265 
266 Fsrpc *
267 getsbuf(void)
268 {
269 	static int ap;
270 	int look;
271 	Fsrpc *wb;
272 
273 	for(look = 0; look < Nr_workbufs; look++) {
274 		if(++ap == Nr_workbufs)
275 			ap = 0;
276 		if(Workq[ap].busy == 0)
277 			break;
278 	}
279 
280 	if(look == Nr_workbufs)
281 		fatal("No more work buffers");
282 
283 	wb = &Workq[ap];
284 	wb->pid = 0;
285 	wb->canint = 0;
286 	wb->flushtag = NOTAG;
287 	wb->busy = 1;
288 
289 	return wb;
290 }
291 
292 char *
293 strcatalloc(char *p, char *n)
294 {
295 	char *v;
296 
297 	v = realloc(p, strlen(p)+strlen(n)+1);
298 	if(v == 0)
299 		fatal("no memory");
300 	strcat(v, n);
301 	return v;
302 }
303 
304 File *
305 file(File *parent, char *name)
306 {
307 	Dir dir;
308 	char buf[128];
309 	File *f, *new;
310 
311 	DEBUG(2, "\tfile: 0x%x %s name %s\n", parent, parent->name, name);
312 
313 	for(f = parent->child; f; f = f->childlist)
314 		if(strcmp(name, f->name) == 0)
315 			return f;
316 
317 	makepath(buf, parent, name);
318 	if(dirstat(buf, &dir) < 0)
319 		return 0;
320 
321 	new = malloc(sizeof(File));
322 	if(new == 0)
323 		fatal("no memory");
324 
325 	memset(new, 0, sizeof(File));
326 	strcpy(new->name, name);
327 	new->qid.vers = dir.qid.vers;
328 	new->qid.path = (dir.qid.path&CHDIR)|++qid;
329 
330 	new->parent = parent;
331 	new->childlist = parent->child;
332 	parent->child = new;
333 
334 	return new;
335 }
336 
337 void
338 initroot(void)
339 {
340 	Dir dir;
341 
342 	root = malloc(sizeof(File));
343 	if(root == 0)
344 		fatal("no memory");
345 
346 	memset(root, 0, sizeof(File));
347 	strcpy(root->name, ".");
348 	if(dirstat(root->name, &dir) < 0)
349 		fatal("root stat");
350 
351 	root->qid.vers = dir.qid.vers;
352 	root->qid.path = (dir.qid.path&CHDIR)|++qid;
353 
354 	psmpt = malloc(sizeof(File));
355 	if(psmpt == 0)
356 		fatal("no memory");
357 
358 	memset(psmpt, 0, sizeof(File));
359 	strcpy(psmpt->name, "/");
360 	if(dirstat(psmpt->name, &dir) < 0)
361 		return;
362 
363 	psmpt->qid.vers = dir.qid.vers;
364 	psmpt->qid.path = (dir.qid.path&CHDIR)|++qid;
365 
366 	psmpt = file(psmpt, "mnt");
367 	if(psmpt == 0)
368 		return;
369 	psmpt = file(psmpt, "exportfs");
370 }
371 
372 void
373 makepath(char *s, File *p, char *name)
374 {
375 	int i;
376 	char *c, *seg[256];
377 
378 	seg[0] = name;
379 	for(i = 1; i < 100 && p; i++, p = p->parent)
380 		seg[i] = p->name;
381 
382 	while(i--) {
383 		for(c = seg[i]; *c; c++)
384 			*s++ = *c;
385 		*s++ = '/';
386 	}
387 	while(s[-1] == '/')
388 		s--;
389 	*s = '\0';
390 }
391 
392 void
393 fatal(char *s)
394 {
395 	char buf[128];
396 	Proc *m;
397 
398 	sprint(buf, "exportfs: %r: %s", s);
399 
400 	/* Clear away the slave children */
401 	for(m = Proclist; m; m = m->next)
402 		postnote(PNPROC, m->pid, "kill");
403 
404 	DEBUG(2, "%s\n", buf);
405 	exits(buf);
406 }
407 
408 char pushmsg[] = "push fcall";
409 
410 void
411 pushfcall(char *ctl)
412 {
413 	int cfd;
414 	Dir dir;
415 
416 	if(dirfstat(0, &dir) < 0){
417 		fprint(2, "dirfstat(0) failed: %r\n");
418 		return;
419 	}
420 	memmove(dir.uid, getuser(), NAMELEN);
421 	if(dirfwstat(1, &dir) < 0){
422 		fprint(2, "dirfwstat(1) failed: %r\n");
423 		return;
424 	}
425 	cfd = open(ctl, ORDWR);
426 	if(cfd < 0){
427 		fprint(2, "open(%s0) failed: %r\n", ctl);
428 		return;
429 	}
430 	if(write(cfd, pushmsg, strlen(pushmsg)) < 0){
431 		fprint(2, "%s failed: %r\n", pushmsg);
432 		return;
433 	}
434 	close(cfd);
435 }
436