Charles.Forsyth20060303a828770d19 years agoHistory
453 lines · 9.8 KB · Brainfuck
RawDownload
implement Styxlib; ## Copyright © 1999 Vita Nuova Limited.  All rights reserved.# Revisions copyright © 2002 Vita Nuova Holdings Limited.  All rights reserved.# include "sys.m";	sys: Sys;include "styx.m";	styx: Styx;	Tmsg, Rmsg: import styx; include "styxlib.m"; CHANHASHSIZE: con 32;starttime: int;timefd: ref Sys->FD; DEBUG: con 0; init(s: Styx): string{	sys = load Sys Sys->PATH;	styx = s;	# our caller inits	return nil;} Styxserver.new(fd: ref Sys->FD): (chan of ref Tmsg, ref Styxserver){	starttime = now();	srv := ref Styxserver(fd, array[CHANHASHSIZE] of list of ref Chan, getuname(), 0);	if(fd == nil)		return (nil, srv);	tchan := chan of ref Tmsg;	sync := chan of int;	spawn tmsgreader(fd, srv, tchan, sync);	<-sync;	return (tchan, srv);} now(): int{	if(timefd == nil){		timefd = sys->open("/dev/time", sys->OREAD);		if(timefd == nil)			return 0;	}	buf := array[64] of byte;	sys->seek(timefd, big 0, 0);	n := sys->read(timefd, buf, len buf);	if(n < 0)		return 0; 	t := (big string buf[0:n]) / big 1000000;	return int t;}  getuname(): string{	if ((fd := sys->open("/dev/user", Sys->OREAD)) == nil)		return "unknown";	buf := array[Sys->NAMEMAX] of byte;	n := sys->read(fd, buf, len buf);	if (n <= 0)		return "unknown";	return string buf[0:n];} tmsgreader(fd: ref Sys->FD, srv: ref Styxserver, tchan: chan of ref Tmsg, sync: chan of int){	sys->pctl(Sys->NEWFD|Sys->NEWNS, fd.fd :: nil);	sync <-= 1;	fd = sys->fildes(fd.fd);	while((m := Tmsg.read(fd, srv.msize)) != nil && tagof m != tagof Tmsg.Readerror){		tchan <-= m;		m = nil;	}	tchan <-= m;} Styxserver.reply(srv: self ref Styxserver, m: ref Rmsg): int{	if (DEBUG) 		sys->fprint(sys->fildes(2), "%s\n", m.text());	a := m.pack();	if(a == nil)		return -1;	return sys->write(srv.fd, a, len a);} Styxserver.devversion(srv: self ref Styxserver, m: ref Tmsg.Version): int{	if(srv.msize <= 0)		srv.msize = Styx->MAXRPC;	(msize, version) := styx->compatible(m, srv.msize, Styx->VERSION);	if(msize < 128){		srv.reply(ref Rmsg.Error(m.tag, "unusable message size"));		return -1;	}	srv.msize = msize;	srv.reply(ref Rmsg.Version(m.tag, msize, version));	return 0;} Styxserver.devauth(srv: self ref Styxserver, m: ref Tmsg.Auth){	srv.reply(ref Rmsg.Error(m.tag, "authentication not required"));} Styxserver.devattach(srv: self ref Styxserver, m: ref Tmsg.Attach): ref Chan{	c := srv.newchan(m.fid);	if (c == nil) {		srv.reply(ref Rmsg.Error(m.tag, Einuse));		return nil;	}	c.uname = m.uname;	c.qid.qtype = Sys->QTDIR;	c.qid.path = big 0;	c.path = "dev";	srv.reply(ref Rmsg.Attach(m.tag, c.qid));	return c;} Styxserver.clone(srv: self ref Styxserver, oc: ref Chan, newfid: int): ref Chan{	c := srv.newchan(newfid);	if (c == nil) 		return nil;	c.qid = oc.qid;	c.uname  = oc.uname;	c.open = oc.open;	c.mode = oc.mode;	c.path = oc.path;	c.data = oc.data;	return c;} Styxserver.devflush(srv: self ref Styxserver, m: ref Tmsg.Flush){	srv.reply(ref Rmsg.Flush(m.tag));} Styxserver.devwalk(srv: self ref Styxserver, m: ref Tmsg.Walk,							gen: Dirgenmod, tab: array of Dirtab): ref Chan{	c := srv.fidtochan(m.fid);	if (c == nil) {		srv.reply(ref Rmsg.Error(m.tag, Ebadfid));		return nil;	}	if (c.open) {		srv.reply(ref Rmsg.Error(m.tag, Eopen));		return nil;	}	if (!c.isdir()) {		srv.reply(ref Rmsg.Error(m.tag, Enotdir));		return nil;	}	# should check permissions here?	qids: array of Sys->Qid;	cc := ref *c;	# walk a temporary copy	if(len m.names > 0){		qids = array[len m.names] of Sys->Qid;		for(i := 0; i < len m.names; i++){			for(k := 0;; k++){				(ok, d) := gen->dirgen(srv, cc, tab, k);				if(ok < 0){					if(i == 0)						srv.reply(ref Rmsg.Error(m.tag, Enotfound));					else						srv.reply(ref Rmsg.Walk(m.tag, qids[0:i]));					return nil;				}				if (d.name == m.names[i]) {					cc.qid = d.qid;					cc.path = d.name;					qids[i] = cc.qid;					break;				}			}		}	}	# successful walk	if(m.newfid != m.fid){		# clone/walk		nc := srv.clone(cc, m.newfid);		if(nc == nil){			srv.reply(ref Rmsg.Error(m.tag, Einuse));			return nil;		}		c = nc;	}else{		# walk c itself		c.qid = cc.qid;		c.path = cc.path;	}	srv.reply(ref Rmsg.Walk(m.tag, qids));	return c;} Styxserver.devclunk(srv: self ref Styxserver, m: ref Tmsg.Clunk): ref Chan{	c := srv.fidtochan(m.fid);	if (c == nil) {		srv.reply(ref Rmsg.Error(m.tag, Ebadfid));		return nil;	}	srv.chanfree(c);	srv.reply(ref Rmsg.Clunk(m.tag));	return c;} Styxserver.devstat(srv: self ref Styxserver, m: ref Tmsg.Stat,							gen: Dirgenmod, tab: array of Dirtab){	c := srv.fidtochan(m.fid);	if (c == nil) {		srv.reply(ref Rmsg.Error(m.tag, Ebadfid));		return;	}	i := 0;	(ok, d) := gen->dirgen(srv, c, tab, i++);	while (ok >= 0) {		if (ok > 0 && c.qid.path == d.qid.path) {			srv.reply(ref Rmsg.Stat(m.tag, d));			return;		}		(ok, d) = gen->dirgen(srv, c, tab, i++);	}	# auto-generate entry for directory if not found.	# XXX this is asking for trouble, as the permissions given	# on stat() of a directory can be different from those given	# when reading the directory's entry in its parent dir.	if (c.qid.qtype & Sys->QTDIR)		srv.reply(ref Rmsg.Stat(m.tag, devdir(c, c.qid, c.path, big 0, srv.uname, Sys->DMDIR|8r555)));	else		srv.reply(ref Rmsg.Error(m.tag, Enotfound));} Styxserver.devdirread(srv: self ref Styxserver, m: ref Tmsg.Read,							gen: Dirgenmod, tab: array of Dirtab){	c := srv.fidtochan(m.fid);	if (c == nil) {		srv.reply(ref Rmsg.Error(m.tag, Ebadfid));		return;	}	offset := int m.offset;	data := array[m.count] of byte;	start := 0;	n := 0;	for (k := 0;; k++) {		(ok, d) := gen->dirgen(srv, c, tab, k);		if(ok < 0){			srv.reply(ref Rmsg.Read(m.tag, data[0:n]));			return;		}		size := styx->packdirsize(d);		if(start < offset){			start += size;			continue;		}		if(n+size > m.count)			break;		data[n:] = styx->packdir(d);		n += size;	}	srv.reply(ref Rmsg.Read(m.tag, data[0:n]));} Styxserver.devopen(srv: self ref Styxserver, m: ref Tmsg.Open,							gen: Dirgenmod, tab: array of Dirtab): ref Chan{	c := srv.fidtochan(m.fid);	if (c == nil) {		srv.reply(ref Rmsg.Error(m.tag, Ebadfid));		return nil;	}	omode := m.mode;	i := 0;	(ok, d) := gen->dirgen(srv, c, tab, i++);	while (ok >= 0) {		# XXX dev.c checks vers as well... is that desirable?		if (ok > 0 && c.qid.path == d.qid.path) {			if (openok(omode, d.mode, c.uname, d.uid, d.gid)) {				c.qid.vers = d.qid.vers;				break;			}			srv.reply(ref Rmsg.Error(m.tag, Eperm));			return nil;		}		(ok, d) = gen->dirgen(srv, c, tab, i++);	}	if ((c.qid.qtype & Sys->QTDIR) && omode != Sys->OREAD) {		srv.reply(ref Rmsg.Error(m.tag, Eperm));		return nil;	}	if ((c.mode = openmode(omode)) == -1) {		srv.reply(ref Rmsg.Error(m.tag, Ebadarg));		return nil;	}	c.open = 1;	c.mode = omode;	srv.reply(ref Rmsg.Open(m.tag, c.qid, Styx->MAXFDATA));	return c;} Styxserver.devremove(srv: self ref Styxserver, m: ref Tmsg.Remove): ref Chan{	c := srv.fidtochan(m.fid);	if (c == nil) {		srv.reply(ref Rmsg.Error(m.tag, Ebadfid));		return nil;	}	srv.chanfree(c);	srv.reply(ref Rmsg.Error(m.tag, Eperm));	return c;} Styxserver.fidtochan(srv: self ref Styxserver, fid: int): ref Chan{	for (l := srv.chans[fid & (CHANHASHSIZE-1)]; l != nil; l = tl l)		if ((hd l).fid == fid)			return hd l;	return nil;} Styxserver.chanfree(srv: self ref Styxserver, c: ref Chan){	slot := c.fid & (CHANHASHSIZE-1);	nl: list of ref Chan;	for (l := srv.chans[slot]; l != nil; l = tl l)		if ((hd l).fid != c.fid)			nl = (hd l) :: nl;	srv.chans[slot] = nl;} Styxserver.chanlist(srv: self ref Styxserver): list of ref Chan{	cl: list of ref Chan;	for (i := 0; i < len srv.chans; i++)		for (l := srv.chans[i]; l != nil; l = tl l)			cl = hd l :: cl;	return cl;} Styxserver.newchan(srv: self ref Styxserver, fid: int): ref Chan{	# fid already in use	if ((c := srv.fidtochan(fid)) != nil)		return nil;	c = ref Chan;	c.qid = Sys->Qid(big 0, 0, Sys->QTFILE);	c.open = 0;	c.mode = 0;	c.fid = fid;	slot := fid & (CHANHASHSIZE-1);	srv.chans[slot] = c :: srv.chans[slot];	return c;} devdir(nil: ref Chan, qid: Sys->Qid, name: string, length: big,				user: string, perm: int): Sys->Dir{	d: Sys->Dir;	d.name = name;	d.qid = qid;	d.dtype = 'X';	d.dev = 0;		# XXX what should this be?	d.mode = perm;	if (qid.qtype & Sys->QTDIR)		d.mode |= Sys->DMDIR;	d.atime = starttime;	# XXX should be better than this.	d.mtime = starttime;	d.length = length;	d.uid = user;	d.gid = user;	return d;} readbytes(m: ref Tmsg.Read, d: array of byte): ref Rmsg.Read{	r := ref Rmsg.Read(m.tag, nil);	offset := int m.offset;	if (offset >= len d)		return r;	e := offset + m.count;	if (e > len d)		e = len d;	r.data = d[offset:e];	return r;} readnum(m: ref Tmsg.Read, val, size: int): ref Rmsg.Read{	return readbytes(m, sys->aprint("%-*d", size, val));} readstr(m: ref Tmsg.Read, d: string): ref Rmsg.Read{	return readbytes(m, array of byte d);} dirgenmodule(): Dirgenmod{	return load Dirgenmod "$self";} dirgen(srv: ref Styxserver, c: ref Styxlib->Chan,				tab: array of Dirtab, i: int): (int, Sys->Dir){	d: Sys->Dir;	if (tab == nil || i >= len tab)		return (-1, d);	return (1, devdir(c, tab[i].qid, tab[i].name, tab[i].length, srv.uname, tab[i].perm));} openmode(o: int): int{	OTRUNC, ORCLOSE, OREAD, ORDWR: import Sys;	if(o >= (OTRUNC|ORCLOSE|ORDWR))		return -1;	o &= ~(OTRUNC|ORCLOSE);	if(o > ORDWR)		return -1;	return o;} access := array[] of {8r400, 8r200, 8r600, 8r100};openok(omode, perm: int, uname, funame, nil: string): int{	# XXX what should we do about groups?	# this is inadequate anyway:	# OTRUNC	# user should be allowed to open it if permission	# is allowed to others.	mode: int;	if (uname == funame)		mode = perm;	else		mode = perm << 6; 	t := access[omode & 3];	return ((t & mode) == t);}	 Chan.isdir(c: self ref Chan): int{	return (c.qid.qtype & Sys->QTDIR) != 0;}