shithub: purgatorio

ref: 8a788aea84aad3bfbd5b39d78c1925654f3b9e14
dir: /appl/lib/styxconv/ostyx.b/

View raw version
implement OStyx;

#
# Copyright © 1999 Vita Nuova Limited.  All rights reserved.
#

include "sys.m";
	sys: Sys;
include "osys.m";
include "ostyx.m";

DEBUG: con 0;

CHANHASHSIZE: con 32;

init()
{
	sys = load Sys Sys->PATH;
	gsofar = 0;
	gdata = array[MAXRPC] of {* => byte 0};
}

# note that this implementation fails if we're reading OTmsgs and ORmsgs
# concurrently. luckily we don't need to in styxconv.
gsofar: int;
gdata: array of byte;

ORmsg.read(fd: ref Sys->FD): ref ORmsg
{
	mlen := 0;
	m: ref ORmsg;
	for (;;){
		if(gsofar > 0)
			(mlen, m) = d2rmsg(gdata[0 : gsofar]);
		if(mlen == 0){
			if(gsofar == len gdata){
				ndata := array[MAXRPC] of byte;
				ndata[0:] = gdata;
				gdata = ndata;
			}
			n := sys->read(fd, gdata[gsofar:], len gdata - gsofar);
			if(n <= 0)
				return nil;
			gsofar += n;
		}else if(mlen > 0){
			if(tagof(m) == tagof(OTmsg.Write)) {
				ndata := array[MAXRPC] of byte;
				ndata[0:] = gdata[mlen : gsofar];
				gdata = ndata;
			}else
				gdata[0:] = gdata[mlen : gsofar];
			gsofar -= mlen;
			return m;
		}else
			gsofar = 0;
	}
}

OTmsg.read(fd: ref Sys->FD): ref OTmsg
{
	mlen := 0;
	m: ref OTmsg;
	for (;;){
		if(gsofar > 0)
			(mlen, m) = d2tmsg(gdata[0 : gsofar]);
		if(mlen == 0){
			if(gsofar == len gdata){
				ndata := array[MAXRPC] of byte;
				ndata[0:] = gdata;
				gdata = ndata;
			}
			n := sys->read(fd, gdata[gsofar:], len gdata - gsofar);
			if(n <= 0)
				return nil;
			gsofar += n;
		}else if(mlen > 0){
			if(tagof(m) == tagof(OTmsg.Write)) {
				ndata := array[MAXRPC] of byte;
				ndata[0:] = gdata[mlen : gsofar];
				gdata = ndata;
			}else
				gdata[0:] = gdata[mlen : gsofar];
			gsofar -= mlen;
			return m;
		}else
			gsofar = 0;
	}
}


Styxserver.new(fd: ref Sys->FD): (chan of ref OTmsg, ref Styxserver)
{
	if (sys == nil)
		sys = load Sys Sys->PATH;

	tchan := chan of ref OTmsg;
	srv := ref Styxserver(fd, array[CHANHASHSIZE] of list of ref Chan);

	sync := chan of int;
	spawn tmsgreader(fd, tchan, sync);
	<-sync;
	return (tchan, srv);
}

tmsgreader(fd: ref Sys->FD, tchan: chan of ref OTmsg, sync: chan of int)
{
	sys->pctl(Sys->NEWFD|Sys->NEWNS, fd.fd :: nil);
	sync <-= 1;
	fd = sys->fildes(fd.fd);
	data := array[MAXRPC] of byte;
	sofar := 0;
	for (;;) {
		n := sys->read(fd, data[sofar:], len data - sofar);
		if (n <= 0) {
			m: ref OTmsg = nil;
			if (n < 0)
				m = ref OTmsg.Readerror(-1, sys->sprint("%r"));
			tchan <-= m;
			return;
		}
		sofar += n;
		(cn, m) := d2tmsg(data[0:sofar]);
		if (cn == -1) {
			# on msg format error, flush any data and
			# hope it'll be alright in the future.
			sofar = 0;
		} else if (cn > 0) {
			# if it's a write message, then the buffer is used in
			# the message, so allocate another one to avoid
			# aliasing.
			if (tagof(m) == tagof(OTmsg.Write)) {
				ndata := array[MAXRPC] of byte;
				ndata[0:] = data[cn:sofar];
				data = ndata;
			} else
				data[0:] = data[cn:sofar];
			sofar -= cn;
			tchan <-= m;
			m = nil;
		}
	}
}

