xref: /plan9/sys/src/cmd/unix/drawterm/exportfs/exportfs.c (revision 58da3067adcdccaaa043d0bfde28ba83b7ced07d)
1 /*
2  * exportfs - Export a plan 9 name space across a network
3  */
4 #include <u.h>
5 #include <libc.h>
6 #include <fcall.h>
7 #include <libsec.h>
8 #include "drawterm.h"
9 #define Extern
10 #include "exportfs.h"
11 
12 /* #define QIDPATH	((1LL<<48)-1) */
13 #define QIDPATH	((((vlong)1)<<48)-1)
14 vlong newqid = 0;
15 
16 void (*fcalls[256])(Fsrpc*);
17 
18 /* accounting and debugging counters */
19 int	filecnt;
20 int	freecnt;
21 int	qidcnt;
22 int	qfreecnt;
23 int	ncollision;
24 int	netfd;
25 
26 int
exportfs(int fd,int msgsz)27 exportfs(int fd, int msgsz)
28 {
29 	char buf[ERRMAX], ebuf[ERRMAX];
30 	Fsrpc *r;
31 	int i, n;
32 
33 	fcalls[Tversion] = Xversion;
34 	fcalls[Tauth] = Xauth;
35 	fcalls[Tflush] = Xflush;
36 	fcalls[Tattach] = Xattach;
37 	fcalls[Twalk] = Xwalk;
38 	fcalls[Topen] = slave;
39 	fcalls[Tcreate] = Xcreate;
40 	fcalls[Tclunk] = Xclunk;
41 	fcalls[Tread] = slave;
42 	fcalls[Twrite] = slave;
43 	fcalls[Tremove] = Xremove;
44 	fcalls[Tstat] = Xstat;
45 	fcalls[Twstat] = Xwstat;
46 
47 	srvfd = -1;
48 	netfd = fd;
49 	//dbg = 1;
50 
51 	strcpy(buf, "this is buf");
52 	strcpy(ebuf, "this is ebuf");
53 	DEBUG(DFD, "exportfs: started\n");
54 
55 //	rfork(RFNOTEG);
56 
57 	messagesize = msgsz;
58 	if(messagesize == 0){
59 		messagesize = iounit(netfd);
60 		if(messagesize == 0)
61 			messagesize = 8*8192+IOHDRSZ;
62 	}
63 
64 	Workq = emallocz(sizeof(Fsrpc)*Nr_workbufs);
65 //	for(i=0; i<Nr_workbufs; i++)
66 //		Workq[i].buf = emallocz(messagesize);
67 	fhash = emallocz(sizeof(Fid*)*FHASHSIZE);
68 
69 	fmtinstall('F', fcallfmt);
70 
71 	initroot();
72 
73 	DEBUG(DFD, "exportfs: %s\n", buf);
74 
75 	/*
76 	 * Start serving file requests from the network
77 	 */
78 	for(;;) {
79 		r = getsbuf();
80 		if(r == 0)
81 			fatal("Out of service buffers");
82 
83 		DEBUG(DFD, "read9p...");
84 		n = read9pmsg(netfd, r->buf, messagesize);
85 		if(n <= 0)
86 			fatal("eof: n=%d %r", n);
87 
88 		if(convM2S(r->buf, n, &r->work) == 0){
89 			iprint("convM2S %d byte message\n", n);
90 			for(i=0; i<n; i++){
91 				iprint(" %.2ux", r->buf[i]);
92 				if(i%16 == 15)
93 					iprint("\n");
94 			}
95 			if(i%16)
96 				iprint("\n");
97 			fatal("convM2S format error");
98 		}
99 
100 if(0) iprint("<- %F\n", &r->work);
101 		DEBUG(DFD, "%F\n", &r->work);
102 		(fcalls[r->work.type])(r);
103 	}
104 }
105 
106 void
reply(Fcall * r,Fcall * t,char * err)107 reply(Fcall *r, Fcall *t, char *err)
108 {
109 	uchar *data;
110 	int m, n;
111 
112 	t->tag = r->tag;
113 	t->fid = r->fid;
114 	if(err) {
115 		t->type = Rerror;
116 		t->ename = err;
117 	}
118 	else
119 		t->type = r->type + 1;
120 
121 if(0) iprint("-> %F\n", t);
122 	DEBUG(DFD, "\t%F\n", t);
123 
124 	data = malloc(messagesize);	/* not mallocz; no need to clear */
125 	if(data == nil)
126 		fatal(Enomem);
127 	n = convS2M(t, data, messagesize);
128 	if((m=write(netfd, data, n))!=n){
129 		iprint("wrote %d got %d (%r)\n", n, m);
130 		fatal("write");
131 	}
132 	free(data);
133 }
134 
135 Fid *
getfid(int nr)136 getfid(int nr)
137 {
138 	Fid *f;
139 
140 	for(f = fidhash(nr); f; f = f->next)
141 		if(f->nr == nr)
142 			return f;
143 
144 	return 0;
145 }
146 
147 int
freefid(int nr)148 freefid(int nr)
149 {
150 	Fid *f, **l;
151 	char buf[128];
152 
153 	l = &fidhash(nr);
154 	for(f = *l; f; f = f->next) {
155 		if(f->nr == nr) {
156 			if(f->mid) {
157 				sprint(buf, "/mnt/exportfs/%d", f->mid);
158 				unmount(0, buf);
159 				psmap[f->mid] = 0;
160 			}
161 			if(f->f) {
162 				freefile(f->f);
163 				f->f = nil;
164 			}
165 			*l = f->next;
166 			f->next = fidfree;
167 			fidfree = f;
168 			return 1;
169 		}
170 		l = &f->next;
171 	}
172 
173 	return 0;
174 }
175 
176 Fid *
newfid(int nr)177 newfid(int nr)
178 {
179 	Fid *new, **l;
180 	int i;
181 
182 	l = &fidhash(nr);
183 	for(new = *l; new; new = new->next)
184 		if(new->nr == nr)
185 			return 0;
186 
187 	if(fidfree == 0) {
188 		fidfree = emallocz(sizeof(Fid) * Fidchunk);
189 
190 		for(i = 0; i < Fidchunk-1; i++)
191 			fidfree[i].next = &fidfree[i+1];
192 
193 		fidfree[Fidchunk-1].next = 0;
194 	}
195 
196 	new = fidfree;
197 	fidfree = new->next;
198 
199 	memset(new, 0, sizeof(Fid));
200 	new->next = *l;
201 	*l = new;
202 	new->nr = nr;
203 	new->fid = -1;
204 	new->mid = 0;
205 
206 	return new;
207 }
208 
209 Fsrpc *
getsbuf(void)210 getsbuf(void)
211 {
212 	static int ap;
213 	int look, rounds;
214 	Fsrpc *wb;
215 	int small_instead_of_fast = 1;
216 
217 	if(small_instead_of_fast)
218 		ap = 0;	/* so we always start looking at the beginning and reuse buffers */
219 
220 	for(rounds = 0; rounds < 10; rounds++) {
221 		for(look = 0; look < Nr_workbufs; look++) {
222 			if(++ap == Nr_workbufs)
223 				ap = 0;
224 			if(Workq[ap].busy == 0)
225 				break;
226 		}
227 
228 		if(look == Nr_workbufs){
229 			sleep(10 * rounds);
230 			continue;
231 		}
232 
233 		wb = &Workq[ap];
234 		wb->pid = 0;
235 		wb->canint = 0;
236 		wb->flushtag = NOTAG;
237 		wb->busy = 1;
238 		if(wb->buf == nil)	/* allocate buffers dynamically to keep size down */
239 			wb->buf = emallocz(messagesize);
240 		return wb;
241 	}
242 	fatal("No more work buffers");
243 	return nil;
244 }
245 
246 void
freefile(File * f)247 freefile(File *f)
248 {
249 	File *parent, *child;
250 
251 Loop:
252 	f->ref--;
253 	if(f->ref > 0)
254 		return;
255 	freecnt++;
256 	if(f->ref < 0) abort();
257 	DEBUG(DFD, "free %s\n", f->name);
258 	/* delete from parent */
259 	parent = f->parent;
260 	if(parent->child == f)
261 		parent->child = f->childlist;
262 	else{
263 		for(child=parent->child; child->childlist!=f; child=child->childlist)
264 			if(child->childlist == nil)
265 				fatal("bad child list");
266 		child->childlist = f->childlist;
267 	}
268 	freeqid(f->qidt);
269 	free(f->name);
270 	f->name = nil;
271 	free(f);
272 	f = parent;
273 	if(f != nil)
274 		goto Loop;
275 }
276 
277 File *
file(File * parent,char * name)278 file(File *parent, char *name)
279 {
280 	Dir *dir;
281 	char *path;
282 	File *f;
283 
284 	DEBUG(DFD, "\tfile: 0x%p %s name %s\n", parent, parent->name, name);
285 
286 	path = makepath(parent, name);
287 	dir = dirstat(path);
288 	free(path);
289 	if(dir == nil)
290 		return nil;
291 
292 	for(f = parent->child; f; f = f->childlist)
293 		if(strcmp(name, f->name) == 0)
294 			break;
295 
296 	if(f == nil){
297 		f = emallocz(sizeof(File));
298 		f->name = estrdup(name);
299 
300 		f->parent = parent;
301 		f->childlist = parent->child;
302 		parent->child = f;
303 		parent->ref++;
304 		f->ref = 0;
305 		filecnt++;
306 	}
307 	f->ref++;
308 	f->qid.type = dir->qid.type;
309 	f->qid.vers = dir->qid.vers;
310 	f->qidt = uniqueqid(dir);
311 	f->qid.path = f->qidt->uniqpath;
312 
313 	f->inval = 0;
314 
315 	free(dir);
316 
317 	return f;
318 }
319 
320 void
initroot(void)321 initroot(void)
322 {
323 	Dir *dir;
324 
325 	root = emallocz(sizeof(File));
326 	root->name = estrdup(".");
327 
328 	dir = dirstat(root->name);
329 	if(dir == nil)
330 		fatal("root stat");
331 
332 	root->ref = 1;
333 	root->qid.vers = dir->qid.vers;
334 	root->qidt = uniqueqid(dir);
335 	root->qid.path = root->qidt->uniqpath;
336 	root->qid.type = QTDIR;
337 	free(dir);
338 
339 	psmpt = emallocz(sizeof(File));
340 	psmpt->name = estrdup("/");
341 
342 	dir = dirstat(psmpt->name);
343 	if(dir == nil)
344 		return;
345 
346 	psmpt->ref = 1;
347 	psmpt->qid.vers = dir->qid.vers;
348 	psmpt->qidt = uniqueqid(dir);
349 	psmpt->qid.path = psmpt->qidt->uniqpath;
350 	free(dir);
351 
352 	psmpt = file(psmpt, "mnt");
353 	if(psmpt == 0)
354 		return;
355 	psmpt = file(psmpt, "exportfs");
356 }
357 
358 char*
makepath(File * p,char * name)359 makepath(File *p, char *name)
360 {
361 	int i, n;
362 	char *c, *s, *path, *seg[256];
363 
364 	seg[0] = name;
365 	n = strlen(name)+2;
366 	for(i = 1; i < 256 && p; i++, p = p->parent){
367 		seg[i] = p->name;
368 		n += strlen(p->name)+1;
369 	}
370 	path = malloc(n);
371 	if(path == nil)
372 		fatal("out of memory");
373 	s = path;
374 
375 	while(i--) {
376 		for(c = seg[i]; *c; c++)
377 			*s++ = *c;
378 		*s++ = '/';
379 	}
380 	while(s[-1] == '/')
381 		s--;
382 	*s = '\0';
383 
384 	return path;
385 }
386 
387 int
qidhash(vlong path)388 qidhash(vlong path)
389 {
390 	int h, n;
391 
392 	h = 0;
393 	for(n=0; n<64; n+=Nqidbits){
394 		h ^= path;
395 		path >>= Nqidbits;
396 	}
397 	return h & (Nqidtab-1);
398 }
399 
400 void
freeqid(Qidtab * q)401 freeqid(Qidtab *q)
402 {
403 	ulong h;
404 	Qidtab *l;
405 
406 	q->ref--;
407 	if(q->ref > 0)
408 		return;
409 	qfreecnt++;
410 	h = qidhash(q->path);
411 	if(qidtab[h] == q)
412 		qidtab[h] = q->next;
413 	else{
414 		for(l=qidtab[h]; l->next!=q; l=l->next)
415 			if(l->next == nil)
416 				fatal("bad qid list");
417 		l->next = q->next;
418 	}
419 	free(q);
420 }
421 
422 Qidtab*
qidlookup(Dir * d)423 qidlookup(Dir *d)
424 {
425 	ulong h;
426 	Qidtab *q;
427 
428 	h = qidhash(d->qid.path);
429 	for(q=qidtab[h]; q!=nil; q=q->next)
430 		if(q->type==d->type && q->dev==d->dev && q->path==d->qid.path)
431 			return q;
432 	return nil;
433 }
434 
435 int
qidexists(vlong path)436 qidexists(vlong path)
437 {
438 	int h;
439 	Qidtab *q;
440 
441 	for(h=0; h<Nqidtab; h++)
442 		for(q=qidtab[h]; q!=nil; q=q->next)
443 			if(q->uniqpath == path)
444 				return 1;
445 	return 0;
446 }
447 
448 Qidtab*
uniqueqid(Dir * d)449 uniqueqid(Dir *d)
450 {
451 	ulong h;
452 	vlong path;
453 	Qidtab *q;
454 
455 	q = qidlookup(d);
456 	if(q != nil){
457 		q->ref++;
458 		return q;
459 	}
460 	path = d->qid.path;
461 	while(qidexists(path)){
462 		DEBUG(DFD, "collision on %s\n", d->name);
463 		/* collision: find a new one */
464 		ncollision++;
465 		path &= QIDPATH;
466 		++newqid;
467 		if(newqid >= (1<<16)){
468 			DEBUG(DFD, "collision wraparound\n");
469 			newqid = 1;
470 		}
471 		path |= newqid<<48;
472 		DEBUG(DFD, "assign qid %.16llux\n", path);
473 	}
474 	q = mallocz(sizeof(Qidtab), 1);
475 	if(q == nil)
476 		fatal("no memory for qid table");
477 	qidcnt++;
478 	q->ref = 1;
479 	q->type = d->type;
480 	q->dev = d->dev;
481 	q->path = d->qid.path;
482 	q->uniqpath = path;
483 	h = qidhash(d->qid.path);
484 	q->next = qidtab[h];
485 	qidtab[h] = q;
486 	return q;
487 }
488 
489 void
fatal(char * s,...)490 fatal(char *s, ...)
491 {
492 	char buf[ERRMAX];
493 	va_list arg;
494 
495 	if (s) {
496 		va_start(arg, s);
497 		vsnprint(buf, ERRMAX, s, arg);
498 		va_end(arg);
499 	}
500 
501 	/* Clear away the slave children */
502 //	for(m = Proclist; m; m = m->next)
503 //		postnote(PNPROC, m->pid, "kill");
504 
505 	DEBUG(DFD, "%s\n", buf);
506 	if (s)
507 		sysfatal(buf);
508 	else
509 		sysfatal("");
510 }
511 
512