master ohmu/acme2k / src / cmd / acme / fsys.c
ohmu  ·  2026-09-21
  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
 15static	int	sfd;
 16
 17enum
 18{
 19	Nhash	= 16,
 20	DEBUG	= 0
 21};
 22
 23static	Fid	*fids[Nhash];
 24
 25Fid	*newfid(int);
 26
 27static	Xfid*	fsysflush(Xfid*, Fid*);
 28static	Xfid*	fsysauth(Xfid*, Fid*);
 29static	Xfid*	fsysversion(Xfid*, Fid*);
 30static	Xfid*	fsysattach(Xfid*, Fid*);
 31static	Xfid*	fsyswalk(Xfid*, Fid*);
 32static	Xfid*	fsysopen(Xfid*, Fid*);
 33static	Xfid*	fsyscreate(Xfid*, Fid*);
 34static	Xfid*	fsysread(Xfid*, Fid*);
 35static	Xfid*	fsyswrite(Xfid*, Fid*);
 36static	Xfid*	fsysclunk(Xfid*, Fid*);
 37static	Xfid*	fsysremove(Xfid*, Fid*);
 38static	Xfid*	fsysstat(Xfid*, Fid*);
 39static	Xfid*	fsyswstat(Xfid*, Fid*);
 40
 41Xfid* 	(*fcall[Tmax])(Xfid*, Fid*);
 42
 43static void
 44initfcall(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
 61char Eperm[] = "permission denied";
 62char Eexist[] = "file does not exist";
 63char Enotdir[] = "not a directory";
 64
 65Dirtab 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
 80Dirtab 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
 97typedef struct Mnt Mnt;
 98struct Mnt
 99{
100	QLock	lk;
101	int		id;
102	Mntdir	*md;
103};
104
105Mnt	mnt;
106
107Xfid*	respond(Xfid*, Fcall*, char*);
108int		dostat(int, Dirtab*, uchar*, int, uint);
109uint	getclock(void);
110
111char	*user = "Wile E. Coyote";
112static int closing = 0;
113int	messagesize = Maxblock+IOHDRSZ;	/* good start */
114
115void	fsysproc(void *);
116
117void
118fsysinit(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
135void
136fsysproc(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
192Mntdir*
193fsysaddid(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
213void
214fsysincid(Mntdir *m)
215{
216	qlock(&mnt.lk);
217	m->ref++;
218	qunlock(&mnt.lk);
219}
220
221void
222fsysdelid(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 */
260Mntdir*
261fsysmount(Rune *dir, int ndir, Rune **incl, int nincl)
262{
263	return fsysaddid(dir, ndir, incl, nincl);
264}
265
266void
267fsysclose(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
278Xfid*
279respond(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
304static
305Xfid*
306fsysversion(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
321static
322Xfid*
323fsysauth(Xfid *x, Fid *f)
324{
325	Fcall t;
326
327	USED(f);
328	return respond(x, &t, "acme: authentication not required");
329}
330
331static
332Xfid*
333fsysflush(Xfid *x, Fid *f)
334{
335	USED(f);
336	sendp(x->c, (void*)xfidflush);
337	return nil;
338}
339
340static
341Xfid*
342fsysattach(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
377static
378Xfid*
379fsyswalk(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
522static
523Xfid*
524fsysopen(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
557static
558Xfid*
559fsyscreate(Xfid *x, Fid *f)
560{
561	Fcall t;
562
563	USED(f);
564	return respond(x, &t, Eperm);
565}
566
567static
568int
569idcmp(const void *a, const void *b)
570{
571	return *(int*)a - *(int*)b;
572}
573
574static
575Xfid*
576fsysread(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
652static
653Xfid*
654fsyswrite(Xfid *x, Fid *f)
655{
656	USED(f);
657	sendp(x->c, (void*)xfidwrite);
658	return nil;
659}
660
661static
662Xfid*
663fsysclunk(Xfid *x, Fid *f)
664{
665	fsysdelid(f->mntdir);
666	sendp(x->c, (void*)xfidclose);
667	return nil;
668}
669
670static
671Xfid*
672fsysremove(Xfid *x, Fid *f)
673{
674	Fcall t;
675
676	USED(f);
677	return respond(x, &t, Eperm);
678}
679
680static
681Xfid*
682fsysstat(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
693static
694Xfid*
695fsyswstat(Xfid *x, Fid *f)
696{
697	Fcall t;
698
699	USED(f);
700	return respond(x, &t, Eperm);
701}
702
703Fid*
704newfid(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
726uint
727getclock(void)
728{
729	return time(0);
730}
731
732int
733dostat(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}