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