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; }