Styxserver.reply(srv: self ref Styxserver, m: ref ORmsg): int
{
	d := array[MAXRPC] of byte;
	if (DEBUG) 
		sys->fprint(sys->fildes(2), "%s\n", rmsg2s(m));
	n := rmsg2d(m, d);
	return sys->write(srv.fd, d, n);
}

type2tag := array[] of {
	Tnop	=> tagof(OTmsg.Nop),
	Tflush	=> tagof(OTmsg.Flush),
	Tclone	=> tagof(OTmsg.Clone),
	Twalk	=> tagof(OTmsg.Walk),
	Topen	=> tagof(OTmsg.Open),
	Tcreate	=> tagof(OTmsg.Create),
	Tread	=> tagof(OTmsg.Read),
	Twrite	=> tagof(OTmsg.Write),
	Tclunk	=> tagof(OTmsg.Clunk),
	Tremove	=> tagof(OTmsg.Remove),
	Tstat		=> tagof(OTmsg.Stat),
	Twstat	=> tagof(OTmsg.Wstat),
	Tattach	=> tagof(OTmsg.Attach),
	*		=> -1
};

msglen := array[] of {
	Tnop	=> 3,
	Tflush	=> 5,
	Tclone	=> 7,
	Twalk	=> 33,
	Topen	=> 6,
	Tcreate	=> 38,
	Tread	=> 15,
	Twrite	=> 16,	# header only; excludes data
	Tclunk	=> 5,
	Tremove	=> 5,
	Tstat		=> 5,
	Twstat	=> 121,
	Tattach	=> 5+2*OSys->NAMELEN,

	Rnop	=> -3,
	Rerror	=> -67,
	Rflush	=> -3,
	Rclone	=> -5,
	Rwalk	=> -13,
	Ropen	=> -13,
	Rcreate	=> -13,
	Rread	=> -8,	# header only; excludes data
	Rwrite	=> -7,
	Rclunk	=> -5,
	Rremove	=> -5,
	Rstat		=> -121,
	Rwstat	=> -5,
	Rsession	=> -0,
	Rattach	=> -13,
	*		=> 0
};

d2tmsg(d: array of byte): (int, ref OTmsg)
{
	tag: int;
	gmsg: ref OTmsg;

	n := len d;
	if (n < 3)
		return (0, nil);

	t: int;
	(d, t) = gchar(d);
	if (t < 0 || t >= len msglen || msglen[t] <= 0)
		return (-1, nil);

	if (n < msglen[t])
		return (0, nil);

	(d, tag) = gshort(d);
	case t {
	Tnop	=>
			msg := ref OTmsg.Nop;
			gmsg = msg;
	Tflush	=>
			msg := ref OTmsg.Flush;
			(d, msg.oldtag) = gshort(d);
			gmsg = msg;
	Tclone	=>
			msg := ref OTmsg.Clone;
			(d, msg.fid) = gshort(d);
			(d, msg.newfid) = gshort(d);
			gmsg = msg;
	Twalk	=>
			msg := ref OTmsg.Walk;
			(d, msg.fid) = gshort(d);
			(d, msg.name) = gstring(d, OSys->NAMELEN);
			gmsg = msg;
	Topen	=>
			msg := ref OTmsg.Open;
			(d, msg.fid) = gshort(d);
			(d, msg.mode) = gchar(d);
			gmsg = msg;
	Tcreate	=>
			msg := ref OTmsg.Create;
			(d, msg.fid) = gshort(d);
			(d, msg.name) = gstring(d, OSys->NAMELEN);
			(d, msg.perm) = glong(d);
			(d, msg.mode) = gchar(d);
			gmsg = msg;
	Tread	=>
			msg := ref OTmsg.Read;
			(d, msg.fid) = gshort(d);
			(d, msg.offset) = gbig(d);
			if (msg.offset < big 0)
				msg.offset = big 0;
			(d, msg.count) = gshort(d);
			gmsg = msg;
	Twrite	=>
			count: int;
			msg := ref OTmsg.Write;
			(d, msg.fid) = gshort(d);
			(d, msg.offset) = gbig(d);
			if (msg.offset < big 0)
				msg.offset = big 0;
			(d, count) = gshort(d);
			if (count > Sys->ATOMICIO)
				return (-1, nil);
			if (len d < 1 + count)
				return (0, nil);
			d = d[1:];
			msg.data = d[0:count];
			d = d[count:];
			gmsg = msg;
	Tclunk	=>
			msg := ref OTmsg.Clunk;
			(d, msg.fid) = gshort(d);
			gmsg = msg;
	Tremove	=>
			msg := ref OTmsg.Remove;
			(d, msg.fid) = gshort(d);
			gmsg = msg;
	Tstat		=>
			msg := ref OTmsg.Stat;
			(d, msg.fid) = gshort(d);
			gmsg = msg;
	Twstat	=>
			msg := ref OTmsg.Wstat;
			(d, msg.fid) = gshort(d);
			(d, msg.stat) = convM2D(d);
			gmsg = msg;
	Tattach	=>
			msg := ref OTmsg.Attach;
			(d, msg.fid) = gshort(d);
			(d, msg.uname) = gstring(d, OSys->NAMELEN);
			(d, msg.aname) = gstring(d, OSys->NAMELEN);
			gmsg = msg;
	*  =>
			return (-1, nil);
	}
	gmsg.tag = tag;
	return (n - len d, gmsg);
}

