implement Op;
#
# A little bit dirty. But it was easier to follow (and borrow) the code
# in styx.b than it was writing this from scratch.
#
# BUG: must change the protocol. Both Tput and Tget must accept either paths or fds
# a Tput/Tget with an invalid fd must use the path and reopen the file, reporting a new fd
# back to the client. Old fds must be closed.
include "sys.m";
sys: Sys;
fprint, fildes, print, nulldir, sprint , Qid: import sys;
include "draw.m";
include "io.m";
io: Io;
readn: import io;
include "op.m";
STR: con BIT16SZ; # string length
TAG: con BIT16SZ;
QID: con BIT8SZ+BIT32SZ+BIT64SZ;
LEN: con BIT16SZ; # stat and qid array lengths
COUNT: con BIT32SZ;
OFFSET: con BIT64SZ;
H: con BIT32SZ+BIT8SZ+BIT16SZ; # minimum header length: size[4] type tag[2]
init()
{
sys = load Sys Sys->PATH;
io = load Io Io->PATH;
if (io == nil)
fprint(fildes(2), "op: can't load %s: %r\n", Io->PATH);
}
utflen(s: string): int
{
# the domain is 16-bit unicode only, which is all that Inferno now implements
n := l := len s;
for(i:=0; i<l; i++)
if((c := s[i]) > 16r7F){
n++;
if(c > 16r7FF)
n++;
}
return n;
}
packdirsize(d: Sys->Dir): int
{
return STATFIXLEN+utflen(d.name)+utflen(d.uid)+utflen(d.gid)+utflen(d.muid);
}
packdir(f: Sys->Dir): array of byte
{
ds := packdirsize(f);
a := array[ds] of byte;
# size[2]
a[0] = byte (ds-LEN);
a[1] = byte ((ds-LEN)>>8);
# type[2]
a[2] = byte f.dtype;
a[3] = byte (f.dtype>>8);
# dev[4]
a[4] = byte f.dev;
a[5] = byte (f.dev>>8);
a[6] = byte (f.dev>>16);
a[7] = byte (f.dev>>24);
# qid.type[1]
# qid.vers[4]
# qid.path[8]
pqid(a, 8, f.qid);
# mode[4]
a[21] = byte f.mode;
a[22] = byte (f.mode>>8);
a[23] = byte (f.mode>>16);
a[24] = byte (f.mode>>24);
# atime[4]
a[25] = byte f.atime;
a[26] = byte (f.atime>>8);
a[27] = byte (f.atime>>16);
a[28] = byte (f.atime>>24);
# mtime[4]
a[29] = byte f.mtime;
a[30] = byte (f.mtime>>8);
a[31] = byte (f.mtime>>16);
a[32] = byte (f.mtime>>24);
# length[8]
p64(a, 33, big f.length);
# name[s]
i := pstring(a, 33+BIT64SZ, f.name);
i = pstring(a, i, f.uid);
i = pstring(a, i, f.gid);
i = pstring(a, i, f.muid);
if(i != len a)
raise "assertion: Styx->packdir: bad count"; # can't happen unless packedsize is wrong
return a;
}
pqid(a: array of byte, o: int, q: Sys->Qid): int
{
a[o] = byte q.qtype;
v := q.vers;
a[o+1] = byte v;
a[o+2] = byte (v>>8);
a[o+3] = byte (v>>16);
a[o+4] = byte (v>>24);
v = int q.path;
a[o+5] = byte v;
a[o+6] = byte (v>>8);
a[o+7] = byte (v>>16);
a[o+8] = byte (v>>24);
v = int (q.path >> 32);
a[o+9] = byte v;
a[o+10] = byte (v>>8);
a[o+11] = byte (v>>16);
a[o+12] = byte (v>>24);
return o+QID;
}
pstring(a: array of byte, o: int, s: string): int
{
sa := array of byte s; # could do conversion ourselves
n := len sa;
a[o] = byte n;
a[o+1] = byte (n>>8);
a[o+2:] = sa;
return o+LEN+n;
}
p16(a: array of byte, o: int, v: int): int
{
a[o] = byte v;
a[o+1] = byte (v>>8);
return o+BIT16SZ;
}
p32(a: array of byte, o: int, v: int): int
{
a[o] = byte v;
a[o+1] = byte (v>>8);
a[o+2] = byte (v>>16);
a[o+3] = byte (v>>24);
return o+BIT32SZ;
}
p64(a: array of byte, o: int, b: big): int
{
i := int b;
a[o] = byte i;
a[o+1] = byte (i>>8);
a[o+2] = byte (i>>16);
a[o+3] = byte (i>>24);
i = int (b>>32);
a[o+4] = byte i;
a[o+5] = byte (i>>8);
a[o+6] = byte (i>>16);
a[o+7] = byte (i>>24);
return o+BIT64SZ;
}
unpackdir(a: array of byte): (int, Sys->Dir)
{
dir: Sys->Dir;
if(len a < STATFIXLEN)
return (0, dir);
# size[2]
sz := ((int a[1] << 8) | int a[0])+LEN; # bytes this packed dir should occupy
if(len a < sz)
return (0, dir);
# type[2]
dir.dtype = (int a[3]<<8) | int a[2];
# dev[4]
dir.dev = (((((int a[7] << 8) | int a[6]) << 8) | int a[5]) << 8) | int a[4];
# qid.type[1]
# qid.vers[4]
# qid.path[8]
dir.qid = gqid(a, 8);
# mode[4]
dir.mode = (((((int a[24] << 8) | int a[23]) << 8) | int a[22]) << 8) | int a[21];
# atime[4]
dir.atime = (((((int a[28] << 8) | int a[27]) << 8) | int a[26]) << 8) | int a[25];
# mtime[4]
dir.mtime = (((((int a[32] << 8) | int a[31]) << 8) | int a[30]) << 8) | int a[29];
# length[8]
v0 := (((((int a[36] << 8) | int a[35]) << 8) | int a[34]) << 8) | int a[33];
v1 := (((((int a[40] << 8) | int a[39]) << 8) | int a[38]) << 8) | int a[37];
dir.length = (big v1 << 32) | (big v0 & 16rFFFFFFFF);
# name[s], uid[s], gid[s], muid[s]
i: int;
(dir.name, i) = gstring(a, 41);
(dir.uid, i) = gstring(a, i);
(dir.gid, i) = gstring(a, i);
(dir.muid, i) = gstring(a, i);
if(i != sz)
return (0, dir);
return (i, dir);
}
gqid(f: array of byte, i: int): Sys->Qid
{
qtype := int f[i];
vers := (((((int f[i+4] << 8) | int f[i+3]) << 8) | int f[i+2]) << 8) | int f[i+1];
i += BIT8SZ+BIT32SZ;
path0 := (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i];
i += BIT32SZ;
path1 := (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i];
path := (big path1 << 32) | (big path0 & 16rFFFFFFFF);
return (path, vers, qtype);
}
g32(f: array of byte, i: int): int
{
r := (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i];
if (r == int 16rFFFFFFFF)
r = ~0;
return r;
}
g16(f: array of byte, i: int): int
{
r := (( int f[i+1]) << 8) | int f[i];
if (r == int 16rFFFF)
r = ~0;
return r;
}
g64(f: array of byte, i: int): big
{
b0 := (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i];
b1 := (((((int f[i+7] << 8) | int f[i+6]) << 8) | int f[i+5]) << 8) | int f[i+4];
return (big b1 << 32) | (big b0 & 16rFFFFFFFF);
}
gstring(a: array of byte, o: int): (string, int)
{
if(o < 0 || o+STR > len a)
return (nil, -1);
l := (int a[o+1] << 8) | int a[o];
o += STR;
e := o+l;
if(e > len a)
return (nil, -1);
return (string a[o:e], e);
}
ttag2type := array[] of {
tagof Tmsg.Readerror => 0,
tagof Tmsg.Attach => Tattach,
tagof Tmsg.Flush => Tflush,
tagof Tmsg.Put => Tput,
tagof Tmsg.Get => Tget,
tagof Tmsg.Remove => Tremove};
Tmsg.mtype(t: self ref Tmsg): int
{
return ttag2type[tagof t];
}
Tmsg.packedsize(t: self ref Tmsg): int
{
mtype := ttag2type[tagof t];
if(mtype <= 0)
return 0;
ml := H;
pick m := t {
Attach =>
ml += STR + utflen(m.uname);
ml += STR + utflen(m.path);
Flush =>
ml += TAG;
Put =>
ml += STR + utflen(m.path);
ml += BIT16SZ;
ml += BIT16SZ;
if (m.mode & OSTAT)
ml += packdirsize(m.stat);
ml += OFFSET;
ml += COUNT;
ml += len m.data;
Get =>
ml += STR + utflen(m.path);
ml += BIT16SZ;
ml += BIT16SZ;
ml += BIT16SZ;
ml += OFFSET;
ml += COUNT;
Remove =>
ml += STR + utflen(m.path);
}
return ml;
}
Tmsg.pack(t: self ref Tmsg): array of byte
{
if(t == nil)
return nil;
ds := t.packedsize();
if(ds <= 0)
return nil;
d := array[ds] of byte;
d[0] = byte ds;
d[1] = byte (ds>>8);
d[2] = byte (ds>>16);
d[3] = byte (ds>>24);
d[4] = byte ttag2type[tagof t];
d[5] = byte t.tag;
d[6] = byte (t.tag >> 8);
pick m := t {
Attach =>
o := pstring(d, H, m.uname);
pstring(d, o, m.path);
Flush =>
v := m.oldtag;
d[H] = byte v;
d[H+1] = byte (v>>8);
Put =>
o := pstring(d, H, m.path);
p16(d, o, m.fd); o += BIT16SZ;
p16(d, o, m.mode); o += BIT16SZ;
if (m.mode&OSTAT){
stat := packdir(m.stat);
n := len stat;
d[o:] = stat;
o += n;
}
p64(d, o, m.offset); o += OFFSET;
p32(d, o, len m.data); o += COUNT;
d[o:] = m.data;
Get =>
o := pstring(d, H, m.path);
p16(d, o, m.fd); o += BIT16SZ;
p16(d, o, m.mode); o += BIT16SZ;
p16(d, o, m.nmsgs); o += BIT16SZ;
p64(d, o, m.offset); o += OFFSET;
p32(d, o, m.count); o += COUNT;
Remove =>
pstring(d, H, m.path);
* =>
fprint(fildes(2), "op: pack: bad tag: %d", tagof t);
}
return d;
}
Tmsg.unpack(f: array of byte): (int, ref Tmsg)
{
if(len f < H)
return (0, nil);
size := (int f[1] << 8) | int f[0];
size |= ((int f[3] << 8) | int f[2]) << 16;
if(len f != size){
if(len f < size)
return (0, nil); # need more data
f = f[0:size]; # trim to exact length
}
mtype := int f[4];
if(mtype >= Tmax || (mtype&1) == 0 || mtype <= 0){
fprint(fildes(2), "upack: bad mtype %d\n", mtype);
return (-1, nil);
}
tag := (int f[6] << 8) | int f[5];
case mtype {
* =>
fprint(fildes(2), "op: unpack: bad type %d\n", mtype);
Tattach =>
(uname, o1) := gstring(f, H);
(path, o2) := gstring(f, o1);
return (o2, ref Tmsg.Attach(tag, uname, path));
Tflush =>
oldtag := (int f[H+1] << 8) | int f[H];
return (H+TAG, ref Tmsg.Flush(tag, oldtag));
Tput =>
stat : Sys->Dir;
(path, o) := gstring(f, H);
fd := g16(f, o); o+= BIT16SZ;
mode := g16(f, o); o+= BIT16SZ;
if (mode&OSTAT){
o1 : int;
(o1, stat) = unpackdir(f[o:]); o += o1;
}
offset := g64(f, o); o+= OFFSET;
count := g32(f, o); o+= COUNT;
data := f[o:o+count]; o+= count;
return (o, ref Tmsg.Put(tag, path, fd, mode, stat, offset, data));
Tget =>
(path, o) := gstring(f, H);
fd := g16(f, o); o += BIT16SZ;
mode := g16(f, o); o+= BIT16SZ;
nmsgs := g16(f, o); o+= BIT16SZ;
offset := g64(f, o); o+= OFFSET;
count := g32(f, o); o+= COUNT;
return (o, ref Tmsg.Get(tag, path, fd, mode, nmsgs, offset, count));
Tremove =>
(path, o1) := gstring(f, H);
return (o1, ref Tmsg.Remove(tag, path));
}
return (-1, nil); # illegal
}
tmsgname := array[] of {
tagof Tmsg.Readerror => "READERROR",
tagof Tmsg.Attach => "attach",
tagof Tmsg.Flush => "flush",
tagof Tmsg.Put => "put",
tagof Tmsg.Get => "get",
tagof Tmsg.Remove => "remove",};
Tmsg.text(t: self ref Tmsg): string
{
if(t == nil)
return "nil";
s := sys->sprint("T%s %ud", tmsgname[tagof t], t.tag);
pick m:= t {
* =>
return s + " ILLEGAL";
Readerror =>
return s + sys->sprint(" \"%s\"", m.error);
Attach =>
return s + sys->sprint(" \"%s\" \"%s\"", m.uname, m.path);
Flush =>
return s + sys->sprint(" %ud", m.oldtag);
Put =>
s += sys->sprint("\"%s\" fd=%d %s", m.path, m.fd, mode2text(m.mode));
if (m.mode&OSTAT)
s += sys->sprint(" %s", dir2text(m.stat));
n := len m.data;
s += sys->sprint(" o=%bd c=%ud", m.offset, n);
if (n > 0){
x := "";
if (n > 10) {
x= "..."; n = 10;
}
s += sys->sprint(" \"%s%s\"", string m.data[0:n], x);
}
return s ;
Get =>
s += sys->sprint(" \"%s\" fd=%d %s", m.path, m.fd, mode2text(m.mode));
s += sys->sprint(" n=%d o=%bd c=%ud", m.nmsgs, m.offset, m.count);
return s ;
Remove =>
return s + sys->sprint(" \"%s\"", m.path);
}
}
Tmsg.read(fd: ref Sys->FD, msglim: int): ref Tmsg
{
(msg, err) := readmsg(fd, msglim);
if(err != nil)
return ref Tmsg.Readerror(0, err);
if(msg == nil)
return nil;
(nil, m) := Tmsg.unpack(msg);
if(m == nil)
return ref Tmsg.Readerror(0, "bad Op T-message");
return m;
}
rtag2type := array[] of {
tagof Rmsg.Readerror=> 0,
tagof Rmsg.Error => Rerror,
tagof Rmsg.Attach => Rattach,
tagof Rmsg.Flush => Rflush,
tagof Rmsg.Put => Rput,
tagof Rmsg.Get => Rget,
tagof Rmsg.Remove => Rremove,};
Rmsg.mtype(r: self ref Rmsg): int
{
return rtag2type[tagof r];
}
Rmsg.packedsize(r: self ref Rmsg): int
{
mtype := rtag2type[tagof r];
if(mtype <= 0)
return 0;
ml := H;
pick m := r {
Error =>
ml += STR + utflen(m.ename);
Attach or Flush =>
Put =>
ml += BIT16SZ;
ml += COUNT;
ml += QID;
ml += BIT32SZ;
Get =>
ml += BIT16SZ;
ml += BIT16SZ;
if (m.mode&OSTAT)
ml += packdirsize(m.stat);
ml += BIT32SZ;
ml += len m.data;
Remove =>
}
return ml;
}
Rmsg.pack(r: self ref Rmsg): array of byte
{
if(r == nil)
return nil;
ps := r.packedsize();
if(ps <= 0)
return nil;
d := array[ps] of byte;
d[0] = byte ps;
d[1] = byte (ps>>8);
d[2] = byte (ps>>16);
d[3] = byte (ps>>24);
d[4] = byte rtag2type[tagof r];
d[5] = byte r.tag;
d[6] = byte (r.tag >> 8);
o := H;
pick m := r {
Error =>
pstring(d, o, m.ename);
Attach or Flush =>
Put =>
p16(d, o, m.fd); o += BIT16SZ;
p32(d, o, m.count); o += COUNT;
pqid(d, o, m.qid); o += QID;
p32(d, o, m.mtime);
Get =>
p16(d, o, m.fd); o += BIT16SZ;
p16(d, o, m.mode); o += BIT16SZ;
if (m.mode&OSTAT){
stat := packdir(m.stat);
n := len stat;
d[o:] = stat;
o += n;
}
p32(d, o, len m.data); o += COUNT;
d[o:] = m.data;
Remove =>
* =>
fprint(fildes(2), "op: pack: bad tag: %d", tagof r);
}
return d;
}
Rmsg.unpack(f: array of byte): (int, ref Rmsg)
{
if(len f < H)
return (0, nil);
size := (int f[1] << 8) | int f[0];
size |= ((int f[3] << 8) | int f[2]) << 16;
if(len f != size){
if(len f < size)
return (0, nil); # need more data
f = f[0:size]; # trim to exact length
}
mtype := int f[4];
if(mtype >= Tmax || (mtype&1) != 0 || mtype <= 0){
fprint(fildes(2), "upack: bad mtype %d\n", mtype);
return (-1, nil);
}
tag := (int f[6] << 8) | int f[5];
case mtype {
* =>
fprint(fildes(2), "op: unpack: bad type %d\n", mtype);
Rerror =>
if (len f < H + STR)
return (H, ref Rmsg.Readerror(-1, "short Rerror msg"));
(ename, o1) := gstring(f, H);
return (o1, ref Rmsg.Error(tag, ename));
Rattach =>
return (H, ref Rmsg.Attach(tag));
Rflush =>
return (H, ref Rmsg.Flush(tag));
Rput =>
if (len f < H + BIT16SZ + COUNT + QID + BIT32SZ)
return (H, ref Rmsg.Readerror(-1, "short Rput msg"));
o := H;
fd := g16(f, o); o += BIT16SZ;
count := g32(f, o); o += COUNT;
qid := gqid(f, o); o += QID;
mtime := g32(f, o); o += BIT32SZ;
return (o, ref Rmsg.Put(tag, fd, count, qid, mtime));
Rget =>
if (len f < H + BIT16SZ + BIT16SZ)
return (H, ref Rmsg.Readerror(-1, "short Rget msg"));
o := H;
stat: Sys->Dir;
fd := g16(f, o); o += BIT16SZ;
mode := g16(f, o); o+= BIT16SZ;
if (mode&OSTAT){
o1 : int;
if (len f < o + BIT32SZ)
return (H, ref Rmsg.Readerror(-1, "short Rget msg"));
(o1, stat) = unpackdir(f[o:]); o+= o1;
}
if (len f < o + COUNT)
return (H, ref Rmsg.Readerror(-1, "short Rget msg"));
count := g32(f, o); o+= COUNT;
if (len f < o + count)
return (H, ref Rmsg.Readerror(-1, "short Rget msg"));
data := f[o:o+count]; o+= count;
return (o, ref Rmsg.Get(tag, fd, mode, stat, data));
Rremove =>
return (H, ref Rmsg.Remove(tag));
}
return (-1, nil); # illegal
}
rmsgname := array[] of {
tagof Rmsg.Readerror => "READERROR",
tagof Rmsg.Error => "error",
tagof Rmsg.Attach => "attach",
tagof Rmsg.Flush => "flush",
tagof Rmsg.Put => "put",
tagof Rmsg.Get => "get",
tagof Rmsg.Remove => "remove",
};
Rmsg.text(r: self ref Rmsg): string
{
if(r == nil)
return "nil";
s := sys->sprint("R%s %ud", rmsgname[tagof r], r.tag);
pick m:= r {
* =>
return s + " ILLEGAL";
Readerror =>
return s + sys->sprint(" \"%s\"", m.error);
Error =>
return s + sys->sprint(" \"%s\"", m.ename);
Attach or Flush =>
return s;
Put =>
s += sys->sprint(" fd=%d %d %s %d", m.fd, m.count, qid2text(m.qid), m.mtime);
return s ;
Get =>
if (m.mode&OSTAT)
s += sys->sprint(" fd=%d %s %s", m.fd, mode2text(m.mode), dir2text(m.stat));
else
s += sys->sprint(" fd=%d %s", m.fd, mode2text(m.mode));
n := len m.data;
s += sys->sprint(" %ud", n);
if (n > 0){
x := "";
if (n > 10) {
x= "..."; n = 10;
}
s += sys->sprint(" \"%s%s\"", string m.data[0:n], x);
}
return s ;
Remove =>
return s;
}
}
Rmsg.read(fd: ref Sys->FD, msglim: int): ref Rmsg
{
(msg, err) := readmsg(fd, msglim);
if(err != nil)
return ref Rmsg.Readerror(0, err);
if(msg == nil)
return nil;
(nil, m) := Rmsg.unpack(msg);
if(m == nil)
return ref Rmsg.Readerror(0, "bad Op R-message format");
return m;
}
dir2text(d: Sys->Dir): string
{
return sys->sprint("[\"%s\" \"%s\" \"%s\" %s 8r%uo %d %d %bd 16r%ux %d]",
d.name, d.uid, d.gid, qid2text(d.qid), d.mode, d.atime, d.mtime, d.length, d.dtype, d.dev);
}
qid2text(q: Sys->Qid): string
{
return sys->sprint("(16r%ubx,%d,16r%.2ux)", q.path, q.vers, q.qtype);
}
mode2text(m: int) : string
{
td := ts := tc := tm := "-";
if (m&ODATA)
td = "d";
if (m&OSTAT)
ts = "s";
if (m&OCREATE)
tc = "c";
if (m&OMORE)
tm = "m";
return td+ts+tc+tm;
}
readmsg(fd: ref Sys->FD, msglim: int): (array of byte, string)
{
if(msglim <= 0)
msglim = MAXHDR+MAXDATA;
sbuf := array[BIT32SZ] of byte;
if((n := readn(fd, sbuf, BIT32SZ)) != BIT32SZ){
if(n == 0)
return (nil, nil);
return (nil, sys->sprint("%r"));
}
ml := (int sbuf[1] << 8) | int sbuf[0];
ml |= ((int sbuf[3] << 8) | int sbuf[2]) << 16;
if(ml <= BIT32SZ)
return (nil, "invalid Op message size");
if(ml > msglim)
return (nil, "Op message longer than agreed: " + sprint("%d", ml));
buf := array[ml] of byte;
buf[0:] = sbuf;
if((n = readn(fd, buf[BIT32SZ:], ml-BIT32SZ)) != ml-BIT32SZ){
if(n == 0)
return (nil, "Op message truncated");
return (nil, sys->sprint("%r"));
}
return (buf, nil);
}
istmsg(f: array of byte): int
{
if(len f < H)
return -1;
return (int f[BIT32SZ] & 1) != 0;
}
|