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