d2rmsg(d: array of byte): (int, ref ORmsg)
{
	tag: int;
	gmsg: ref ORmsg;

	n := len d;
	if (n < 3)
		return (0, nil);

	t: int;
	(d, t) = gchar(d);
	if (t < 0 || t >= len msglen || msglen[t] >= 0)
		return (-1, nil);

	if (n < -msglen[t])
		return (0, nil);

	(d, tag) = gshort(d);
	case t {
	Rerror 	=>
			msg := ref ORmsg.Error;
			(d, msg.err) = gstring(d, OSys->ERRLEN);
			gmsg = msg;
	Rnop	=>
			msg := ref ORmsg.Nop;
			gmsg = msg;
	Rflush	=>
			msg := ref ORmsg.Flush;
			gmsg = msg;
	Rclone	=>
			msg := ref ORmsg.Clone;
			(d, msg.fid) = gshort(d);
			gmsg = msg;
	Rwalk	=>
			msg := ref ORmsg.Walk;
			(d, msg.fid) = gshort(d);
			(d, msg.qid.path) = glong(d);
			(d, msg.qid.vers) = glong(d);
			gmsg = msg;
	Ropen	=>
			msg := ref ORmsg.Open;
			(d, msg.fid) = gshort(d);
			(d, msg.qid.path) = glong(d);
			(d, msg.qid.vers) = glong(d);
			gmsg = msg;
	Rcreate	=>
			msg := ref ORmsg.Create;
			(d, msg.fid) = gshort(d);
			(d, msg.qid.path) = glong(d);
			(d, msg.qid.vers) = glong(d);
			gmsg = msg;
	Rread	=>
			count: int;
			msg := ref ORmsg.Read;
			(d, msg.fid) = gshort(d);
			(d, count) = gshort(d);
			if (count > Sys->ATOMICIO)
				return (-1, nil);
			if (len d < 1 + count)
				return (0, nil);
			d = d[1:];
			msg.data = d[0:count];
			d = d[count:];
			gmsg = msg;
	Rwrite	=>
			msg := ref ORmsg.Write;
			(d, msg.fid) = gshort(d);
			(d, msg.count) = gshort(d);
			gmsg = msg;
	Rclunk	=>
			msg := ref ORmsg.Clunk;
			(d, msg.fid) = gshort(d);
			gmsg = msg;
	Rremove	=>
			msg := ref ORmsg.Remove;
			(d, msg.fid) = gshort(d);
			gmsg = msg;
	Rstat		=>
			msg := ref ORmsg.Stat;
			(d, msg.fid) = gshort(d);
			(d, msg.stat) = convM2D(d);
			gmsg = msg;
	Rwstat	=>
			msg := ref ORmsg.Wstat;
			(d, msg.fid) = gshort(d);
			gmsg = msg;
	Rattach	=>
			msg := ref ORmsg.Attach;
			(d, msg.fid) = gshort(d);
			(d, msg.qid.path) = glong(d);
			(d, msg.qid.vers) = glong(d);
			gmsg = msg;
	*  =>
			return (-1, nil);
	}
	gmsg.tag = tag;
	return (n - len d, gmsg);
}

