1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <thread.h>
5 #include <cursor.h>
6 #include <mouse.h>
7 #include <keyboard.h>
8 #include <frame.h>
9 #include <fcall.h>
10 #include <plumb.h>
11 #include <libsec.h>
12 #include "dat.h"
13 #include "fns.h"
14 
15 static	int	sfd;
16 
17 enum
18 {
19 	Nhash	= 16,
20 	DEBUG	= 0
21 };
22 
23 static	Fid	*fids[Nhash];
24 
25 Fid	*newfid(int);
26 
27 static	Xfid*	fsysflush(Xfid*, Fid*);
28 static	Xfid*	fsysauth(Xfid*, Fid*);
29 static	Xfid*	fsysversion(Xfid*, Fid*);
30 static	Xfid*	fsysattach(Xfid*, Fid*);
31 static	Xfid*	fsyswalk(Xfid*, Fid*);
32 static	Xfid*	fsysopen(Xfid*, Fid*);
33 static	Xfid*	fsyscreate(Xfid*, Fid*);
34 static	Xfid*	fsysread(Xfid*, Fid*);
35 static	Xfid*	fsyswrite(Xfid*, Fid*);
36 static	Xfid*	fsysclunk(Xfid*, Fid*);
37 static	Xfid*	fsysremove(Xfid*, Fid*);
38 static	Xfid*	fsysstat(Xfid*, Fid*);
39 static	Xfid*	fsyswstat(Xfid*, Fid*);
40 
41 Xfid* 	(*fcall[Tmax])(Xfid*, Fid*);
42 
43 static void
initfcall(void)44 initfcall(void)
45 {
46 	fcall[Tflush]	= fsysflush;
47 	fcall[Tversion]	= fsysversion;
48 	fcall[Tauth]	= fsysauth;
49 	fcall[Tattach]	= fsysattach;
50 	fcall[Twalk]	= fsyswalk;
51 	fcall[Topen]	= fsysopen;
52 	fcall[Tcreate]	= fsyscreate;
53 	fcall[Tread]	= fsysread;
54 	fcall[Twrite]	= fsyswrite;
55 	fcall[Tclunk]	= fsysclunk;
56 	fcall[Tremove]= fsysremove;
57 	fcall[Tstat]	= fsysstat;
58 	fcall[Twstat]	= fsyswstat;
59 }
60 
61 char Eperm[] = "permission denied";
62 char Eexist[] = "file does not exist";
63 char Enotdir[] = "not a directory";
64 
65 Dirtab dirtab[]=
66 {
67 	{ ".",			QTDIR,	Qdir,		0500|DMDIR },
68 	{ "acme",		QTDIR,	Qacme,	0500|DMDIR },
69 	{ "cons",		QTFILE,	Qcons,	0600 },
70 	{ "consctl",	QTFILE,	Qconsctl,	0000 },
71 	{ "draw",		QTDIR,	Qdraw,	0000|DMDIR },	/* to suppress graphics progs started in acme */
72 	{ "editout",	QTFILE,	Qeditout,	0200 },
73 	{ "index",		QTFILE,	Qindex,	0400 },
74 	{ "label",		QTFILE,	Qlabel,	0600 },
75 	{ "log",		QTFILE,	Qlog,	0400 },
76 	{ "new",		QTDIR,	Qnew,	0500|DMDIR },
77 	{ nil, }
78 };
79 
80 Dirtab dirtabw[]=
81 {
82 	{ ".",			QTDIR,		Qdir,			0500|DMDIR },
83 	{ "addr",		QTFILE,		QWaddr,		0600 },
84 	{ "body",		QTAPPEND,	QWbody,		0600|DMAPPEND },
85 	{ "ctl",		QTFILE,		QWctl,		0600 },
86 	{ "data",		QTFILE,		QWdata,		0600 },
87 	{ "editout",	QTFILE,		QWeditout,	0200 },
88 	{ "errors",		QTFILE,		QWerrors,		0200 },
89 	{ "event",		QTFILE,		QWevent,		0600 },
90 	{ "rdsel",		QTFILE,		QWrdsel,		0400 },
91 	{ "wrsel",		QTFILE,		QWwrsel,		0200 },
92 	{ "tag",		QTAPPEND,	QWtag,		0600|DMAPPEND },
93 	{ "xdata",		QTFILE,		QWxdata,		0600 },
94 	{ nil, }
95 };
96 
97 typedef struct Mnt Mnt;
98 struct Mnt
99 {
100 	QLock	lk;
101 	int		id;
102 	Mntdir	*md;
103 };
104 
105 Mnt	mnt;
106 
107 Xfid*	respond(Xfid*, Fcall*, char*);
108 int		dostat(int, Dirtab*, uchar*, int, uint);
109 uint	getclock(void);
110 
111 char	*user = "Wile E. Coyote";
112 static int closing = 0;
113 int	messagesize = Maxblock+IOHDRSZ;	/* good start */
114 
115 void	fsysproc(void *);
116 
117 void
fsysinit(void)118 fsysinit(void)
119 {
120 	int p[2];
121 	char *u;
122 
123 	initfcall();
124 	if(pipe(p) < 0)
125 		error("can't create pipe");
126 	if(post9pservice(p[0], "acme", mtpt) < 0)
127 		error("can't post service");
128 	sfd = p[1];
129 	fmtinstall('F', fcallfmt);
130 	if((u = getuser()) != nil)
131 		user = estrdup(u);
132 	proccreate(fsysproc, nil, STACK);
133 }
134 
135 void
fsysproc(void * v)136 fsysproc(void *v)
137 {
138 	int n;
139 	Xfid *x;
140 	Fid *f;
141 	Fcall t;
142 	uchar *buf;
143 
144 	threadsetname("fsysproc");
145 
146 	USED(v);
147 	x = nil;
148 	for(;;){
149 		buf = emalloc(messagesize+UTFmax);	/* overflow for appending partial rune in xfidwrite */
150 		n = read9pmsg(sfd, buf, messagesize);
151 		if(n <= 0){
152 			if(closing)
153 				break;
154 			error("i/o error on server channel");
155 		}
156 		if(x == nil){
157 			sendp(cxfidalloc, nil);
158 			x = recvp(cxfidalloc);
159 		}
160 		x->buf = buf;
161 		if(convM2S(buf, n, &x->fcall) != n)
162 			error("convert error in convM2S");
163 		if(DEBUG)
164 			fprint(2, "%F\n", &x->fcall);
165 		if(fcall[x->fcall.type] == nil)
166 			x = respond(x, &t, "bad fcall type");
167 		else{
168 			switch(x->fcall.type){
169 			case Tversion:
170 			case Tauth:
171 			case Tflush:
172 				f = nil;
173 				break;
174 			case Tattach:
175 				f = newfid(x->fcall.fid);
176 				break;
177 			default:
178 				f = newfid(x->fcall.fid);
179 				if(!f->busy){
180 					x->f = f;
181 					x = respond(x, &t, "fid not in use");
182 					continue;
183 				}
184 				break;
185 			}
186 			x->f = f;
187 			x  = (*fcall[x->fcall.type])(x, f);
188 		}
189 	}
190 }
191 
192 Mntdir*
fsysaddid(Rune * dir,int ndir,Rune ** incl,int nincl)193 fsysaddid(Rune *dir, int ndir, Rune **incl, int nincl)
194 {
195 	Mntdir *m;
196 	int id;
197 
198 	qlock(&mnt.lk);
199 	id = ++mnt.id;
200 	m = emalloc(sizeof *m);
201 	m->id = id;
202 	m->dir =  dir;
203 	m->ref = 1;	/* one for Command, one will be incremented in attach */
204 	m->ndir = ndir;
205 	m->next = mnt.md;
206 	m->incl = incl;
207 	m->nincl = nincl;
208 	mnt.md = m;
209 	qunlock(&mnt.lk);
210 	return m;
211 }
212 
213 void
fsysincid(Mntdir * m)214 fsysincid(Mntdir *m)
215 {
216 	qlock(&mnt.lk);
217 	m->ref++;
218 	qunlock(&mnt.lk);
219 }
220 
221 void
fsysdelid(Mntdir * idm)222 fsysdelid(Mntdir *idm)
223 {
224 	Mntdir *m, *prev;
225 	int i;
226 	char buf[64];
227 
228 	if(idm == nil)
229 		return;
230 	qlock(&mnt.lk);
231 	if(--idm->ref > 0){
232 		qunlock(&mnt.lk);
233 		return;
234 	}
235 	prev = nil;
236 	for(m=mnt.md; m; m=m->next){
237 		if(m == idm){
238 			if(prev)
239 				prev->next = m->next;
240 			else
241 				mnt.md = m->next;
242 			for(i=0; i<m->nincl; i++)
243 				free(m->incl[i]);
244 			free(m->incl);
245 			free(m->dir);
246 			free(m);
247 			qunlock(&mnt.lk);
248 			return;
249 		}
250 		prev = m;
251 	}
252 	qunlock(&mnt.lk);
253 	sprint(buf, "fsysdelid: can't find id %d\n", idm->id);
254 	sendp(cerr, estrdup(buf));
255 }
256 
257 /*
258  * Called only in exec.c:/^run(), from a different FD group
259  */
260 Mntdir*
fsysmount(Rune * dir,int ndir,Rune ** incl,int nincl)261 fsysmount(Rune *dir, int ndir, Rune **incl, int nincl)
262 {
263 	return fsysaddid(dir, ndir, incl, nincl);
264 }
265 
266 void
fsysclose(void)267 fsysclose(void)
268 {
269 	closing = 1;
270 	/*
271 	 * apparently this is not kosher on openbsd.
272 	 * perhaps because fsysproc is reading from sfd right now,
273 	 * the close hangs indefinitely.
274 	close(sfd);
275 	 */
276 }
277 
278 Xfid*
respond(Xfid * x,Fcall * t,char * err)279 respond(Xfid *x, Fcall *t, char *err)
280 {
281 	int n;
282 
283 	if(err){
284 		t->type = Rerror;
285 		t->ename = err;
286 	}else
287 		t->type = x->fcall.type+1;
288 	t->fid = x->fcall.fid;
289 	t->tag = x->fcall.tag;
290 	if(x->buf == nil)
291 		x->buf = emalloc(messagesize);
292 	n = convS2M(t, x->buf, messagesize);
293 	if(n <= 0)
294 		error("convert error in convS2M");
295 	if(write(sfd, x->buf, n) != n)
296 		error("write error in respond");
297 	free(x->buf);
298 	x->buf = nil;
299 	if(DEBUG)
300 		fprint(2, "r: %F\n", t);
301 	return x;
302 }
303 
304 static
305 Xfid*
fsysversion(Xfid * x,Fid * f)306 fsysversion(Xfid *x, Fid *f)
307 {
308 	Fcall t;
309 
310 	USED(f);
311 	if(x->fcall.msize < 256)
312 		return respond(x, &t, "version: message size too small");
313 	messagesize = x->fcall.msize;
314 	t.msize = messagesize;
315 	if(strncmp(x->fcall.version, "9P2000", 6) != 0)
316 		return respond(x, &t, "unrecognized 9P version");
317 	t.version = "9P2000";
318 	return respond(x, &t, nil);
319 }
320 
321 static
322 Xfid*
fsysauth(Xfid * x,Fid * f)323 fsysauth(Xfid *x, Fid *f)
324 {
325 	Fcall t;
326 
327 	USED(f);
328 	return respond(x, &t, "acme: authentication not required");
329 }
330 
331 static
332 Xfid*
fsysflush(Xfid * x,Fid * f)333 fsysflush(Xfid *x, Fid *f)
334 {
335 	USED(f);
336 	sendp(x->c, (void*)xfidflush);
337 	return nil;
338 }
339 
340 static
341 Xfid*
fsysattach(Xfid * x,Fid * f)342 fsysattach(Xfid *x, Fid *f)
343 {
344 	Fcall t;
345 	int id;
346 	Mntdir *m;
347 	char buf[128];
348 
349 	if(strcmp(x->fcall.uname, user) != 0)
350 		return respond(x, &t, Eperm);
351 	f->busy = TRUE;
352 	f->open = FALSE;
353 	f->qid.path = Qdir;
354 	f->qid.type = QTDIR;
355 	f->qid.vers = 0;
356 	f->dir = dirtab;
357 	f->nrpart = 0;
358 	f->w = nil;
359 	t.qid = f->qid;
360 	f->mntdir = nil;
361 	id = atoi(x->fcall.aname);
362 	qlock(&mnt.lk);
363 	for(m=mnt.md; m; m=m->next)
364 		if(m->id == id){
365 			f->mntdir = m;
366 			m->ref++;
367 			break;
368 		}
369 	if(m == nil && x->fcall.aname[0]){
370 		snprint(buf, sizeof buf, "unknown id '%s' in attach", x->fcall.aname);
371 		sendp(cerr, estrdup(buf));
372 	}
373 	qunlock(&mnt.lk);
374 	return respond(x, &t, nil);
375 }
376 
377 static
378 Xfid*
fsyswalk(Xfid * x,Fid * f)379 fsyswalk(Xfid *x, Fid *f)
380 {
381 	Fcall t;
382 	int c, i, j, id;
383 	Qid q;
384 	uchar type;
385 	ulong path;
386 	Fid *nf;
387 	Dirtab *d, *dir;
388 	Window *w;
389 	char *err;
390 
391 	nf = nil;
392 	w = nil;
393 	if(f->open)
394 		return respond(x, &t, "walk of open file");
395 	if(x->fcall.fid != x->fcall.newfid){
396 		nf = newfid(x->fcall.newfid);
397 		if(nf->busy)
398 			return respond(x, &t, "newfid already in use");
399 		nf->busy = TRUE;
400 		nf->open = FALSE;
401 		nf->mntdir = f->mntdir;
402 		if(f->mntdir)
403 			f->mntdir->ref++;
404 		nf->dir = f->dir;
405 		nf->qid = f->qid;
406 		nf->w = f->w;
407 		nf->nrpart = 0;	/* not open, so must be zero */
408 		if(nf->w)
409 			incref(&nf->w->ref);
410 		f = nf;	/* walk f */
411 	}
412 
413 	t.nwqid = 0;
414 	err = nil;
415 	dir = nil;
416 	id = WIN(f->qid);
417 	q = f->qid;
418 
419 	if(x->fcall.nwname > 0){
420 		for(i=0; i<x->fcall.nwname; i++){
421 			if((q.type & QTDIR) == 0){
422 				err = Enotdir;
423 				break;
424 			}
425 
426 			if(strcmp(x->fcall.wname[i], "..") == 0){
427 				type = QTDIR;
428 				path = Qdir;
429 				id = 0;
430 				if(w){
431 					winclose(w);
432 					w = nil;
433 				}
434     Accept:
435 				if(i == MAXWELEM){
436 					err = "name too long";
437 					break;
438 				}
439 				q.type = type;
440 				q.vers = 0;
441 				q.path = QID(id, path);
442 				t.wqid[t.nwqid++] = q;
443 				continue;
444 			}
445 
446 			/* is it a numeric name? */
447 			for(j=0; (c=x->fcall.wname[i][j]); j++)
448 				if(c<'0' || '9'<c)
449 					goto Regular;
450 			/* yes: it's a directory */
451 			if(w)	/* name has form 27/23; get out before losing w */
452 				break;
453 			id = atoi(x->fcall.wname[i]);
454 			qlock(&row.lk);
455 			w = lookid(id, FALSE);
456 			if(w == nil){
457 				qunlock(&row.lk);
458 				break;
459 			}
460 			incref(&w->ref);	/* we'll drop reference at end if there's an error */
461 			path = Qdir;
462 			type = QTDIR;
463 			qunlock(&row.lk);
464 			dir = dirtabw;
465 			goto Accept;
466 
467     Regular:
468 			if(strcmp(x->fcall.wname[i], "new") == 0){
469 				if(w)
470 					error("w set in walk to new");
471 				sendp(cnewwindow, nil);	/* signal newwindowthread */
472 				w = recvp(cnewwindow);	/* receive new window */
473 				incref(&w->ref);
474 				type = QTDIR;
475 				path = QID(w->id, Qdir);
476 				id = w->id;
477 				dir = dirtabw;
478 				goto Accept;
479 			}
480 
481 			if(id == 0)
482 				d = dirtab;
483 			else
484 				d = dirtabw;
485 			d++;	/* skip '.' */
486 			for(; d->name; d++)
487 				if(strcmp(x->fcall.wname[i], d->name) == 0){
488 					path = d->qid;
489 					type = d->type;
490 					dir = d;
491 					goto Accept;
492 				}
493 
494 			break;	/* file not found */
495 		}
496 
497 		if(i==0 && err == nil)
498 			err = Eexist;
499 	}
500 
501 	if(err!=nil || t.nwqid<x->fcall.nwname){
502 		if(nf){
503 			nf->busy = FALSE;
504 			fsysdelid(nf->mntdir);
505 		}
506 	}else if(t.nwqid  == x->fcall.nwname){
507 		if(w){
508 			f->w = w;
509 			w = nil;	/* don't drop the reference */
510 		}
511 		if(dir)
512 			f->dir = dir;
513 		f->qid = q;
514 	}
515 
516 	if(w != nil)
517 		winclose(w);
518 
519 	return respond(x, &t, err);
520 }
521 
522 static
523 Xfid*
fsysopen(Xfid * x,Fid * f)524 fsysopen(Xfid *x, Fid *f)
525 {
526 	Fcall t;
527 	int m;
528 
529 	/* can't truncate anything, so just disregard */
530 	x->fcall.mode &= ~(OTRUNC|OCEXEC);
531 	/* can't execute or remove anything */
532 	if(x->fcall.mode==OEXEC || (x->fcall.mode&ORCLOSE))
533 		goto Deny;
534 	switch(x->fcall.mode){
535 	default:
536 		goto Deny;
537 	case OREAD:
538 		m = 0400;
539 		break;
540 	case OWRITE:
541 		m = 0200;
542 		break;
543 	case ORDWR:
544 		m = 0600;
545 		break;
546 	}
547 	if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
548 		goto Deny;
549 
550 	sendp(x->c, (void*)xfidopen);
551 	return nil;
552 
553     Deny:
554 	return respond(x, &t, Eperm);
555 }
556 
557 static
558 Xfid*
fsyscreate(Xfid * x,Fid * f)559 fsyscreate(Xfid *x, Fid *f)
560 {
561 	Fcall t;
562 
563 	USED(f);
564 	return respond(x, &t, Eperm);
565 }
566 
567 static
568 int
idcmp(const void * a,const void * b)569 idcmp(const void *a, const void *b)
570 {
571 	return *(int*)a - *(int*)b;
572 }
573 
574 static
575 Xfid*
fsysread(Xfid * x,Fid * f)576 fsysread(Xfid *x, Fid *f)
577 {
578 	Fcall t;
579 	uchar *b;
580 	int i, id, n, o, e, j, k, *ids, nids;
581 	Dirtab *d, dt;
582 	Column *c;
583 	uint clock, len;
584 	char buf[16];
585 
586 	if(f->qid.type & QTDIR){
587 		if(FILE(f->qid) == Qacme){	/* empty dir */
588 			t.data = nil;
589 			t.count = 0;
590 			respond(x, &t, nil);
591 			return x;
592 		}
593 		o = x->fcall.offset;
594 		e = x->fcall.offset+x->fcall.count;
595 		clock = getclock();
596 		b = emalloc(messagesize);
597 		id = WIN(f->qid);
598 		n = 0;
599 		if(id > 0)
600 			d = dirtabw;
601 		else
602 			d = dirtab;
603 		d++;	/* first entry is '.' */
604 		for(i=0; d->name!=nil && i<e; i+=len){
605 			len = dostat(WIN(x->f->qid), d, b+n, x->fcall.count-n, clock);
606 			if(len <= BIT16SZ)
607 				break;
608 			if(i >= o)
609 				n += len;
610 			d++;
611 		}
612 		if(id == 0){
613 			qlock(&row.lk);
614 			nids = 0;
615 			ids = nil;
616 			for(j=0; j<row.ncol; j++){
617 				c = row.col[j];
618 				for(k=0; k<c->nw; k++){
619 					ids = realloc(ids, (nids+1)*sizeof(int));
620 					ids[nids++] = c->w[k]->id;
621 				}
622 			}
623 			qunlock(&row.lk);
624 			qsort(ids, nids, sizeof ids[0], idcmp);
625 			j = 0;
626 			dt.name = buf;
627 			for(; j<nids && i<e; i+=len){
628 				k = ids[j];
629 				sprint(dt.name, "%d", k);
630 				dt.qid = QID(k, Qdir);
631 				dt.type = QTDIR;
632 				dt.perm = DMDIR|0700;
633 				len = dostat(k, &dt, b+n, x->fcall.count-n, clock);
634 				if(len == 0)
635 					break;
636 				if(i >= o)
637 					n += len;
638 				j++;
639 			}
640 			free(ids);
641 		}
642 		t.data = (char*)b;
643 		t.count = n;
644 		respond(x, &t, nil);
645 		free(b);
646 		return x;
647 	}
648 	sendp(x->c, (void*)xfidread);
649 	return nil;
650 }
651 
652 static
653 Xfid*
fsyswrite(Xfid * x,Fid * f)654 fsyswrite(Xfid *x, Fid *f)
655 {
656 	USED(f);
657 	sendp(x->c, (void*)xfidwrite);
658 	return nil;
659 }
660 
661 static
662 Xfid*
fsysclunk(Xfid * x,Fid * f)663 fsysclunk(Xfid *x, Fid *f)
664 {
665 	fsysdelid(f->mntdir);
666 	sendp(x->c, (void*)xfidclose);
667 	return nil;
668 }
669 
670 static
671 Xfid*
fsysremove(Xfid * x,Fid * f)672 fsysremove(Xfid *x, Fid *f)
673 {
674 	Fcall t;
675 
676 	USED(f);
677 	return respond(x, &t, Eperm);
678 }
679 
680 static
681 Xfid*
fsysstat(Xfid * x,Fid * f)682 fsysstat(Xfid *x, Fid *f)
683 {
684 	Fcall t;
685 
686 	t.stat = emalloc(messagesize-IOHDRSZ);
687 	t.nstat = dostat(WIN(x->f->qid), f->dir, t.stat, messagesize-IOHDRSZ, getclock());
688 	x = respond(x, &t, nil);
689 	free(t.stat);
690 	return x;
691 }
692 
693 static
694 Xfid*
fsyswstat(Xfid * x,Fid * f)695 fsyswstat(Xfid *x, Fid *f)
696 {
697 	Fcall t;
698 
699 	USED(f);
700 	return respond(x, &t, Eperm);
701 }
702 
703 Fid*
newfid(int fid)704 newfid(int fid)
705 {
706 	Fid *f, *ff, **fh;
707 
708 	ff = nil;
709 	fh = &fids[fid&(Nhash-1)];
710 	for(f=*fh; f; f=f->next)
711 		if(f->fid == fid)
712 			return f;
713 		else if(ff==nil && f->busy==FALSE)
714 			ff = f;
715 	if(ff){
716 		ff->fid = fid;
717 		return ff;
718 	}
719 	f = emalloc(sizeof *f);
720 	f->fid = fid;
721 	f->next = *fh;
722 	*fh = f;
723 	return f;
724 }
725 
726 uint
getclock(void)727 getclock(void)
728 {
729 	return time(0);
730 }
731 
732 int
dostat(int id,Dirtab * dir,uchar * buf,int nbuf,uint clock)733 dostat(int id, Dirtab *dir, uchar *buf, int nbuf, uint clock)
734 {
735 	Dir d;
736 
737 	d.qid.path = QID(id, dir->qid);
738 	d.qid.vers = 0;
739 	d.qid.type = dir->type;
740 	d.mode = dir->perm;
741 	d.length = 0;	/* would be nice to do better */
742 	d.name = dir->name;
743 	d.uid = user;
744 	d.gid = user;
745 	d.muid = user;
746 	d.atime = clock;
747 	d.mtime = clock;
748 	return convD2M(&d, buf, nbuf);
749 }
750