implement Styxconv; include "sys.m"; sys: Sys; include "osys.m"; include "draw.m"; include "styx.m"; styx: Styx; Tmsg, Rmsg: import styx; include "ostyx.m"; ostyx: OStyx; OTmsg, ORmsg: import ostyx; include "styxconv.m"; DEBUG: con 0; Fid: adt { fid: int; qid: OSys->Qid; n: int; odri: int; dri: int; next: cyclic ref Fid; }; fids: ref Fid; init() { sys = load Sys Sys->PATH; if(sys == nil) nomod("Sys", Sys->PATH); styx = load Styx Styx->PATH; if(styx == nil) nomod("Styx", Styx->PATH); ostyx = load OStyx OStyx->PATH; if(ostyx == nil) nomod("OStyx", OStyx->PATH); styx->init(); } nomod(mod: string, path: string) { fatal(sys->sprint("can't load %s(%s): %r", mod, path)); } fatal(err: string) { sys->fprint(sys->fildes(2), "%s\n", err); exit; } newfid(fid: int, qid: OSys->Qid): ref Fid { f := ref Fid; f.fid = fid; f.qid = qid; f.n = f.odri = f.dri = 0; f.next = fids; fids = f; return f; } clonefid(ofid: int, fid: int): ref Fid { if((f := findfid(ofid)) != nil) return newfid(fid, f.qid); return newfid(fid, (0, 0)); } deletefid(fid: int) { lf: ref Fid; for(f := fids; f != nil; f = f.next) if(f.fid == fid){ if(lf == nil) fids = f.next; else lf.next = f.next; return; } } findfid(fid: int): ref Fid { for(f := fids; f != nil && f.fid != fid; f = f.next) ; return f; } setfid(fid: int, qid: OSys->Qid) { if((f := findfid(fid)) != nil) f.qid = qid; } om2nm(om: int): int { # DMDIR == CHDIR return om; } nm2om(m: int): int { # DMDIR == CHDIR return m&~(Sys->DMAPPEND|Sys->DMEXCL|Sys->DMAUTH); } oq2nq(oq: OSys->Qid): Sys->Qid { q: Sys->Qid; isdir := oq.path&OSys->CHDIR; q.path = big (oq.path&~OSys->CHDIR); q.vers = oq.vers; q.qtype = 0; if(isdir) q.qtype |= Sys->QTDIR; return q; } nq2oq(q: Sys->Qid): OSys->Qid { oq: OSys->Qid; isdir := q.qtype&Sys->QTDIR; oq.path = int q.path; oq.vers = q.vers; if(isdir) oq.path |= OSys->CHDIR; return oq; } od2nd(od: OSys->Dir): Sys->Dir { d: Sys->Dir; d.name = od.name; d.uid = od.uid; d.gid = od.gid; d.muid = od.uid; d.qid = oq2nq(od.qid); d.mode = om2nm(od.mode); d.atime = od.atime; d.mtime = od.mtime; d.length = big od.length; d.dtype = od.dtype; d.dev = od.dev; return d; } nd2od(d: Sys->Dir): OSys->Dir { od: OSys->Dir; od.name = d.name; od.uid = d.uid; od.gid = d.gid; od.qid = nq2oq(d.qid); od.mode = nm2om(d.mode); od.atime = d.atime; od.mtime = d.mtime; od.length = int d.length; od.dtype = d.dtype; od.dev = d.dev; return od; } ods2nds(fp: ref Fid, ob: array of byte): array of byte { od: OSys->Dir; m := len ob; if(m % OStyx->DIRLEN != 0) fatal(sys->sprint("bad dir len %d", m)); m /= OStyx->DIRLEN; n := 0; p := ob; for(i := 0; i < m; i++){ (p, od) = ostyx->convM2D(p); d := od2nd(od); nn := styx->packdirsize(d); if(n+nn > fp.n) # might just happen with long file names break; n += nn; } m = i; fp.odri += m*OStyx->DIRLEN; fp.dri += n; b := array[n] of byte; n = 0; p = ob; for(i = 0; i < m; i++){ (p, od) = ostyx->convM2D(p); d := od2nd(od); q := styx->packdir(d); nn := len q; b[n: ] = q[0: nn]; n += nn; } return b; } Tsend(fd: ref Sys->FD, otm: ref OTmsg): int { if(DEBUG) sys->print("OT: %s\n", ostyx->tmsg2s(otm)); s := array[OStyx->MAXRPC] of byte; n := ostyx->tmsg2d(otm, s); if(n < 0) return -1; return sys->write(fd, s, n); } Rsend(fd: ref Sys->FD, rm: ref Rmsg): int { if(DEBUG) sys->print("NR: %s\n", rm.text()); s := rm.pack(); if(s == nil) return -1; return sys->write(fd, s, len s); } Trecv(fd: ref Sys->FD): ref Tmsg { tm := Tmsg.read(fd, Styx->MAXRPC); if(tm == nil) exit; if(DEBUG) sys->print("NT: %s\n", tm.text()); return tm; } Rrecv(fd: ref Sys->FD): ref ORmsg { orm := ORmsg.read(fd, OStyx->MAXRPC); if(orm == nil) exit; if(DEBUG) sys->print("OR: %s\n", ostyx->rmsg2s(orm)); return orm; } clunkfid(fd2: ref Sys->FD, tm: ref Tmsg.Walk) { deletefid(tm.newfid); otm := ref OTmsg.Clunk(tm.tag, tm.newfid); Tsend(fd2, otm); os2ns(Rrecv(fd2)); # should check return } # T messages: new to old (mostly) ns2os(tm0: ref Tmsg, fd2: ref Sys->FD): (ref OTmsg, ref Rmsg) { otm: ref OTmsg; rm: ref Rmsg; i, j: int; err: string; otm = nil; rm = nil; pick tm := tm0{ Version => (s, v) := styx->compatible(tm, Styx->MAXRPC, nil); rm = ref Rmsg.Version(tm.tag, s, v); Auth => rm = ref Rmsg.Error(tm.tag, "authorization not required"); Attach => newfid(tm.fid, (0, 0)); otm = ref OTmsg.Attach(tm.tag, tm.fid, tm.uname, tm.aname); Readerror => exit; Flush => otm = ref OTmsg.Flush(tm.tag, tm.oldtag); Walk => # multiple use of tag ok I think n := len tm.names; if(tm.newfid != tm.fid){ clonefid(tm.fid, tm.newfid); if(n != 0){ otm = ref OTmsg.Clone(tm.tag, tm.fid, tm.newfid); Tsend(fd2, otm); os2ns(Rrecv(fd2)); # should check return } } qids := array[n] of Sys->Qid; if(n == 0) otm = ref OTmsg.Clone(tm.tag, tm.fid, tm.newfid); else if(n == 1){ otm = ref OTmsg.Walk(tm.tag, tm.newfid, tm.names[0]); Tsend(fd2, otm); rm = os2ns(Rrecv(fd2)); pick rm0 := rm{ Readerror => exit; Error => if(tm.newfid != tm.fid) clunkfid(fd2, tm); Walk => * => fatal("bad Rwalk message"); } otm = nil; } else{ loop: for(i = 0; i < n; i++){ otm = ref OTmsg.Walk(tm.tag, tm.newfid, tm.names[i]); Tsend(fd2, otm); rm = os2ns(Rrecv(fd2)); pick rm0 := rm{ Readerror => exit; Error => err = rm0.ename; break loop; Walk => qids[i] = rm0.qids[0]; * => fatal("bad Rwalk message"); } } if(i != n && i != 0 && tm.fid == tm.newfid){ for(j = 0; j < i; j++){ otm = ref OTmsg.Walk(tm.tag, tm.fid, ".."); Tsend(fd2, otm); rm = os2ns(Rrecv(fd2)); pick rm0 := rm{ Readerror => exit; Walk => * => fatal("cannot retrieve fid"); } } } if(i != n && tm.newfid != tm.fid) clunkfid(fd2, tm); otm = nil; if(i == 0) rm = ref Rmsg.Error(tm.tag, err); else rm = ref Rmsg.Walk(tm.tag, qids[0: i]); } Open => otm = ref OTmsg.Open(tm.tag, tm.fid, tm.mode); Create => otm = ref OTmsg.Create(tm.tag, tm.fid, tm.perm, tm.mode, tm.name); Read => fp := findfid(tm.fid); count := tm.count; offset := tm.offset; if(fp != nil && fp.qid.path&OSys->CHDIR){ fp.n = count; count = (count/OStyx->DIRLEN)*OStyx->DIRLEN; if(int offset != fp.dri) fatal("unexpected offset in Read"); offset = big fp.odri; } otm = ref OTmsg.Read(tm.tag, tm.fid, count, offset); Write => otm = ref OTmsg.Write(tm.tag, tm.fid, tm.offset, tm.data); Clunk => deletefid(tm.fid); otm = ref OTmsg.Clunk(tm.tag, tm.fid); Remove => deletefid(tm.fid); otm = ref OTmsg.Remove(tm.tag, tm.fid); Stat => otm = ref OTmsg.Stat(tm.tag, tm.fid); Wstat => otm = ref OTmsg.Wstat(tm.tag, tm.fid, nd2od(tm.stat)); * => fatal("bad T message"); } if(otm == nil && rm == nil || otm != nil && rm != nil) fatal("both nil or not in ns2os"); return (otm, rm); } # R messages: old to new os2ns(orm0: ref ORmsg): ref Rmsg { rm: ref Rmsg; rm = nil; pick orm := orm0{ Error => rm = ref Rmsg.Error(orm.tag, orm.err); Flush => rm = ref Rmsg.Flush(orm.tag); Clone => rm = ref Rmsg.Walk(orm.tag, nil); Walk => setfid(orm.fid, orm.qid); rm = ref Rmsg.Walk(orm.tag, array[1] of { * => oq2nq(orm.qid) }); Open => setfid(orm.fid, orm.qid); rm = ref Rmsg.Open(orm.tag, oq2nq(orm.qid), 0); Create => setfid(orm.fid, orm.qid); rm = ref Rmsg.Create(orm.tag, oq2nq(orm.qid), 0); Read => fp := findfid(orm.fid); data := orm.data; if(fp != nil && fp.qid.path&OSys->CHDIR) data = ods2nds(fp, data); rm = ref Rmsg.Read(orm.tag, data); Write => rm = ref Rmsg.Write(orm.tag, orm.count); Clunk => rm = ref Rmsg.Clunk(orm.tag); Remove => rm = ref Rmsg.Remove(orm.tag); Stat => rm = ref Rmsg.Stat(orm.tag, od2nd(orm.stat)); Wstat => rm = ref Rmsg.Wstat(orm.tag); Attach => setfid(orm.fid, orm.qid); rm = ref Rmsg.Attach(orm.tag, oq2nq(orm.qid)); * => fatal("bad R message"); } if(rm == nil) fatal("nil in os2ns"); return rm; } styxconv(fd1: ref Sys->FD, fd2: ref Sys->FD, c: chan of int) { c <-= sys->pctl(0, nil); for(;;){ tm := Trecv(fd1); (otm, rm) := ns2os(tm, fd2); if(otm != nil){ Tsend(fd2, otm); orm := Rrecv(fd2); rm = os2ns(orm); } Rsend(fd1, rm); } }