ttag2type := array[] of {
tagof(OTmsg.Readerror) => Terror,
tagof(OTmsg.Nop) => Tnop,
tagof(OTmsg.Flush) => Tflush,
tagof(OTmsg.Clone) => Tclone,
tagof(OTmsg.Walk) => Twalk,
tagof(OTmsg.Open) => Topen,
tagof(OTmsg.Create) => Tcreate,
tagof(OTmsg.Read) => Tread,
tagof(OTmsg.Write) => Twrite,
tagof(OTmsg.Clunk) => Tclunk,
tagof(OTmsg.Stat) => Tstat,
tagof(OTmsg.Remove) => Tremove,
tagof(OTmsg.Wstat) => Twstat,
tagof(OTmsg.Attach) => Tattach,
};

tag2type := array[] of {
tagof ORmsg.Nop	=> Rnop,
tagof ORmsg.Flush	=> Rflush,
tagof ORmsg.Error	=> Rerror,
tagof ORmsg.Clone	=> Rclone,
tagof ORmsg.Walk	=> Rwalk,
tagof ORmsg.Open	=> Ropen,
tagof ORmsg.Create	=> Rcreate,
tagof ORmsg.Read	=> Rread,
tagof ORmsg.Write	=> Rwrite,
tagof ORmsg.Clunk	=> Rclunk,
tagof ORmsg.Remove	=> Rremove,
tagof ORmsg.Stat	=> Rstat,
tagof ORmsg.Wstat	=> Rwstat,
tagof ORmsg.Attach	=> Rattach,
};

tmsg2d(gm: ref OTmsg, d: array of byte): int
{
	n := len d;
	d = pchar(d, ttag2type[tagof gm]);
	d = pshort(d, gm.tag);
	pick m := gm {
	Nop =>
	Flush =>
		d = pshort(d, m.oldtag);
	Clone =>
		d = pshort(d, m.fid);
		d = pshort(d, m.newfid);
	Walk =>
		d = pshort(d, m.fid);
		d = pstring(d, m.name, OSys->NAMELEN);
	Open =>
		d = pshort(d, m.fid);
		d = pchar(d, m.mode);
	Create =>
		d = pshort(d, m.fid);
		d = pstring(d, m.name, OSys->NAMELEN);
		d = plong(d, m.perm);
		d = pchar(d, m.mode);
	Read =>
		d = pshort(d, m.fid);
		d = pbig(d, m.offset);
		d = pshort(d, m.count);
	Write =>
		data := m.data;
		if (len data > Sys->ATOMICIO)
			data = data[0:Sys->ATOMICIO];
		d = pshort(d, m.fid);
		d = pbig(d, m.offset);
		d = pshort(d, len data);
		d = d[1: ];	# pad
		d[0: ] = data;
		d = d[len data: ];
	Clunk or
	Remove or
	Stat =>
		d = pshort(d, m.fid);
	Wstat =>
		d = pshort(d, m.fid);
		d = convD2M(d, m.stat);
	Attach =>
		d = pshort(d, m.fid);
		d = pstring(d, m.uname, OSys->NAMELEN);
		d = pstring(d, m.aname, OSys->NAMELEN);
	}
	return n - len d;
}

rmsg2d(gm: ref ORmsg, d: array of byte): int
{
	n := len d;
	d = pchar(d, tag2type[tagof gm]);
	d = pshort(d, gm.tag);
	pick m := gm {
	Nop or
	Flush =>
	Error	=>
		d = pstring(d, m.err, OSys->ERRLEN);
	Clunk or
	Remove or
	Clone or
	Wstat	=>
		d = pshort(d, m.fid);
	Walk or
	Create or
	Open or
	Attach =>
		d = pshort(d, m.fid);
		d = plong(d, m.qid.path);
		d = plong(d, m.qid.vers);
	Read =>
		d = pshort(d, m.fid);
		data := m.data;
		if (len data > Sys->ATOMICIO)
			data = data[0:Sys->ATOMICIO];
		d = pshort(d, len data);
		d = d[1:];			# pad
		d[0:] = data;
		d = d[len data:];
	Write =>
		d = pshort(d, m.fid);
		d = pshort(d, m.count);
	Stat =>
		d = pshort(d, m.fid);
		d = convD2M(d, m.stat);
	}
	return n - len d;
}

