diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
| commit | 37da2899f40661e3e9631e497da8dc59b971cbd0 (patch) | |
| tree | cbc6d4680e347d906f5fa7fca73214418741df72 /appl/lib/styxlib.b | |
| parent | 54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff) | |
20060303a
Diffstat (limited to 'appl/lib/styxlib.b')
| -rw-r--r-- | appl/lib/styxlib.b | 453 |
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; +} |
