summaryrefslogtreecommitdiff
path: root/appl/lib/styxservers.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/lib/styxservers.b')
-rw-r--r--appl/lib/styxservers.b605
1 files changed, 605 insertions, 0 deletions
diff --git a/appl/lib/styxservers.b b/appl/lib/styxservers.b
new file mode 100644
index 00000000..034cd4b7
--- /dev/null
+++ b/appl/lib/styxservers.b
@@ -0,0 +1,605 @@
+implement Styxservers;
+
+#
+# Copyright © 1999 Vita Nuova Limited. All rights reserved.
+# Revisions copyright © 2000-2003 Vita Nuova Holdings Limited. All rights reserved.
+#
+# Derived from Roger Peppe's Styxlib by Martin C. Atkins, 2001/2002 by
+# adding new helper functions, and then removing Dirgenmod and its helpers
+#
+# Further modified by Roger Peppe to simplify the interface by
+# adding the Navigator/Navop channel interface and making other changes,
+# including using the Styx module
+#
+# converted to revised Styx at Vita Nuova
+# further revised August/September 2002
+#
+# TO DO:
+# - directory reading interface revision?
+#
+
+include "sys.m";
+ sys: Sys;
+
+include "styx.m";
+ styx: Styx;
+ Tmsg, Rmsg: import styx;
+
+include "styxservers.m";
+
+CHANHASHSIZE: con 32;
+DIRREADSIZE: con Styx->STATFIXLEN+4*20; # ``reasonable'' chunk for reading directories
+
+debug := 0;
+
+init(styxmod: Styx)
+{
+ sys = load Sys Sys->PATH;
+ styx = styxmod;
+}
+
+traceset(d: int)
+{
+ debug = d;
+}
+
+Styxserver.new(fd: ref Sys->FD, t: ref Navigator, rootpath: big): (chan of ref Tmsg, ref Styxserver)
+{
+ tchan := chan of ref Tmsg;
+ srv := ref Styxserver(fd, array[CHANHASHSIZE] of list of ref Fid, chan[1] of int, t, rootpath, 0, nil);
+
+ sync := chan of int;
+ spawn tmsgreader(fd, srv, tchan, sync);
+ <-sync;
+ return (tchan, srv);
+}
+
+tmsgreader(fd: ref Sys->FD, srv: ref Styxserver, tchan: chan of ref Tmsg, sync: chan of int)
+{
+ if(debug)
+ sys->pctl(Sys->NEWFD|Sys->NEWNS, fd.fd :: 2 :: nil);
+ else
+ sys->pctl(Sys->NEWFD|Sys->NEWNS, fd.fd :: nil);
+ sync <-= 1;
+ fd = sys->fildes(fd.fd);
+ m: ref Tmsg;
+ do {
+ m = Tmsg.read(fd, srv.msize);
+ if(debug && m != nil)
+ sys->fprint(sys->fildes(2), "<- %s\n", m.text());
+ tchan <-= m;
+ } while(m != nil && tagof(m) != tagof(Tmsg.Readerror));
+}
+
+Fid.clone(oc: self ref Fid, c: ref Fid): ref Fid
+{
+ # c.fid not touched, other values copied from c
+ c.path = oc.path;
+ c.qtype = oc.qtype;
+ c.isopen = oc.isopen;
+ c.mode = oc.mode;
+ c.doffset = oc.doffset;
+ c.uname = oc.uname;
+ c.param = oc.param;
+ c.data = oc.data;
+ return c;
+}
+
+Fid.walk(c: self ref Fid, qid: Sys->Qid)
+{
+ c.path = qid.path;
+ c.qtype = qid.qtype;
+}
+
+Fid.open(c: self ref Fid, mode: int, qid: Sys->Qid)
+{
+ c.isopen = 1;
+ c.mode = mode;
+ c.doffset = (0, 0);
+ c.path = qid.path;
+ c.qtype = qid.qtype;
+}
+
+Styxserver.reply(srv: self ref Styxserver, m: ref Rmsg): int
+{
+ if(debug)
+ sys->fprint(sys->fildes(2), "-> %s\n", m.text());
+ if(srv.replychan != nil){
+ srv.replychan <-= m;
+ return 0;
+ }
+ return srv.replydirect(m);
+}
+
+Styxserver.replydirect(srv: self ref Styxserver, m: ref Rmsg): int
+{
+ if(srv.msize == 0)
+ m = ref Rmsg.Error(m.tag, "Tversion not seen");
+ d := m.pack();
+ if(srv.msize != 0 && len d > srv.msize){
+ m = ref Rmsg.Error(m.tag, "Styx reply didn't fit");
+ d = m.pack();
+ }
+ return sys->write(srv.fd, d, len d);
+}
+
+Styxserver.attach(srv: self ref Styxserver, m: ref Tmsg.Attach): ref Fid
+{
+ (d, err) := srv.t.stat(srv.rootpath);
+ if(d == nil) {
+ srv.reply(ref Rmsg.Error(m.tag, err));
+ return nil;
+ }
+ if((d.qid.qtype & Sys->QTDIR) == 0) {
+ srv.reply(ref Rmsg.Error(m.tag, Enotdir));
+ return nil;
+ }
+ c := srv.newfid(m.fid);
+ if(c == nil) {
+ srv.reply(ref Rmsg.Error(m.tag, Einuse));
+ return nil;
+ }
+ c.uname = m.uname;
+ c.param = m.aname;
+ c.path = d.qid.path;
+ c.qtype = d.qid.qtype;
+ srv.reply(ref Rmsg.Attach(m.tag, d.qid));
+ return c;
+}
+
+walk1(n: ref Navigator, c: ref Fid, name: string): (ref Sys->Dir, string)
+{
+ (d, err) := n.stat(c.path);
+ if(d == nil)
+ return (nil, err);
+ if((d.qid.qtype & Sys->QTDIR) == 0)
+ return (nil, Enotdir);
+ if(!openok(c.uname, Styx->OEXEC, d.mode, d.uid, d.gid))
+ return (nil, Eperm);
+ (d, err) = n.walk(d.qid.path, name);
+ if(d == nil)
+ return (nil, err);
+ return (d, nil);
+}
+
+Styxserver.walk(srv: self ref Styxserver, m: ref Tmsg.Walk): ref Fid
+{
+ c := srv.getfid(m.fid);
+ if(c == nil) {
+ srv.reply(ref Rmsg.Error(m.tag, Ebadfid));
+ return nil;
+ }
+ if(c.isopen) {
+ srv.reply(ref Rmsg.Error(m.tag, Eopen));
+ return nil;
+ }
+ if(m.newfid != m.fid){
+ nc := srv.newfid(m.newfid);
+ if(nc == nil){
+ srv.reply(ref Rmsg.Error(m.tag, Einuse));
+ return nil;
+ }
+ c = c.clone(nc);
+ }
+ qids := array[len m.names] of Sys->Qid;
+ oldpath := c.path;
+ oldqtype := c.qtype;
+ for(i := 0; i < len m.names; i++){
+ (d, err) := walk1(srv.t, c, m.names[i]);
+ if(d == nil){
+ c.path = oldpath; # restore c
+ c.qtype = oldqtype;
+ if(m.newfid != m.fid)
+ srv.delfid(c);
+ if(i == 0)
+ srv.reply(ref Rmsg.Error(m.tag, err));
+ else
+ srv.reply(ref Rmsg.Walk(m.tag, qids[0:i]));
+ return nil;
+ }
+ c.walk(d.qid);
+ qids[i] = d.qid;
+ }
+ srv.reply(ref Rmsg.Walk(m.tag, qids));
+ return c;
+}
+
+Styxserver.canopen(srv: self ref Styxserver, m: ref Tmsg.Open): (ref Fid, int, ref Sys->Dir, string)
+{
+ c := srv.getfid(m.fid);
+ if(c == nil)
+ return (nil, 0, nil, Ebadfid);
+ if(c.isopen)
+ return (nil, 0, nil, Eopen);
+ (f, err) := srv.t.stat(c.path);
+ if(f == nil)
+ return (nil, 0, nil, err);
+ mode := openmode(m.mode);
+ if(mode == -1)
+ return (nil, 0, nil, Ebadarg);
+ if(mode != Sys->OREAD && f.qid.qtype & Sys->QTDIR)
+ return (nil, 0, nil, Eperm);
+ if(!openok(c.uname, m.mode, f.mode, f.uid, f.gid))
+ return (nil, 0, nil, Eperm);
+ if(m.mode & Sys->ORCLOSE) {
+ (dir, nil) := srv.t.walk(c.path, "..");
+ if(dir == nil || dir.qid.path == f.qid.path && dir.qid.qtype == f.qid.qtype || # can't remove root directory
+ !openok(c.uname, Sys->OWRITE, dir.mode, dir.uid, dir.gid))
+ return (nil, 0, nil, Eperm);
+ mode |= Sys->ORCLOSE;
+ }
+ return (c, mode, f, err);
+}
+
+Styxserver.open(srv: self ref Styxserver, m: ref Tmsg.Open): ref Fid
+{
+ (c, mode, f, err) := srv.canopen(m);
+ if(c == nil){
+ srv.reply(ref Rmsg.Error(m.tag, err));
+ return nil;
+ }
+ c.open(mode, f.qid);
+ srv.reply(ref Rmsg.Open(m.tag, f.qid, srv.iounit()));
+ return c;
+}
+
+Styxserver.cancreate(srv: self ref Styxserver, m: ref Tmsg.Create): (ref Fid, int, ref Sys->Dir, string)
+{
+ c := srv.getfid(m.fid);
+ if(c == nil)
+ return (nil, 0, nil, Ebadfid);
+ if(c.isopen)
+ return (nil, 0, nil, Eopen);
+ (d, err) := srv.t.stat(c.path);
+ if(d == nil)
+ return (nil, 0, nil, err);
+ if((d.mode & Sys->DMDIR) == 0)
+ return (nil, 0, nil, Enotdir);
+ if(m.name == "")
+ return (nil, 0, nil, Ename);
+ if(m.name == "." || m.name == "..")
+ return (nil, 0, nil, Edot);
+ if(!openok(c.uname, Sys->OWRITE, d.mode, d.uid, d.gid))
+ return (nil, 0, nil, Eperm);
+ if(srv.t.walk(d.qid.path, m.name).t0 != nil)
+ return (nil, 0, nil, Eexists);
+ if((mode := openmode(m.mode)) == -1)
+ return (nil, 0, nil, Ebadarg);
+ mode |= m.mode & Sys->ORCLOSE; # can create, so directory known to be writable
+ f := ref Sys->zerodir;
+ if(m.perm & Sys->DMDIR){
+ f.mode = m.perm & (~8r777 | (d.mode & 8r777));
+ f.qid.qtype = Sys->QTDIR;
+ }else{
+ f.mode = m.perm & (~8r666 | (d.mode & 8r666));
+ f.qid.qtype = Sys->QTFILE;
+ }
+ f.name = m.name;
+ f.uid = c.uname;
+ f.muid = c.uname;
+ f.gid = d.gid;
+ f.dtype = d.dtype;
+ f.dev = d.dev;
+ # caller must supply atime, mtime, qid.path
+ return (c, mode, f, nil);
+}
+
+Styxserver.canread(srv: self ref Styxserver, m: ref Tmsg.Read): (ref Fid, string)
+{
+ c := srv.getfid(m.fid);
+ if(c == nil)
+ return (nil, Ebadfid);
+ if(!c.isopen)
+ return (nil, Enotopen);
+ mode := c.mode & 3;
+ if(mode != Sys->OREAD && mode != Sys->ORDWR) # readable modes
+ return (nil, Eaccess);
+ if(m.count < 0 || m.count > srv.msize-Styx->IOHDRSZ)
+ return (nil, Ecount);
+ if(m.offset < big 0)
+ return (nil, Eoffset);
+ return (c, nil);
+}
+
+Styxserver.read(srv: self ref Styxserver, m: ref Tmsg.Read): ref Fid
+{
+ (c, err) := srv.canread(m);
+ if(c == nil){
+ srv.reply(ref Rmsg.Error(m.tag, err));
+ return nil;
+ }
+ if((c.qtype & Sys->QTDIR) == 0) {
+ srv.reply(ref Rmsg.Error(m.tag, Eperm));
+ return nil;
+ }
+ if(m.count <= 0){
+ srv.reply(ref Rmsg.Read(m.tag, nil));
+ return c;
+ }
+ a := array[m.count] of byte;
+ (offset, index) := c.doffset;
+ if(int m.offset != offset){ # rescan from the beginning
+ offset = 0;
+ index = 0;
+ }
+ p := 0;
+Dread:
+ while((d := srv.t.readdir(c.path, index, (m.count+DIRREADSIZE-1)/DIRREADSIZE)) != nil && (nd := len d) > 0){
+ for(i := 0; i < nd; i++) {
+ size := styx->packdirsize(*d[i]);
+ offset += size;
+ index++;
+ if(offset < int m.offset)
+ continue;
+ if((m.count -= size) < 0){ # won't fit, save state for next time
+ offset -= size;
+ index--;
+ break Dread;
+ }
+ de := styx->packdir(*d[i]);
+ a[p:] = de;
+ p += size;
+ }
+ }
+ c.doffset = (offset, index);
+ srv.reply(ref Rmsg.Read(m.tag, a[0:p]));
+ return c;
+}
+
+Styxserver.canwrite(srv: self ref Styxserver, m: ref Tmsg.Write): (ref Fid, string)
+{
+ c := srv.getfid(m.fid);
+ if(c == nil)
+ return (nil, Ebadfid);
+ if(!c.isopen)
+ return (nil, Enotopen);
+ if(c.qtype & Sys->QTDIR)
+ return (nil, Eperm);
+ mode := c.mode & 3;
+ if(mode != Sys->OWRITE && mode != Sys->ORDWR) # writable modes
+ return (nil, Eaccess);
+ if(m.offset < big 0)
+ return (nil, Eoffset);
+ # could check len m.data > iounit, but since we've got it now, it doesn't matter
+ return (c, nil);
+}
+
+Styxserver.stat(srv: self ref Styxserver, m: ref Tmsg.Stat)
+{
+ c := srv.getfid(m.fid);
+ if(c == nil) {
+ srv.reply(ref Rmsg.Error(m.tag, Ebadfid));
+ return;
+ }
+ (d, err) := srv.t.stat(c.path);
+ if(d == nil) {
+ srv.reply(ref Rmsg.Error(m.tag, err));
+ return;
+ }
+ srv.reply(ref Rmsg.Stat(m.tag, *d));
+}
+
+Styxserver.canremove(srv: self ref Styxserver, m: ref Tmsg.Remove): (ref Fid, big, string)
+{
+ c := srv.getfid(m.fid);
+ if(c == nil)
+ return (nil, big 0, Ebadfid);
+ (dir, nil) := srv.t.walk(c.path, ".."); # this relies on .. working for non-directories
+ if(dir == nil)
+ return (nil, big 0, "can't find parent directory");
+ if(dir.qid.path == c.path && dir.qid.qtype == c.qtype || # can't remove root directory
+ !openok(c.uname, Sys->OWRITE, dir.mode, dir.uid, dir.gid))
+ return (nil, big 0, Eperm);
+ return (c, dir.qid.path, nil);
+}
+
+Styxserver.remove(srv: self ref Styxserver, m: ref Tmsg.Remove): ref Fid
+{
+ c := srv.getfid(m.fid);
+ if(c == nil) {
+ srv.reply(ref Rmsg.Error(m.tag, Ebadfid));
+ return nil;
+ }
+ srv.delfid(c); # Remove always clunks the fid
+ srv.reply(ref Rmsg.Error(m.tag, Eperm));
+ return c;
+}
+
+Styxserver.clunk(srv: self ref Styxserver, m: ref Tmsg.Clunk): ref Fid
+{
+ c := srv.getfid(m.fid);
+ if(c == nil) {
+ srv.reply(ref Rmsg.Error(m.tag, Ebadfid));
+ return nil;
+ }
+ srv.delfid(c);
+ srv.reply(ref Rmsg.Clunk(m.tag));
+ return c;
+}
+
+Styxserver.default(srv: self ref Styxserver, gm: ref Tmsg)
+{
+ if(gm == nil) {
+ srv.t.c <-= nil;
+ exit;
+ }
+ pick m := gm {
+ Readerror =>
+ srv.t.c <-= nil;
+ exit;
+ Version =>
+ if(srv.msize <= 0)
+ srv.msize = Styx->MAXRPC;
+ (msize, version) := styx->compatible(m, srv.msize, Styx->VERSION);
+ if(msize < 256){
+ srv.reply(ref Rmsg.Error(m.tag, "message size too small"));
+ break;
+ }
+ srv.msize = msize;
+ srv.reply(ref Rmsg.Version(m.tag, msize, version));
+ Auth =>
+ srv.reply(ref Rmsg.Error(m.tag, "authentication not required"));
+ Flush =>
+ srv.reply(ref Rmsg.Flush(m.tag));
+ Walk =>
+ srv.walk(m);
+ Open =>
+ srv.open(m);
+ Create =>
+ srv.reply(ref Rmsg.Error(m.tag, Eperm));
+ Read =>
+ srv.read(m);
+ Write =>
+ srv.reply(ref Rmsg.Error(m.tag, Eperm));
+ Clunk =>
+ srv.clunk(m);
+ # to delete on ORCLOSE:
+ # c := srv.clunk(m);
+ # if(c != nil && c.mode & Sys->ORCLOSE)
+ # srv.doremove(c);
+ Stat =>
+ srv.stat(m);
+ Remove =>
+ srv.remove(m);
+ Wstat =>
+ srv.reply(ref Rmsg.Error(m.tag, Eperm));
+ Attach =>
+ srv.attach(m);
+ * =>
+ sys->fprint(sys->fildes(2), "styxservers: unhandled Tmsg tag %d - should not happen\n", tagof gm);
+ raise "fail: unhandled case";
+ }
+}
+
+Styxserver.iounit(srv: self ref Styxserver): int
+{
+ n := srv.msize - Styx->IOHDRSZ;
+ if(n <= 0)
+ return 0; # unknown
+ return n;
+}
+
+Styxserver.getfid(srv: self ref Styxserver, fid: int): ref Fid
+{
+ # the list is safe to use without locking
+ for(l := srv.fids[fid & (CHANHASHSIZE-1)]; l != nil; l = tl l)
+ if((hd l).fid == fid)
+ return hd l;
+ return nil;
+}
+
+Styxserver.delfid(srv: self ref Styxserver, c: ref Fid)
+{
+ slot := c.fid & (CHANHASHSIZE-1);
+ nl: list of ref Fid;
+ srv.fidlock <-= 1;
+ for(l := srv.fids[slot]; l != nil; l = tl l)
+ if((hd l).fid != c.fid)
+ nl = (hd l) :: nl;
+ srv.fids[slot] = nl;
+ <-srv.fidlock;
+}
+
+Styxserver.allfids(srv: self ref Styxserver): list of ref Fid
+{
+ cl: list of ref Fid;
+ srv.fidlock <-= 1;
+ for(i := 0; i < len srv.fids; i++)
+ for(l := srv.fids[i]; l != nil; l = tl l)
+ cl = hd l :: cl;
+ <-srv.fidlock;
+ return cl;
+}
+
+Styxserver.newfid(srv: self ref Styxserver, fid: int): ref Fid
+{
+ srv.fidlock <-= 1;
+ if((c := srv.getfid(fid)) != nil){
+ <-srv.fidlock;
+ return nil; # illegal: fid in use
+ }
+ c = ref Fid;
+ c.path = big -1;
+ c.qtype = 0;
+ c.isopen = 0;
+ c.mode = 0;
+ c.fid = fid;
+ c.doffset = (0, 0);
+ slot := fid & (CHANHASHSIZE-1);
+ srv.fids[slot] = c :: srv.fids[slot];
+ <-srv.fidlock;
+ return c;
+}
+
+readstr(m: ref Tmsg.Read, d: string): ref Rmsg.Read
+{
+ return readbytes(m, array of byte d);
+}
+
+readbytes(m: ref Tmsg.Read, d: array of byte): ref Rmsg.Read
+{
+ r := ref Rmsg.Read(m.tag, nil);
+ if(m.offset >= big len d || m.offset < big 0)
+ return r;
+ offset := int m.offset;
+ e := offset + m.count;
+ if(e > len d)
+ e = len d;
+ r.data = d[offset:e];
+ return r;
+}
+
+Navigator.new(c: chan of ref Navop): ref Navigator
+{
+ return ref Navigator(c, chan of (ref Sys->Dir, string));
+}
+
+Navigator.stat(t: self ref Navigator, q: big): (ref Sys->Dir, string)
+{
+ t.c <-= ref Navop.Stat(t.reply, q);
+ return <-t.reply;
+}
+
+Navigator.walk(t: self ref Navigator, q: big, name: string): (ref Sys->Dir, string)
+{
+ t.c <-= ref Navop.Walk(t.reply, q, name);
+ return <-t.reply;
+}
+
+Navigator.readdir(t: self ref Navigator, q: big, offset, count: int): array of ref Sys->Dir
+{
+ a := array[count] of ref Sys->Dir;
+ t.c <-= ref Navop.Readdir(t.reply, q, offset, count);
+ i := 0;
+ while((d := (<-t.reply).t0) != nil)
+ if(i < count)
+ a[i++] = d;
+ if(i == 0)
+ return nil;
+ return a[0:i];
+}
+
+openmode(o: int): int
+{
+ OTRUNC, ORCLOSE, OREAD, ORDWR: import Sys;
+ o &= ~(OTRUNC|ORCLOSE);
+ if(o > ORDWR)
+ return -1;
+ return o;
+}
+
+access := array[] of {8r400, 8r200, 8r600, 8r100};
+openok(uname: string, omode: int, perm: int, fuid: string, fgid: string): int
+{
+ t := access[omode & 3];
+ if(omode & Sys->OTRUNC){
+ if(perm & Sys->DMDIR)
+ return 0;
+ t |= 8r200;
+ }
+ if(uname == fuid && (t&perm) == t)
+ return 1;
+ if(uname == fgid && (t&(perm<<3)) == t)
+ return 1;
+ return (t&(perm<<6)) == t;
+}