ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/lib/styxconv/old2new.b/
implement Styxconv;
include "sys.m";
sys: Sys;
include "osys.m";
include "nsys.m";
include "draw.m";
include "styx.m";
nstyx: Styx;
Tmsg, Rmsg: import nstyx;
include "ostyx.m";
ostyx: OStyx;
OTmsg, ORmsg: import ostyx;
include "styxconv.m";
DEBUG: con 0;
# convert from old styx client to new styx server.
# more straightforward than the other way around
# because there's an almost exactly 1-1 mapping
# between message types. (the exception is Tversion,
# but we do that synchronously anyway).
# todo: map qids > ffffffff into 32 bits.
Msize: con nstyx->IOHDRSZ + OSys->ATOMICIO;
Fid: adt
{
fid: int;
isdir: int;
n: int; # size of last new client dirread request.
soff: int; # dir offset on new server.
coff: int; # dir offset on old client.
next: cyclic ref Fid;
extras: array of byte; # packed old styx dir structures
};
Req: adt {
tag: int;
fid: int;
oldtag: int; # if it's a flush.
newfid: int; # if it's a clone
next: cyclic ref Req;
};
tags: ref Req;
fids: ref Fid;
init()
{
sys = load Sys Sys->PATH;
if(sys == nil)
nomod("Sys", Sys->PATH);
nstyx = load Styx Styx->PATH;
if(nstyx == nil)
nomod("Styx", Styx->PATH);
ostyx = load OStyx OStyx->PATH;
if(ostyx == nil)
nomod("OStyx", OStyx->PATH);
ostyx->init();
nstyx->init();
}
styxconv(oldclient, newsrv: ref Sys->FD)
{
oldtmsg := chan of ref OTmsg;
newrmsg := chan of ref Rmsg;
killpids := chan[2] of int;
spawn readoldtmsgs(killpids, oldclient, oldtmsg);
spawn readnewrmsgs(killpids, newsrv, newrmsg);
# XXX difficulty: what happens if the server isn't responding
# and the client hangs up? we won't know about it.
# but we don't want to know about normal t-messages
# piling up either, so we don't want to alt on oldtmsg too.
NTsend(newsrv, ref Tmsg.Version(nstyx->NOTAG, Msize, "9P2000"));
pick nrm := <-newrmsg {
Version =>
if(DEBUG)
sys->fprint(sys->fildes(2), " <- %s\n", nrm.text());
if(nrm.msize < Msize)
fatal("message size too small");
Error =>
fatal("versioning failed: " + nrm.ename);
* =>
fatal("bad response to Tversion: " + nrm.text());
}
converting:
for(;;)alt{
otm := <-oldtmsg =>
if(DEBUG)
sys->fprint(sys->fildes(2), "-> %s\n", ostyx->tmsg2s(otm));
if(otm == nil || tagof(otm) == tagof(OTmsg.Readerror))
break converting;
oc2ns(otm, oldclient, newsrv);
nrm := <-newrmsg =>
if(DEBUG)
sys->fprint(sys->fildes(2), " <- %s\n", nrm.text());
if(nrm == nil || tagof(nrm) == tagof(Rmsg.Readerror))
break converting;
t := looktag(nrm.tag);
if(t == nil){
warning("reply by new-server to non-existent tag");
break;
}
ns2oc(t, nrm, oldclient);
deletetag(nrm.tag);
}
kill(<-killpids);
kill(<-killpids);
}
# T messages: forward on or reply immediately
oc2ns(tm0: ref OTmsg, oldclient, newsrv: ref Sys->FD)
{
ntm: ref Tmsg;
t := ref Req(tm0.tag, -1, -1, -1, nil);
pick tm := tm0{
Nop =>
ORsend(oldclient, ref ORmsg.Nop(tm.tag));
return;
Attach =>
t.fid = tm.fid;
ntm = ref Tmsg.Attach(tm.tag, tm.fid, nstyx->NOFID, tm.uname, tm.aname);
Clone =>
t.fid = tm.fid;
t.newfid = tm.newfid;
ntm = ref Tmsg.Walk(tm.tag, tm.fid, tm.newfid, nil);
Walk =>
t.fid = tm.fid;
ntm = ref Tmsg.Walk(tm.tag, tm.fid, tm.fid, array[] of {tm.name});
Flush =>
t.oldtag = tm.oldtag;
ntm = ref Tmsg.Flush(tm.tag, tm.oldtag);
Open =>
t.fid = tm.fid;
ntm = ref Tmsg.Open(tm.tag, tm.fid, tm.mode);
Create =>
t.fid = tm.fid;
ntm = ref Tmsg.Create(tm.tag, tm.fid, tm.name, tm.perm, tm.mode);
Read =>
t.fid = tm.fid;
fp := findfid(tm.fid);
count := tm.count;
offset := tm.offset;
if(fp.isdir){
count = (count/OStyx->DIRLEN)*OStyx->DIRLEN;
# if we got some extra entries last time,
# then send 'em back this time.
extras := fp.extras;
if(len extras > 0){
if(count > len extras)
count = len extras;
ORsend(oldclient, ref ORmsg.Read(tm.tag, t.fid, fp.extras[0:count]));
fp.extras = extras[count:];
fp.coff += count;
return;
}
fp.n = count;
if(int offset != fp.coff){
ORsend(oldclient, ref ORmsg.Error(tm.tag, "unexpected offset in dirread"));
return;
}
offset = big fp.soff;
}
ntm = ref Tmsg.Read(tm.tag, tm.fid, offset, count);
Write =>
t.fid = tm.fid;
ntm = ref Tmsg.Write(tm.tag, tm.fid, tm.offset, tm.data);
Clunk =>
t.fid = tm.fid;
ntm = ref Tmsg.Clunk(tm.tag, tm.fid);
Remove =>
t.fid = tm.fid;
ntm = ref Tmsg.Remove(tm.tag, tm.fid);
Stat =>
t.fid = tm.fid;
ntm = ref Tmsg.Stat(tm.tag, tm.fid);
Wstat =>
t.fid = tm.fid;
ntm = ref Tmsg.Wstat(tm.tag, tm.fid, od2nd(tm.stat));
* =>
fatal("bad T message");
}
storetag(t);
NTsend(newsrv, ntm);
}
# R messages: new to old
ns2oc(t: ref Req, nrm0: ref Rmsg, oldclient: ref Sys->FD)
{
rm: ref ORmsg;
pick nrm := nrm0{
Error =>
rm = ref ORmsg.Error(nrm.tag, nrm.ename);
Flush =>
rm = ref ORmsg.Flush(nrm.tag);
deletetag(t.oldtag);
Walk =>
if(len nrm.qids == 0){
clonefid(t.fid, t.newfid);
rm = ref ORmsg.Clone(nrm.tag, t.fid);
}else{
q := nrm.qids[0];
setfid(t.fid, q);
rm = ref ORmsg.Walk(nrm.tag, t.fid, nq2oq(q));
}
Open =>
setfid(t.fid, nrm.qid);
rm = ref ORmsg.Open(nrm.tag, t.fid, nq2oq(nrm.qid));
Create =>
setfid(t.fid, nrm.qid);
rm = ref ORmsg.Create(nrm.tag, t.fid, nq2oq(nrm.qid));
Read =>
fp := findfid(t.fid);
data := nrm.data;
if(fp != nil && fp.isdir){
data = nds2ods(data);
if(len data > fp.n){
fp.extras = data[fp.n:];
data = data[0:fp.n];
}
fp.coff += len data;
fp.soff += len nrm.data;
}
rm = ref ORmsg.Read(nrm.tag, t.fid, data);
Write =>
rm = ref ORmsg.Write(nrm.tag, t.fid, nrm.count);
Clunk =>
deletefid(t.fid);
rm = ref ORmsg.Clunk(nrm.tag, t.fid);
Remove =>
deletefid(t.fid);
rm = ref ORmsg.Remove(nrm.tag, t.fid);
Stat =>
rm = ref ORmsg.Stat(nrm.tag, t.fid, nd2od(nrm.stat));
Wstat =>
rm = ref ORmsg.Wstat(nrm.tag, t.fid);
Attach =>
newfid(t.fid, nrm.qid.qtype & NSys->QTDIR);
rm = ref ORmsg.Attach(nrm.tag, t.fid, nq2oq(nrm.qid));
* =>
fatal("bad R message");
}
ORsend(oldclient, rm);
}
storetag(t: ref Req)
{
t.next = tags;
tags = t;
}
looktag(tag: int): ref Req
{
for(t := tags; t != nil; t = t.next)
if(t.tag == tag)
return t;
return nil;
}
deletetag(tag: int)
{
prev: ref Req;
t := tags;
while(t != nil){
if(t.tag == tag){
next := t.next;
t.next = nil;
if(prev != nil)
prev.next = next;
else
tags = next;
t = next;
}else{
prev = t;
t = t.next;
}
}
}
newfid(fid: int, isdir: int): ref Fid
{
f := ref Fid;
f.fid = fid;
f.isdir = isdir;
f.n = f.soff = f.coff = 0;
f.next = fids;
fids = f;
return f;
}
clonefid(ofid: int, fid: int): ref Fid
{
if((f := findfid(ofid)) != nil)
return newfid(fid, f.isdir);
warning("clone of non-existent fid");
return newfid(fid, 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;
}
lf = f;
}
}
findfid(fid: int): ref Fid
{
for(f := fids; f != nil && f.fid != fid; f = f.next)
;
return f;
}
setfid(fid: int, qid: NSys->Qid)
{
if((f := findfid(fid)) != nil)
f.isdir = qid.qtype & NSys->QTDIR;
}
om2nm(om: int): int
{
# DMDIR == CHDIR
return om;
}
nm2om(m: int): int
{
# DMDIR == CHDIR
return m&~(NSys->DMAPPEND|NSys->DMEXCL|NSys->DMAUTH);
}
oq2nq(oq: OSys->Qid): NSys->Qid
{
q: NSys->Qid;
isdir := oq.path&OSys->CHDIR;
q.path = big (oq.path&~OSys->CHDIR);
q.vers = oq.vers;
q.qtype = 0;
if(isdir)
q.qtype |= NSys->QTDIR;
return q;
}
nq2oq(q: NSys->Qid): OSys->Qid
{
oq: OSys->Qid;
isdir := q.qtype&NSys->QTDIR;
oq.path = int q.path;
oq.vers = q.vers;
if(isdir)
oq.path |= OSys->CHDIR;
return oq;
}
od2nd(od: OSys->Dir): NSys->Dir
{
d: NSys->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: NSys->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;
}
nds2ods(ob: array of byte): array of byte
{
i := 0;
n := 0;
ds: list of NSys->Dir;
while(i < len ob){
(size, d) := nstyx->unpackdir(ob[i:]);
if(size == 0)
break;
ds = d :: ds;
i += size;
n++;
}
b := array[OStyx->DIRLEN * n] of byte;
for(i = (n - 1) * OStyx->DIRLEN; i >= 0; i -= OStyx->DIRLEN){
ostyx->convD2M(b[i:], nd2od(hd ds));
ds = tl ds;
}
return b;
}
NTsend(fd: ref Sys->FD, ntm: ref Tmsg)
{
if(DEBUG)
sys->fprint(sys->fildes(2), " -> %s\n", ntm.text());
s := ntm.pack();
sys->write(fd, s, len s);
}
ORsend(fd: ref Sys->FD, orm: ref ORmsg)
{
if(DEBUG)
sys->fprint(sys->fildes(2), "<- %s\n", ostyx->rmsg2s(orm));
s := array[OStyx->MAXRPC] of byte;
n := ostyx->rmsg2d(orm, s);
if(n > 0)
sys->write(fd, s, n);
}
readoldtmsgs(pidc: chan of int, oldclient: ref Sys->FD, oldtmsg: chan of ref OTmsg)
{
pidc <-= sys->pctl(0, nil);
for(;;){
oldtmsg <-= OTmsg.read(oldclient);
}
}
readnewrmsgs(pidc: chan of int, newsrv: ref Sys->FD, newrmsg: chan of ref Rmsg)
{
pidc <-= sys->pctl(0, nil);
for(;;){
newrmsg <-= Rmsg.read(newsrv, Msize);
}
}
warning(err: string)
{
sys->fprint(sys->fildes(2), "warning: %s\n", err);
}
fatal(err: string)
{
sys->fprint(sys->fildes(2), "%s\n", err);
exit;
}
nomod(mod: string, path: string)
{
fatal(sys->sprint("can't load %s(%s): %r", mod, path));
}
kill(pid: int)
{
sys->fprint(sys->open("#p/"+string pid+"/ctl", Sys->OWRITE), "kill");
}