code: 9ferno

ref: 25e4f8ae90bd66f124f6b96ffdf70ae00100d4c0
dir: /appl/lib/styxconv/old2new.b/

View raw version
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");
}