diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
| commit | 37da2899f40661e3e9631e497da8dc59b971cbd0 (patch) | |
| tree | cbc6d4680e347d906f5fa7fca73214418741df72 /appl/lib/styx.b | |
| parent | 54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff) | |
20060303a
Diffstat (limited to 'appl/lib/styx.b')
| -rw-r--r-- | appl/lib/styx.b | 934 |
1 files changed, 934 insertions, 0 deletions
diff --git a/appl/lib/styx.b b/appl/lib/styx.b new file mode 100644 index 00000000..f4534c6c --- /dev/null +++ b/appl/lib/styx.b @@ -0,0 +1,934 @@ +implement Styx; + +include "sys.m"; + sys: Sys; + +include "styx.m"; + +STR: con BIT16SZ; # string length +TAG: con BIT16SZ; +FID: con BIT32SZ; +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] + +# +# the following array could be shorter if it were indexed by (type-Tversion) +# +hdrlen := array[Tmax] of +{ +Tversion => H+COUNT+STR, # size[4] Tversion tag[2] msize[4] version[s] +Rversion => H+COUNT+STR, # size[4] Rversion tag[2] msize[4] version[s] + +Tauth => H+FID+STR+STR, # size[4] Tauth tag[2] afid[4] uname[s] aname[s] +Rauth => H+QID, # size[4] Rauth tag[2] aqid[13] + +Rerror => H+STR, # size[4] Rerror tag[2] ename[s] + +Tflush => H+TAG, # size[4] Tflush tag[2] oldtag[2] +Rflush => H, # size[4] Rflush tag[2] + +Tattach => H+FID+FID+STR+STR, # size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s] +Rattach => H+QID, # size[4] Rattach tag[2] qid[13] + +Twalk => H+FID+FID+LEN, # size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s]) +Rwalk => H+LEN, # size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13]) + +Topen => H+FID+BIT8SZ, # size[4] Topen tag[2] fid[4] mode[1] +Ropen => H+QID+COUNT, # size[4] Ropen tag[2] qid[13] iounit[4] + +Tcreate => H+FID+STR+BIT32SZ+BIT8SZ, # size[4] Tcreate tag[2] fid[4] name[s] perm[4] mode[1] +Rcreate => H+QID+COUNT, # size[4] Rcreate tag[2] qid[13] iounit[4] + +Tread => H+FID+OFFSET+COUNT, # size[4] Tread tag[2] fid[4] offset[8] count[4] +Rread => H+COUNT, # size[4] Rread tag[2] count[4] data[count] + +Twrite => H+FID+OFFSET+COUNT, # size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count] +Rwrite => H+COUNT, # size[4] Rwrite tag[2] count[4] + +Tclunk => H+FID, # size[4] Tclunk tag[2] fid[4] +Rclunk => H, # size[4] Rclunk tag[2] + +Tremove => H+FID, # size[4] Tremove tag[2] fid[4] +Rremove => H, # size[4] Rremove tag[2] + +Tstat => H+FID, # size[4] Tstat tag[2] fid[4] +Rstat => H+LEN, # size[4] Rstat tag[2] stat[n] + +Twstat => H+FID+LEN, # size[4] Twstat tag[2] fid[4] stat[n] +Rwstat => H, # size[4] Rwstat tag[2] +}; + +init() +{ + sys = load Sys Sys->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; +} + +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 +{ + return (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i]; +} + +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.Version => Tversion, +tagof Tmsg.Auth => Tauth, +tagof Tmsg.Attach => Tattach, +tagof Tmsg.Flush => Tflush, +tagof Tmsg.Walk => Twalk, +tagof Tmsg.Open => Topen, +tagof Tmsg.Create => Tcreate, +tagof Tmsg.Read => Tread, +tagof Tmsg.Write => Twrite, +tagof Tmsg.Clunk => Tclunk, +tagof Tmsg.Stat => Tstat, +tagof Tmsg.Remove => Tremove, +tagof Tmsg.Wstat => Twstat, +}; + +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 := hdrlen[mtype]; + pick m := t { + Version => + ml += utflen(m.version); + Auth => + ml += utflen(m.uname)+utflen(m.aname); + Attach => + ml += utflen(m.uname)+utflen(m.aname); + Walk => + for(i:=0; i<len m.names; i++) + ml += STR+utflen(m.names[i]); + Create => + ml += utflen(m.name); + Write => + ml += len m.data; + Wstat => + ml += packdirsize(m.stat); + } + 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 { + Version => + p32(d, H, m.msize); + pstring(d, H+COUNT, m.version); + Auth => + p32(d, H, m.afid); + o := pstring(d, H+FID, m.uname); + pstring(d, o, m.aname); + Flush => + v := m.oldtag; + d[H] = byte v; + d[H+1] = byte (v>>8); + Attach => + p32(d, H, m.fid); + p32(d, H+FID, m.afid); + o := pstring(d, H+2*FID, m.uname); + pstring(d, o, m.aname); + Walk => + d[H] = byte m.fid; + d[H+1] = byte (m.fid>>8); + d[H+2] = byte (m.fid>>16); + d[H+3] = byte (m.fid>>24); + d[H+FID] = byte m.newfid; + d[H+FID+1] = byte (m.newfid>>8); + d[H+FID+2] = byte (m.newfid>>16); + d[H+FID+3] = byte (m.newfid>>24); + n := len m.names; + d[H+2*FID] = byte n; + d[H+2*FID+1] = byte (n>>8); + o := H+2*FID+LEN; + for(i := 0; i < n; i++) + o = pstring(d, o, m.names[i]); + Open => + p32(d, H, m.fid); + d[H+FID] = byte m.mode; + Create => + p32(d, H, m.fid); + o := pstring(d, H+FID, m.name); + p32(d, o, m.perm); + d[o+BIT32SZ] = byte m.mode; + Read => + p32(d, H, m.fid); + p64(d, H+FID, m.offset); + p32(d, H+FID+OFFSET, m.count); + Write => + p32(d, H, m.fid); + p64(d, H+FID, m.offset); + n := len m.data; + p32(d, H+FID+OFFSET, n); + d[H+FID+OFFSET+COUNT:] = m.data; + Clunk or Remove or Stat => + p32(d, H, m.fid); + Wstat => + p32(d, H, m.fid); + stat := packdir(m.stat); + n := len stat; + d[H+FID] = byte n; + d[H+FID+1] = byte (n>>8); + d[H+FID+LEN:] = stat; + * => + raise sys->sprint("assertion: Styx->Tmsg.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 >= len hdrlen || (mtype&1) != 0 || size < hdrlen[mtype]) + return (-1, nil); + + tag := (int f[6] << 8) | int f[5]; + fid := 0; + if(hdrlen[mtype] >= H+FID) + fid = g32(f, H); # fid is always in same place: extract it once for all if there + + # return out of each case body for a legal message; + # break out of the case for an illegal one + +Decode: + case mtype { + * => + sys->print("styx: Tmsg.unpack: bad type %d\n", mtype); + Tversion => + msize := fid; + (version, o) := gstring(f, H+COUNT); + if(o <= 0) + break; + return (o, ref Tmsg.Version(tag, msize, version)); + Tauth => + (uname, o1) := gstring(f, H+FID); + (aname, o2) := gstring(f, o1); + if(o2 <= 0) + break; + return (o2, ref Tmsg.Auth(tag, fid, uname, aname)); + Tflush => + oldtag := (int f[H+1] << 8) | int f[H]; + return (H+TAG, ref Tmsg.Flush(tag, oldtag)); + Tattach => + afid := g32(f, H+FID); + (uname, o1) := gstring(f, H+2*FID); + (aname, o2) := gstring(f, o1); + if(o2 <= 0) + break; + return (o2, ref Tmsg.Attach(tag, fid, afid, uname, aname)); + Twalk => + newfid := g32(f, H+FID); + n := (int f[H+2*FID+1] << 8) | int f[H+2*FID]; + if(n > MAXWELEM) + break; + o := H+2*FID+LEN; + names: array of string = nil; + if(n > 0){ + names = array[n] of string; + for(i:=0; i<n; i++){ + (names[i], o) = gstring(f, o); + if(o <= 0) + break Decode; + } + } + return (o, ref Tmsg.Walk(tag, fid, newfid, names)); + Topen => + return (H+FID+BIT8SZ, ref Tmsg.Open(tag, fid, int f[H+FID])); + Tcreate => + (name, o) := gstring(f, H+FID); + if(o <= 0 || o+BIT32SZ+BIT8SZ > len f) + break; + perm := g32(f, o); + o += BIT32SZ; + mode := int f[o++]; + return (o, ref Tmsg.Create(tag, fid, name, perm, mode)); + Tread => + offset := g64(f, H+FID); + count := g32(f, H+FID+OFFSET); + return (H+FID+OFFSET+COUNT, ref Tmsg.Read(tag, fid, offset, count)); + Twrite => + offset := g64(f, H+FID); + count := g32(f, H+FID+OFFSET); + O: con H+FID+OFFSET+COUNT; + if(count > len f-O) + break; + data := f[O:O+count]; + return (O+count, ref Tmsg.Write(tag, fid, offset, data)); + Tclunk => + return (H+FID, ref Tmsg.Clunk(tag, fid)); + Tremove => + return (H+FID, ref Tmsg.Remove(tag, fid)); + Tstat => + return (H+FID, ref Tmsg.Stat(tag, fid)); + Twstat => + n := int (f[H+FID+1]<<8) | int f[H+FID]; + if(len f < H+FID+LEN+n) + break; + (ds, stat) := unpackdir(f[H+FID+LEN:]); + if(ds != n){ + sys->print("Styx->Tmsg.unpack: wstat count: %d/%d\n", ds, n); # temporary + break; + } + return (H+FID+LEN+n, ref Tmsg.Wstat(tag, fid, stat)); + } + return (-1, nil); # illegal +} + +tmsgname := array[] of { +tagof Tmsg.Readerror => "Readerror", +tagof Tmsg.Version => "Version", +tagof Tmsg.Auth => "Auth", +tagof Tmsg.Attach => "Attach", +tagof Tmsg.Flush => "Flush", +tagof Tmsg.Walk => "Walk", +tagof Tmsg.Open => "Open", +tagof Tmsg.Create => "Create", +tagof Tmsg.Read => "Read", +tagof Tmsg.Write => "Write", +tagof Tmsg.Clunk => "Clunk", +tagof Tmsg.Stat => "Stat", +tagof Tmsg.Remove => "Remove", +tagof Tmsg.Wstat => "Wstat", +}; + +Tmsg.text(t: self ref Tmsg): string +{ + if(t == nil) + return "nil"; + s := sys->sprint("Tmsg.%s(%ud", tmsgname[tagof t], t.tag); + pick m:= t { + * => + return s + ",ILLEGAL)"; + Readerror => + return s + sys->sprint(",\"%s\")", m.error); + Version => + return s + sys->sprint(",%d,\"%s\")", m.msize, m.version); + Auth => + return s + sys->sprint(",%ud,\"%s\",\"%s\")", m.afid, m.uname, m.aname); + Flush => + return s + sys->sprint(",%ud)", m.oldtag); + Attach => + return s + sys->sprint(",%ud,%ud,\"%s\",\"%s\")", m.fid, m.afid, m.uname, m.aname); + Walk => + s += sys->sprint(",%ud,%ud", m.fid, m.newfid); + if(len m.names != 0){ + s += ",array[] of {"; + for(i := 0; i < len m.names; i++){ + c := ","; + if(i == 0) + c = ""; + s += sys->sprint("%s\"%s\"", c, m.names[i]); + } + s += "}"; + }else + s += ",nil"; + return s + ")"; + Open => + return s + sys->sprint(",%ud,%d)", m.fid, m.mode); + Create => + return s + sys->sprint(",%ud,\"%s\",8r%uo,%d)", m.fid, m.name, m.perm, m.mode); + Read => + return s + sys->sprint(",%ud,%bd,%ud)", m.fid, m.offset, m.count); + Write => + return s + sys->sprint(",%ud,%bd,array[%d] of byte)", m.fid, m.offset, len m.data); + Clunk or + Remove or + Stat => + return s + sys->sprint(",%ud)", m.fid); + Wstat => + return s + sys->sprint(",%ud,%s)", m.fid, dir2text(m.stat)); + } +} + +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 Styx T-message format"); + return m; +} + +rtag2type := array[] of { +tagof Rmsg.Version => Rversion, +tagof Rmsg.Auth => Rauth, +tagof Rmsg.Error => Rerror, +tagof Rmsg.Flush => Rflush, +tagof Rmsg.Attach => Rattach, +tagof Rmsg.Walk => Rwalk, +tagof Rmsg.Open => Ropen, +tagof Rmsg.Create => Rcreate, +tagof Rmsg.Read => Rread, +tagof Rmsg.Write => Rwrite, +tagof Rmsg.Clunk => Rclunk, +tagof Rmsg.Remove => Rremove, +tagof Rmsg.Stat => Rstat, +tagof Rmsg.Wstat => Rwstat, +}; + +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 := hdrlen[mtype]; + pick m := r { + Version => + ml += utflen(m.version); + Error => + ml += utflen(m.ename); + Walk => + ml += QID*len m.qids; + Read => + ml += len m.data; + Stat => + ml += packdirsize(m.stat); + } + 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); + pick m := r { + Version => + p32(d, H, m.msize); + pstring(d, H+BIT32SZ, m.version); + Auth => + pqid(d, H, m.aqid); + Flush or + Clunk or + Remove or + Wstat => + ; # nothing more required + Error => + pstring(d, H, m.ename); + Attach => + pqid(d, H, m.qid); + Walk => + n := len m.qids; + d[H] = byte n; + d[H+1] = byte (n>>8); + o := H+LEN; + for(i:=0; i<n; i++){ + pqid(d, o, m.qids[i]); + o += QID; + } + Create or + Open => + pqid(d, H, m.qid); + p32(d, H+QID, m.iounit); + Read => + v := len m.data; + d[H] = byte v; + d[H+1] = byte (v>>8); + d[H+2] = byte (v>>16); + d[H+3] = byte (v>>24); + d[H+4:] = m.data; + Write => + v := m.count; + d[H] = byte v; + d[H+1] = byte (v>>8); + d[H+2] = byte (v>>16); + d[H+3] = byte (v>>24); + Stat => + stat := packdir(m.stat); + v := len stat; + d[H] = byte v; + d[H+1] = byte (v>>8); + d[H+2:] = stat; # should avoid copy? + * => + raise sys->sprint("assertion: Styx->Rmsg.pack: missed case: 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; # size includes itself + 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 >= len hdrlen || (mtype&1) == 0 || size < hdrlen[mtype]) + return (-1, nil); + + tag := (int f[6] << 8) | int f[5]; + + # return out of each case body for a legal message; + # break out of the case for an illegal one + + case mtype { + * => + sys->print("Styx->Rmsg.unpack: bad type %d\n", mtype); # temporary + Rversion => + msize := g32(f, H); + (version, o) := gstring(f, H+BIT32SZ); + if(o <= 0) + break; + return (o, ref Rmsg.Version(tag, msize, version)); + Rauth => + return (H+QID, ref Rmsg.Auth(tag, gqid(f, H))); + Rflush => + return (H, ref Rmsg.Flush(tag)); + Rerror => + (ename, o) := gstring(f, H); + if(o <= 0) + break; + return (o, ref Rmsg.Error(tag, ename)); + Rclunk => + return (H, ref Rmsg.Clunk(tag)); + Rremove => + return (H, ref Rmsg.Remove(tag)); + Rwstat=> + return (H, ref Rmsg.Wstat(tag)); + Rattach => + return (H+QID, ref Rmsg.Attach(tag, gqid(f, H))); + Rwalk => + nqid := (int f[H+1] << 8) | int f[H]; + if(len f < H+LEN+nqid*QID) + break; + o := H+LEN; + qids := array[nqid] of Sys->Qid; + for(i:=0; i<nqid; i++){ + qids[i] = gqid(f, o); + o += QID; + } + return (o, ref Rmsg.Walk(tag, qids)); + Ropen => + return (H+QID+COUNT, ref Rmsg.Open(tag, gqid(f, H), g32(f, H+QID))); + Rcreate=> + return (H+QID+COUNT, ref Rmsg.Create(tag, gqid(f, H), g32(f, H+QID))); + Rread => + count := g32(f, H); + if(len f < H+COUNT+count) + break; + data := f[H+COUNT:H+COUNT+count]; + return (H+COUNT+count, ref Rmsg.Read(tag, data)); + Rwrite => + return (H+COUNT, ref Rmsg.Write(tag, g32(f, H))); + Rstat => + n := (int f[H+1] << 8) | int f[H]; + if(len f < H+LEN+n) + break; + (ds, d) := unpackdir(f[H+LEN:]); + if(ds <= 0) + break; + if(ds != n){ + sys->print("Styx->Rmsg.unpack: stat count: %d/%d\n", ds, n); # temporary + break; + } + return (H+LEN+n, ref Rmsg.Stat(tag, d)); + } + return (-1, nil); # illegal +} + +rmsgname := array[] of { +tagof Rmsg.Version => "Version", +tagof Rmsg.Auth => "Auth", +tagof Rmsg.Attach => "Attach", +tagof Rmsg.Error => "Error", +tagof Rmsg.Flush => "Flush", +tagof Rmsg.Walk => "Walk", +tagof Rmsg.Create => "Create", +tagof Rmsg.Open => "Open", +tagof Rmsg.Read => "Read", +tagof Rmsg.Write => "Write", +tagof Rmsg.Clunk => "Clunk", +tagof Rmsg.Remove => "Remove", +tagof Rmsg.Stat => "Stat", +tagof Rmsg.Wstat => "Wstat", +}; + +Rmsg.text(r: self ref Rmsg): string +{ + if(sys == nil) + sys = load Sys Sys->PATH; + if(r == nil) + return "nil"; + s := sys->sprint("Rmsg.%s(%ud", rmsgname[tagof r], r.tag); + pick m := r { + * => + return s + "ERROR)"; + Readerror => + return s + sys->sprint(",\"%s\")", m.error); + Version => + return s + sys->sprint(",%d,\"%s\")", m.msize, m.version); + Auth => + return s+sys->sprint(",%s)", qid2text(m.aqid)); + Error => + return s+sys->sprint(",\"%s\")", m.ename); + Flush or + Clunk or + Remove or + Wstat => + return s+")"; + Attach => + return s+sys->sprint(",%s)", qid2text(m.qid)); + Walk => + s += ",array[] of {"; + for(i := 0; i < len m.qids; i++){ + c := ""; + if(i != 0) + c = ","; + s += sys->sprint("%s%s", c, qid2text(m.qids[i])); + } + return s+"})"; + Create or + Open => + return s+sys->sprint(",%s,%d)", qid2text(m.qid), m.iounit); + Read => + return s+sys->sprint(",array[%d] of byte)", len m.data); + Write => + return s+sys->sprint(",%d)", m.count); + Stat => + return s+sys->sprint(",%s)", dir2text(m.stat)); + } +} + +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 Styx R-message format"); + return m; +} + +dir2text(d: Sys->Dir): string +{ + return sys->sprint("Dir(\"%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("Qid(16r%ubx,%d,16r%.2ux)", q.path, q.vers, q.qtype); +} + +readmsg(fd: ref Sys->FD, msglim: int): (array of byte, string) +{ + if(msglim <= 0) + msglim = MAXRPC; + 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 Styx message size"); + if(ml > msglim) + return (nil, "Styx message longer than agreed"); + buf := array[ml] of byte; + buf[0:] = sbuf; + if((n = readn(fd, buf[BIT32SZ:], ml-BIT32SZ)) != ml-BIT32SZ){ + if(n == 0) + return (nil, "Styx message truncated"); + return (nil, sys->sprint("%r")); + } + return (buf, nil); +} + +readn(fd: ref Sys->FD, buf: array of byte, nb: int): int +{ + for(nr := 0; nr < nb;){ + n := sys->read(fd, buf[nr:], nb-nr); + if(n <= 0){ + if(nr == 0) + return n; + break; + } + nr += n; + } + return nr; +} + +istmsg(f: array of byte): int +{ + if(len f < H) + return -1; + return (int f[BIT32SZ] & 1) == 0; +} + +compatible(t: ref Tmsg.Version, msize: int, version: string): (int, string) +{ + if(version == nil) + version = VERSION; + if(t.msize < msize) + msize = t.msize; + v := t.version; + if(len v < 2 || v[0:2] != "9P") + return (msize, "unknown"); + for(i:=2; i<len v; i++) + if((c := v[i]) == '.'){ + v = v[0:i]; + break; + }else if(!(c >= '0' && c <= '9')) + return (msize, "unknown"); # fussier than Plan 9 + if(v < VERSION) + return (msize, "unknown"); + if(v < version) + version = v; + return (msize, version); +} + +# only here to support an implementation of this module that talks the previous version of Styx +write(fd: ref Sys->FD, buf: array of byte, nb: int): int +{ + return sys->write(fd, buf, nb); +} |