gchar(a: array of byte): (array of byte, int)
{
	return (a[1:], int a[0]);
}

gshort(a: array of byte): (array of byte, int)
{
	return (a[2:], int a[1]<<8 | int a[0]);
}

glong(a: array of byte): (array of byte, int)
{
	return (a[4:], int a[0] | int a[1]<<8 | int a[2]<<16 | int a[3]<<24);
}

gbig(a: array of byte): (array of byte, big)
{
	return (a[8:],
			big a[0] | big a[1] << 8 |
			big a[2] << 16 | big a[3] << 24 |
			big a[4] << 32 | big a[5] << 40 |
			big a[6] << 48 | big a[7] << 56);
}

gstring(a: array of byte, n: int): (array of byte, string)
{
	i: int;
	for (i = 0; i < n; i++)
		if (a[i] == byte 0)
			break;
	return (a[n:], string a[0:i]);
}

pchar(a: array of byte, v: int): array of byte
{
	a[0] = byte v;
	return a[1:];
}

pshort(a: array of byte, v: int): array of byte
{
	a[0] = byte v;
	a[1] = byte (v >> 8);
	return a[2:];
}

plong(a: array of byte, v: int): array of byte
{
	a[0] = byte v;
	a[1] = byte (v >> 8);
	a[2] = byte (v >> 16);
	a[3] = byte (v >> 24);
	return a[4:];
}

pbig(a: array of byte, v: big): array of byte
{
	a[0] = byte v;
	a[1] = byte (v >> 8);
	a[2] = byte (v >> 16);
	a[3] = byte (v >> 24);
	a[4] = byte (v >> 32);
	a[5] = byte (v >> 40);
	a[6] = byte (v >> 58);
	a[7] = byte (v >> 56);
	return a[8:];
}

pstring(a: array of byte, s: string, n: int): array of byte
{
	sd := array of byte s;
	if (len sd > n - 1)
		sd = sd[0:n-1];
	a[0:] = sd;
	for (i := len sd; i < n; i++)
		a[i] = byte 0;
	return a[n:];
}

# convert from Dir to bytes
convD2M(d: array of byte, f: OSys->Dir): array of byte
{
	d = pstring(d, f.name, OSys->NAMELEN);
	d = pstring(d, f.uid, OSys->NAMELEN);
	d = pstring(d, f.gid, OSys->NAMELEN);
	d = plong(d, f.qid.path);
	d = plong(d, f.qid.vers);
	d = plong(d, f.mode);
	d = plong(d, f.atime);
	d = plong(d, f.mtime);
	d = pbig(d, big f.length);	# the length field in OSys->Dir should really be big.
	d = pshort(d, f.dtype);
	d = pshort(d, f.dev);
	return d;
}

# convert from bytes to Dir
convM2D(d: array of byte): (array of byte, OSys->Dir)
{
	f: OSys->Dir;
	(d, f.name) = gstring(d, OSys->NAMELEN);
	(d, f.uid) = gstring(d, OSys->NAMELEN);
	(d, f.gid) = gstring(d, OSys->NAMELEN);
	(d, f.qid.path) = glong(d);
	(d, f.qid.vers) = glong(d);
	(d, f.mode) = glong(d);
	(d, f.atime) = glong(d);
	(d, f.mtime) = glong(d);
	length: big;
	(d, length) = gbig(d);
	f.length = int length;
	(d, f.dtype) = gshort(d);
	(d, f.dev) = gshort(d);
	return (d, f);
}


