xref: /plan9/sys/src/cmd/acme/fsys.c (revision 6b8bc68243dff965faa99f64fe710965ebee6f8f)
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
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
140 fsysproc(void *)
141 {
142 	int n;
143 	Xfid *x;
144 	Fid *f;
145 	Fcall t;
146 	uchar *buf;
147 
148 	x = nil;
149 	for(;;){
150 		buf = emalloc(messagesize+UTFmax);	/* overflow for appending partial rune in xfidwrite */
151 		n = read9pmsg(sfd, buf, messagesize);
152 		if(n <= 0){
153 			if(closing)
154 				break;
155 			error("i/o error on server channel");
156 		}
157 		if(x == nil){
158 			sendp(cxfidalloc, nil);
159 			x = recvp(cxfidalloc);
160 		}
161 		x->buf = buf;
162 		if(convM2S(buf, n, x) != n)
163 			error("convert error in convM2S");
164 		if(DEBUG)
165 			fprint(2, "%F\n", &x->Fcall);
166 		if(fcall[x->type] == nil)
167 			x = respond(x, &t, "bad fcall type");
168 		else{
169 			if(x->type==Tversion || x->type==Tauth)
170 				f = nil;
171 			else
172 				f = newfid(x->fid);
173 			x->f = f;
174 			x  = (*fcall[x->type])(x, f);
175 		}
176 	}
177 }
178 
179 Mntdir*
180 fsysaddid(Rune *dir, int ndir, Rune **incl, int nincl)
181 {
182 	Mntdir *m;
183 	int id;
184 
185 	qlock(&mnt);
186 	id = ++mnt.id;
187 	m = emalloc(sizeof *m);
188 	m->id = id;
189 	m->dir =  dir;
190 	m->ref = 1;	/* one for Command, one will be incremented in attach */
191 	m->ndir = ndir;
192 	m->next = mnt.md;
193 	m->incl = incl;
194 	m->nincl = nincl;
195 	mnt.md = m;
196 	qunlock(&mnt);
197 	return m;
198 }
199 
200 void
201 fsysincid(Mntdir *m)
202 {
203 	qlock(&mnt);
204 	m->ref++;
205 	qunlock(&mnt);
206 }
207 
208 void
209 fsysdelid(Mntdir *idm)
210 {
211 	Mntdir *m, *prev;
212 	int i;
213 	char buf[64];
214 
215 	if(idm == nil)
216 		return;
217 	qlock(&mnt);
218 	if(--idm->ref > 0){
219 		qunlock(&mnt);
220 		return;
221 	}
222 	prev = nil;
223 	for(m=mnt.md; m; m=m->next){
224 		if(m == idm){
225 			if(prev)
226 				prev->next = m->next;
227 			else
228 				mnt.md = m->next;
229 			for(i=0; i<m->nincl; i++)
230 				free(m->incl[i]);
231 			free(m->incl);
232 			free(m->dir);
233 			free(m);
234 			qunlock(&mnt);
235 			return;
236 		}
237 		prev = m;
238 	}
239 	qunlock(&mnt);
240 	sprint(buf, "fsysdelid: can't find id %d\n", idm->id);
241 	sendp(cerr, estrdup(buf));
242 }
243 
244 /*
245  * Called only in exec.c:/^run(), from a different FD group
246  */
247 Mntdir*
248 fsysmount(Rune *dir, int ndir, Rune **incl, int nincl)
249 {
250 	char buf[256];
251 	Mntdir *m;
252 
253 	/* close server side so don't hang if acme is half-exited */
254 	close(sfd);
255 	m = fsysaddid(dir, ndir, incl, nincl);
256 	sprint(buf, "%d", m->id);
257 	if(mount(cfd, -1, "/mnt/acme", MREPL, buf) < 0){
258 		fsysdelid(m);
259 		return nil;
260 	}
261 	close(cfd);
262 	bind("/mnt/acme", "/mnt/wsys", MREPL);
263 	if(bind("/mnt/acme", "/dev", MBEFORE) < 0){
264 		fsysdelid(m);
265 		return nil;
266 	}
267 	return m;
268 }
269 
270 void
271 fsysclose(void)
272 {
273 	closing = 1;
274 	close(cfd);
275 	close(sfd);
276 }
277 
278 Xfid*
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->type+1;
288 	t->fid = x->fid;
289 	t->tag = x->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*
306 fsysversion(Xfid *x, Fid*)
307 {
308 	Fcall t;
309 
310 	if(x->msize < 256)
311 		return respond(x, &t, "version: message size too small");
312 	messagesize = x->msize;
313 	t.msize = messagesize;
314 	if(strncmp(x->version, "9P2000", 6) != 0)
315 		return respond(x, &t, "unrecognized 9P version");
316 	t.version = "9P2000";
317 	return respond(x, &t, nil);
318 }
319 
320 static
321 Xfid*
322 fsysauth(Xfid *x, Fid*)
323 {
324 	return respond(x, nil, "acme: authentication not required");
325 }
326 
327 static
328 Xfid*
329 fsysflush(Xfid *x, Fid*)
330 {
331 	sendp(x->c, xfidflush);
332 	return nil;
333 }
334 
335 static
336 Xfid*
337 fsysattach(Xfid *x, Fid *f)
338 {
339 	Fcall t;
340 	int id;
341 	Mntdir *m;
342 
343 	if(strcmp(x->uname, user) != 0)
344 		return respond(x, &t, Eperm);
345 	f->busy = TRUE;
346 	f->open = FALSE;
347 	f->qid.path = Qdir;
348 	f->qid.type = QTDIR;
349 	f->qid.vers = 0;
350 	f->dir = dirtab;
351 	f->nrpart = 0;
352 	f->w = nil;
353 	t.qid = f->qid;
354 	f->mntdir = nil;
355 	id = atoi(x->aname);
356 	qlock(&mnt);
357 	for(m=mnt.md; m; m=m->next)
358 		if(m->id == id){
359 			f->mntdir = m;
360 			m->ref++;
361 			break;
362 		}
363 	if(m == nil)
364 		sendp(cerr, estrdup("unknown id in attach"));
365 	qunlock(&mnt);
366 	return respond(x, &t, nil);
367 }
368 
369 static
370 Xfid*
371 fsyswalk(Xfid *x, Fid *f)
372 {
373 	Fcall t;
374 	int c, i, j, id;
375 	Qid q;
376 	uchar type;
377 	ulong path;
378 	Fid *nf;
379 	Dirtab *d, *dir;
380 	Window *w;
381 	char *err;
382 
383 	nf = nil;
384 	w = nil;
385 	if(f->open)
386 		return respond(x, &t, "walk of open file");
387 	if(x->fid != x->newfid){
388 		nf = newfid(x->newfid);
389 		if(nf->busy)
390 			return respond(x, &t, "newfid already in use");
391 		nf->busy = TRUE;
392 		nf->open = FALSE;
393 		nf->mntdir = f->mntdir;
394 		if(f->mntdir)
395 			f->mntdir->ref++;
396 		nf->dir = f->dir;
397 		nf->qid = f->qid;
398 		nf->w = f->w;
399 		nf->nrpart = 0;	/* not open, so must be zero */
400 		if(nf->w)
401 			incref(nf->w);
402 		f = nf;	/* walk f */
403 	}
404 
405 	t.nwqid = 0;
406 	err = nil;
407 	dir = nil;
408 	id = WIN(f->qid);
409 	q = f->qid;
410 
411 	if(x->nwname > 0){
412 		for(i=0; i<x->nwname; i++){
413 			if((q.type & QTDIR) == 0){
414 				err = Enotdir;
415 				break;
416 			}
417 
418 			if(strcmp(x->wname[i], "..") == 0){
419 				type = QTDIR;
420 				path = Qdir;
421 				id = 0;
422 				if(w){
423 					winclose(w);
424 					w = nil;
425 				}
426     Accept:
427 				if(i == MAXWELEM){
428 					err = "name too long";
429 					break;
430 				}
431 				q.type = type;
432 				q.vers = 0;
433 				q.path = QID(id, path);
434 				t.wqid[t.nwqid++] = q;
435 				continue;
436 			}
437 
438 			/* is it a numeric name? */
439 			for(j=0; (c=x->wname[i][j]); j++)
440 				if(c<'0' || '9'<c)
441 					goto Regular;
442 			/* yes: it's a directory */
443 			if(w)	/* name has form 27/23; get out before losing w */
444 				break;
445 			id = atoi(x->wname[i]);
446 			qlock(&row);
447 			w = lookid(id, FALSE);
448 			if(w == nil){
449 				qunlock(&row);
450 				break;
451 			}
452 			incref(w);	/* we'll drop reference at end if there's an error */
453 			path = Qdir;
454 			type = QTDIR;
455 			qunlock(&row);
456 			dir = dirtabw;
457 			goto Accept;
458 
459     Regular:
460 //			if(FILE(f->qid) == Qacme)	/* empty directory */
461 //				break;
462 			if(strcmp(x->wname[i], "new") == 0){
463 				if(w)
464 					error("w set in walk to new");
465 				sendp(cnewwindow, nil);	/* signal newwindowthread */
466 				w = recvp(cnewwindow);	/* receive new window */
467 				incref(w);
468 				type = QTDIR;
469 				path = QID(w->id, Qdir);
470 				id = w->id;
471 				dir = dirtabw;
472 				goto Accept;
473 			}
474 
475 			if(id == 0)
476 				d = dirtab;
477 			else
478 				d = dirtabw;
479 			d++;	/* skip '.' */
480 			for(; d->name; d++)
481 				if(strcmp(x->wname[i], d->name) == 0){
482 					path = d->qid;
483 					type = d->type;
484 					dir = d;
485 					goto Accept;
486 				}
487 
488 			break;	/* file not found */
489 		}
490 
491 		if(i==0 && err == nil)
492 			err = Eexist;
493 	}
494 
495 	if(err!=nil || t.nwqid<x->nwname){
496 		if(nf){
497 			nf->busy = FALSE;
498 			fsysdelid(nf->mntdir);
499 		}
500 	}else if(t.nwqid  == x->nwname){
501 		if(w){
502 			f->w = w;
503 			w = nil;	/* don't drop the reference */
504 		}
505 		if(dir)
506 			f->dir = dir;
507 		f->qid = q;
508 	}
509 
510 	if(w != nil)
511 		winclose(w);
512 
513 	return respond(x, &t, err);
514 }
515 
516 static
517 Xfid*
518 fsysopen(Xfid *x, Fid *f)
519 {
520 	Fcall t;
521 	int m;
522 
523 	/* can't truncate anything, so just disregard */
524 	x->mode &= ~(OTRUNC|OCEXEC);
525 	/* can't execute or remove anything */
526 	if(x->mode==OEXEC || (x->mode&ORCLOSE))
527 		goto Deny;
528 	switch(x->mode){
529 	default:
530 		goto Deny;
531 	case OREAD:
532 		m = 0400;
533 		break;
534 	case OWRITE:
535 		m = 0200;
536 		break;
537 	case ORDWR:
538 		m = 0600;
539 		break;
540 	}
541 	if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
542 		goto Deny;
543 
544 	sendp(x->c, xfidopen);
545 	return nil;
546 
547     Deny:
548 	return respond(x, &t, Eperm);
549 }
550 
551 static
552 Xfid*
553 fsyscreate(Xfid *x, Fid*)
554 {
555 	Fcall t;
556 
557 	return respond(x, &t, Eperm);
558 }
559 
560 static
561 int
562 idcmp(void *a, void *b)
563 {
564 	return *(int*)a - *(int*)b;
565 }
566 
567 static
568 Xfid*
569 fsysread(Xfid *x, Fid *f)
570 {
571 	Fcall t;
572 	uchar *b;
573 	int i, id, n, o, e, j, k, *ids, nids;
574 	Dirtab *d, dt;
575 	Column *c;
576 	uint clock, len;
577 	char buf[16];
578 
579 	if(f->qid.type & QTDIR){
580 		if(FILE(f->qid) == Qacme){	/* empty dir */
581 			t.data = nil;
582 			t.count = 0;
583 			respond(x, &t, nil);
584 			return x;
585 		}
586 		o = x->offset;
587 		e = x->offset+x->count;
588 		clock = getclock();
589 		b = emalloc(messagesize);
590 		id = WIN(f->qid);
591 		n = 0;
592 		if(id > 0)
593 			d = dirtabw;
594 		else
595 			d = dirtab;
596 		d++;	/* first entry is '.' */
597 		for(i=0; d->name!=nil && i<e; i+=len){
598 			len = dostat(WIN(x->f->qid), d, b+n, x->count-n, clock);
599 			if(len <= BIT16SZ)
600 				break;
601 			if(i >= o)
602 				n += len;
603 			d++;
604 		}
605 		if(id == 0){
606 			qlock(&row);
607 			nids = 0;
608 			ids = nil;
609 			for(j=0; j<row.ncol; j++){
610 				c = row.col[j];
611 				for(k=0; k<c->nw; k++){
612 					ids = realloc(ids, (nids+1)*sizeof(int));
613 					ids[nids++] = c->w[k]->id;
614 				}
615 			}
616 			qunlock(&row);
617 			qsort(ids, nids, sizeof ids[0], idcmp);
618 			j = 0;
619 			dt.name = buf;
620 			for(; j<nids && i<e; i+=len){
621 				k = ids[j];
622 				sprint(dt.name, "%d", k);
623 				dt.qid = QID(k, Qdir);
624 				dt.type = QTDIR;
625 				dt.perm = DMDIR|0700;
626 				len = dostat(k, &dt, b+n, x->count-n, clock);
627 				if(len == 0)
628 					break;
629 				if(i >= o)
630 					n += len;
631 				j++;
632 			}
633 			free(ids);
634 		}
635 		t.data = (char*)b;
636 		t.count = n;
637 		respond(x, &t, nil);
638 		free(b);
639 		return x;
640 	}
641 	sendp(x->c, xfidread);
642 	return nil;
643 }
644 
645 static
646 Xfid*
647 fsyswrite(Xfid *x, Fid*)
648 {
649 	sendp(x->c, xfidwrite);
650 	return nil;
651 }
652 
653 static
654 Xfid*
655 fsysclunk(Xfid *x, Fid *f)
656 {
657 	fsysdelid(f->mntdir);
658 	sendp(x->c, xfidclose);
659 	return nil;
660 }
661 
662 static
663 Xfid*
664 fsysremove(Xfid *x, Fid*)
665 {
666 	Fcall t;
667 
668 	return respond(x, &t, Eperm);
669 }
670 
671 static
672 Xfid*
673 fsysstat(Xfid *x, Fid *f)
674 {
675 	Fcall t;
676 
677 	t.stat = emalloc(messagesize-IOHDRSZ);
678 	t.nstat = dostat(WIN(x->f->qid), f->dir, t.stat, messagesize-IOHDRSZ, getclock());
679 	x = respond(x, &t, nil);
680 	free(t.stat);
681 	return x;
682 }
683 
684 static
685 Xfid*
686 fsyswstat(Xfid *x, Fid*)
687 {
688 	Fcall t;
689 
690 	return respond(x, &t, Eperm);
691 }
692 
693 Fid*
694 newfid(int fid)
695 {
696 	Fid *f, *ff, **fh;
697 
698 	ff = nil;
699 	fh = &fids[fid&(Nhash-1)];
700 	for(f=*fh; f; f=f->next)
701 		if(f->fid == fid)
702 			return f;
703 		else if(ff==nil && f->busy==FALSE)
704 			ff = f;
705 	if(ff){
706 		ff->fid = fid;
707 		return ff;
708 	}
709 	f = emalloc(sizeof *f);
710 	f->fid = fid;
711 	f->next = *fh;
712 	*fh = f;
713 	return f;
714 }
715 
716 uint
717 getclock()
718 {
719 	char buf[32];
720 
721 	buf[0] = '\0';
722 	pread(clockfd, buf, sizeof buf, 0);
723 	return atoi(buf);
724 }
725 
726 int
727 dostat(int id, Dirtab *dir, uchar *buf, int nbuf, uint clock)
728 {
729 	Dir d;
730 
731 	d.qid.path = QID(id, dir->qid);
732 	d.qid.vers = 0;
733 	d.qid.type = dir->type;
734 	d.mode = dir->perm;
735 	d.length = 0;	/* would be nice to do better */
736 	d.name = dir->name;
737 	d.uid = user;
738 	d.gid = user;
739 	d.muid = user;
740 	d.atime = clock;
741 	d.mtime = clock;
742 	return convD2M(&d, buf, nbuf);
743 }
744