summaryrefslogtreecommitdiff
path: root/appl/lib/styxlib.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/lib/styxlib.b')
-rw-r--r--appl/lib/styxlib.b453
1 files changed, 453 insertions, 0 deletions
diff --git a/appl/lib/styxlib.b b/appl/lib/styxlib.b
new file mode 100644
index 00000000..700eb8bb
--- /dev/null
+++ b/appl/lib/styxlib.b
@@ -0,0 +1,453 @@
+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;
+}