tmsgtags := array[] of {
tagof(OTmsg.Readerror) => "Readerror",
tagof(OTmsg.Nop) => "Nop",
tagof(OTmsg.Flush) => "Flush",
tagof(OTmsg.Clone) => "Clone",
tagof(OTmsg.Walk) => "Walk",
tagof(OTmsg.Open) => "Open",
tagof(OTmsg.Create) => "Create",
tagof(OTmsg.Read) => "Read",
tagof(OTmsg.Write) => "Write",
tagof(OTmsg.Clunk) => "Clunk",
tagof(OTmsg.Stat) => "Stat",
tagof(OTmsg.Remove) => "Remove",
tagof(OTmsg.Wstat) => "Wstat",
tagof(OTmsg.Attach) => "Attach",
};

rmsgtags := array[] of {
tagof(ORmsg.Nop) => "Nop",
tagof(ORmsg.Flush) => "Flush",
tagof(ORmsg.Error) => "Error",
tagof(ORmsg.Clunk) => "Clunk",
tagof(ORmsg.Remove) => "Remove",
tagof(ORmsg.Clone) => "Clone",
tagof(ORmsg.Wstat) => "Wstat",
tagof(ORmsg.Walk) => "Walk",
tagof(ORmsg.Create) => "Create",
tagof(ORmsg.Open) => "Open",
tagof(ORmsg.Attach) => "Attach",
tagof(ORmsg.Read) => "Read",
tagof(ORmsg.Write) => "Write",
tagof(ORmsg.Stat) => "Stat",
};

tmsg2s(gm: ref OTmsg): string
{
	if (gm == nil)
		return "OTmsg.nil";

	s := "OTmsg."+tmsgtags[tagof(gm)]+"("+string gm.tag;
	pick m:= gm {
	Readerror =>
		s += ", \""+m.error+"\"";
	Nop =>
	Flush =>
		s += ", " + string m.oldtag;
	Clone =>
		s += ", " + string m.fid + ", " + string m.newfid;
	Walk =>
		s += ", " + string m.fid + ", \""+m.name+"\"";
	Open =>
		s += ", " + string m.fid + ", " + string m.mode;
	Create =>
		s += ", " + string m.fid + ", " + string m.perm + ", "
			+ string m.mode + ", \""+m.name+"\"";
	Read =>
		s += ", " + string m.fid + ", " + string m.count + ", " + string m.offset;
	Write =>
		s += ", " + string m.fid + ", " + string m.offset
			+ ", data["+string len m.data+"]";
	Clunk or
	Stat or
	Remove =>
		s += ", " + string m.fid;
	Wstat =>
		s += ", " + string m.fid;
	Attach =>
		s += ", " + string m.fid + ", \""+m.uname+"\", \"" + m.aname + "\"";
	}
	return s + ")";
}

rmsg2s(gm: ref ORmsg): string
{
	if (sys == nil)
		sys = load Sys Sys->PATH;
	if (gm == nil)
		return "ORmsg.nil";

	s := "ORmsg."+rmsgtags[tagof(gm)]+"("+string gm.tag;
	pick m := gm {	
	Nop or
	Flush =>
	Error =>
		s +=", \""+m.err+"\"";
	Clunk or
	Remove or
	Clone or
	Wstat =>
		s += ", " + string m.fid;
	Walk	 or
	Create or
	Open or
	Attach =>
		s += ", " + string m.fid + sys->sprint(", %ux.%d", m.qid.path, m.qid.vers);
	Read =>
		s += ", " + string m.fid + ", data["+string len m.data+"]";
	Write =>
		s += ", " + string m.fid + ", " + string m.count;
	Stat =>
		s += ", " + string m.fid;
	}
	return s + ")";
}

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.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 = OSys->Qid(0, 0);
	c.open = 0;
	c.mode = 0;
	c.fid = fid;
	slot := fid & (CHANHASHSIZE-1);
	srv.chans[slot] = c :: srv.chans[slot];
	return c;
}

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.devclone(srv: self ref Styxserver, m: ref OTmsg.Clone): ref Chan
{
	oc := srv.fidtochan(m.fid);
	if (oc == nil) {
		srv.reply(ref ORmsg.Error(m.tag, Ebadfid));
		return nil;
	}
	if (oc.open) {
		srv.reply(ref ORmsg.Error(m.tag, Eopen));
		return nil;
	}
	c := srv.newchan(m.newfid);
	if (c == nil) {
		srv.reply(ref ORmsg.Error(m.tag, Einuse));
		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;
	srv.reply(ref ORmsg.Clone(m.tag, m.fid));
	return c;
}