diff options
| author | forsyth <forsyth@vitanuova.com> | 2010-02-21 12:11:43 +0000 |
|---|---|---|
| committer | forsyth <forsyth@vitanuova.com> | 2010-02-21 12:11:43 +0000 |
| commit | c93eaa9a36d5071c19f42debcef978afd89ec994 (patch) | |
| tree | c3a3b8048c3ceaf7aa887922f754826c74ff1591 | |
| parent | aee7f58dfcd519d45482e45ab1ce6026c846dc21 (diff) | |
20100221-1211
| -rw-r--r-- | CHANGES | 2 | ||||
| -rw-r--r-- | appl/cmd/9660srv.b | 2 | ||||
| -rw-r--r-- | appl/cmd/auth/keyfs.b | 4 | ||||
| -rw-r--r-- | appl/cmd/dossrv.b | 9 | ||||
| -rw-r--r-- | appl/cmd/ftpfs.b | 2 | ||||
| -rw-r--r-- | appl/lib/ninep.b | 914 | ||||
| -rw-r--r-- | appl/lib/styx.b | 28 | ||||
| -rw-r--r-- | dis/9660srv.dis | bin | 18232 -> 18232 bytes | |||
| -rw-r--r-- | dis/auth/keyfs.dis | bin | 14490 -> 14490 bytes | |||
| -rw-r--r-- | dis/dossrv.dis | bin | 45736 -> 45631 bytes | |||
| -rw-r--r-- | dis/ftpfs.dis | bin | 22803 -> 22793 bytes | |||
| -rw-r--r-- | dis/lib/ninep.dis | bin | 0 -> 16469 bytes | |||
| -rw-r--r-- | dis/lib/styx.dis | bin | 16609 -> 16519 bytes | |||
| -rw-r--r-- | include/version.h | 2 | ||||
| -rw-r--r-- | man/2/9p-ninep | 416 | ||||
| -rw-r--r-- | module/9p.m | 178 |
16 files changed, 1526 insertions, 31 deletions
@@ -1,3 +1,5 @@ +20100220 + appl/cmd/keyfs.b wasn't clunking fids on error in remove [issue 225] 20100213 apply saoret.one's changes for thumb [issue 155] 20100205 diff --git a/appl/cmd/9660srv.b b/appl/cmd/9660srv.b index 17fa053b..90de6918 100644 --- a/appl/cmd/9660srv.b +++ b/appl/cmd/9660srv.b @@ -185,7 +185,7 @@ fileserve(rfd: ref Sys->FD, sync: chan of int) error("bad R-message conversion"); if(showstyx) chat(r.text()+"\n"); - if(styx->write(rfd, rbuf, len rbuf) != len rbuf) + if(sys->write(rfd, rbuf, len rbuf) != len rbuf) error(sys->sprint("connection write error: %r")); } diff --git a/appl/cmd/auth/keyfs.b b/appl/cmd/auth/keyfs.b index f81c3ee7..f1f1a25a 100644 --- a/appl/cmd/auth/keyfs.b +++ b/appl/cmd/auth/keyfs.b @@ -415,6 +415,7 @@ serveloop(tchan: chan of ref Tmsg, srv: ref Styxserver, pidc: chan of int, navop } case TYPE(c.path) { Quser => + srv.delfid(c); u := finduserpath(c.path); if(u == nil){ srv.reply(ref Rmsg.Error(m.tag, Eremoved)); @@ -422,9 +423,9 @@ serveloop(tchan: chan of ref Tmsg, srv: ref Styxserver, pidc: chan of int, navop } removeuser(u); writekeys(keyfile); - srv.delfid(c); srv.reply(ref Rmsg.Remove(m.tag)); Qsecret => + srv.delfid(c); u := finduserpath(c.path); if(u == nil){ srv.reply(ref Rmsg.Error(m.tag, Eremoved)); @@ -432,7 +433,6 @@ serveloop(tchan: chan of ref Tmsg, srv: ref Styxserver, pidc: chan of int, navop } u.secret = nil; writekeys(keyfile); - srv.delfid(c); srv.reply(ref Rmsg.Remove(m.tag)); * => srv.remove(m); # let it reject it diff --git a/appl/cmd/dossrv.b b/appl/cmd/dossrv.b index aefe7948..1cfcdf93 100644 --- a/appl/cmd/dossrv.b +++ b/appl/cmd/dossrv.b @@ -195,7 +195,6 @@ dossrv(rfd: ref Sys->FD) { sys->pctl(Sys->NEWFD, rfd.fd :: 2 :: nil); rfd = sys->fildes(rfd.fd); - data := array[Styx->MAXRPC] of byte; while((t := Tmsg.read(rfd, 0)) != nil){ if(debug & STYX_MESS) chat(sys->sprint("%s...", t.text())); @@ -242,7 +241,7 @@ dossrv(rfd: ref Sys->FD) panic("Rmsg.pack"); if(debug & STYX_MESS) chat(sys->sprint("%s\n", r.text())); - if(styx->write(rfd, rbuf, len rbuf) != len rbuf) + if(sys->write(rfd, rbuf, len rbuf) != len rbuf) panic("mount write"); } @@ -967,7 +966,7 @@ rwstat(t: ref Tmsg.Wstat): ref Rmsg oaddr := dp.addr; ooffset := dp.offset; d := dpd; - od := *d; +# od := *d; # start := getstart(f.xf, d); start := d.start; length := d.length; @@ -1395,7 +1394,7 @@ putstart(nil: ref Xfs, d: ref Dosdir, start: int) fileclust(f: ref Xfile, iclust: int, cflag: int): int { - bp := f.xf.ptr; +# bp := f.xf.ptr; dp := f.ptr; if(isroot(dp.addr)) return -1; # root directory for old FAT format does not start on a cluster boundary @@ -2067,7 +2066,7 @@ writefile(f: ref Xfile, buf: array of byte, offset,count: int): int truncfile(f: ref Xfile): int { xf := f.xf; - bp := xf.ptr; +# bp := xf.ptr; dp := f.ptr; d := Dosdir.arr2Dd(dp.p.iobuf[dp.offset:dp.offset+DOSDIRSIZE]); diff --git a/appl/cmd/ftpfs.b b/appl/cmd/ftpfs.b index ee5a44d4..9d074124 100644 --- a/appl/cmd/ftpfs.b +++ b/appl/cmd/ftpfs.b @@ -280,7 +280,7 @@ sendreply(r: ref Rmsg) if (debug) sys->print("> %s\n", r.text()); a := r.pack(); - if(styx->write(styxfd, a, len a) != len a) + if(sys->write(styxfd, a, len a) != len a) sys->print("ftpfs: error replying: %r\n"); } diff --git a/appl/lib/ninep.b b/appl/lib/ninep.b new file mode 100644 index 00000000..91467f0b --- /dev/null +++ b/appl/lib/ninep.b @@ -0,0 +1,914 @@ +implement Ninep; + +include "sys.m"; + sys: Sys; + +include "9p.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: Ninep->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: Ninep->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("Ninep->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 9P 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: Ninep->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("Ninep->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("Ninep->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 9P 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 = DEFMSIZE; + sbuf := array[BIT32SZ] of byte; + if((n := sys->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 9P message size"); + if(ml > msglim) + return (nil, "9P message longer than agreed"); + buf := array[ml] of byte; + buf[0:] = sbuf; + if((n = sys->readn(fd, buf[BIT32SZ:], ml-BIT32SZ)) != ml-BIT32SZ){ + if(n == 0) + return (nil, "9P 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; +} + +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); +} diff --git a/appl/lib/styx.b b/appl/lib/styx.b index f4534c6c..c862c9a1 100644 --- a/appl/lib/styx.b +++ b/appl/lib/styx.b @@ -571,7 +571,7 @@ Tmsg.read(fd: ref Sys->FD, msglim: int): ref Tmsg return nil; (nil, m) := Tmsg.unpack(msg); if(m == nil) - return ref Tmsg.Readerror(0, "bad Styx T-message format"); + return ref Tmsg.Readerror(0, "bad 9P T-message format"); return m; } @@ -843,7 +843,7 @@ Rmsg.read(fd: ref Sys->FD, msglim: int): ref Rmsg return nil; (nil, m) := Rmsg.unpack(msg); if(m == nil) - return ref Rmsg.Readerror(0, "bad Styx R-message format"); + return ref Rmsg.Readerror(0, "bad 9P R-message format"); return m; } @@ -863,7 +863,7 @@ 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 := sys->readn(fd, sbuf, BIT32SZ)) != BIT32SZ){ if(n == 0) return (nil, nil); return (nil, sys->sprint("%r")); @@ -871,33 +871,19 @@ readmsg(fd: ref Sys->FD, msglim: int): (array of byte, string) 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"); + return (nil, "invalid 9P message size"); if(ml > msglim) - return (nil, "Styx message longer than agreed"); + return (nil, "9P message longer than agreed"); buf := array[ml] of byte; buf[0:] = sbuf; - if((n = readn(fd, buf[BIT32SZ:], ml-BIT32SZ)) != ml-BIT32SZ){ + if((n = sys->readn(fd, buf[BIT32SZ:], ml-BIT32SZ)) != ml-BIT32SZ){ if(n == 0) - return (nil, "Styx message truncated"); + return (nil, "9P 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) diff --git a/dis/9660srv.dis b/dis/9660srv.dis Binary files differindex 1c2fac58..c837a336 100644 --- a/dis/9660srv.dis +++ b/dis/9660srv.dis diff --git a/dis/auth/keyfs.dis b/dis/auth/keyfs.dis Binary files differindex 71ba386c..edb7b654 100644 --- a/dis/auth/keyfs.dis +++ b/dis/auth/keyfs.dis diff --git a/dis/dossrv.dis b/dis/dossrv.dis Binary files differindex 2e652041..7f699f13 100644 --- a/dis/dossrv.dis +++ b/dis/dossrv.dis diff --git a/dis/ftpfs.dis b/dis/ftpfs.dis Binary files differindex 22fcb23f..f3cc69c3 100644 --- a/dis/ftpfs.dis +++ b/dis/ftpfs.dis diff --git a/dis/lib/ninep.dis b/dis/lib/ninep.dis Binary files differnew file mode 100644 index 00000000..bb88078f --- /dev/null +++ b/dis/lib/ninep.dis diff --git a/dis/lib/styx.dis b/dis/lib/styx.dis Binary files differindex 88354c7d..426ae444 100644 --- a/dis/lib/styx.dis +++ b/dis/lib/styx.dis diff --git a/include/version.h b/include/version.h index ea0b4213..9436d6bc 100644 --- a/include/version.h +++ b/include/version.h @@ -1 +1 @@ -#define VERSION "Fourth Edition (20100213)" +#define VERSION "Fourth Edition (20100221)" diff --git a/man/2/9p-ninep b/man/2/9p-ninep new file mode 100644 index 00000000..f2f9e6c4 --- /dev/null +++ b/man/2/9p-ninep @@ -0,0 +1,416 @@ +.TH 9P-NINEP 2 +.SH NAME +Ninep: Rmsg, Tmsg, dir2text, istmsg, packdir, packdirsize, readmsg, qid2text, unpackdir \- interface to 9P file protocol +.SH SYNOPSIS +.EX +include "9p.m"; +ninep := load Ninep Ninep->PATH; + +Tmsg: adt { + tag: int; + pick { + Readerror => + error: string; # tag is unused in this case + Version => + msize: int; + version: string; + Auth => + afid: int; + uname, aname: string; + Attach => + fid, afid: int; + uname, aname: string; + Flush => + oldtag: int; + Walk => + fid, newfid: int; + names: array of string; + Open => + fid, mode: int; + Create => + fid: int; + name: string; + perm, mode: int; + Read => + fid: int; + offset: big; + count: int; + Write => + fid: int; + offset: big; + data: array of byte; + Clunk or + Stat or + Remove => + fid: int; + Wstat => + fid: int; + stat: Sys->Dir; + } + + read: fn(fd: ref Sys->FD, msize: int): ref Tmsg; + unpack: fn(a: array of byte): (int, ref Tmsg); + pack: fn(nil: self ref Tmsg): array of byte; + packedsize: fn(nil: self ref Tmsg): int; + text: fn(nil: self ref Tmsg): string; + mtype: fn(nil: self ref Tmsg): int; +}; + +Rmsg: adt { + tag: int; + pick { + Readerror => + error: string; # tag is unused in this case + Version => + msize: int; + version: string; + Auth => + aqid: Sys->Qid; + Attach => + qid: Sys->Qid; + Flush => + Error => + ename: string; + Clunk or + Remove or + Wstat => + Walk => + qids: array of Sys->Qid; + Create or + Open => + qid: Sys->Qid; + iounit: int; + Read => + data: array of byte; + Write => + count: int; + Stat => + stat: Sys->Dir; + } + + read: fn(fd: ref Sys->FD, msize: int): ref Rmsg; + unpack: fn(a: array of byte): (int, ref Rmsg); + pack: fn(nil: self ref Rmsg): array of byte; + packedsize: fn(nil: self ref Rmsg): int; + text: fn(nil: self ref Rmsg): string; + mtype: fn(nil: self ref Rmsg): int; +}; + +init: fn(); + +readmsg: fn(fd: ref Sys->FD, msize: int): (array of byte, string); +istmsg: fn(f: array of byte): int; + +compatible: fn(t: ref Tmsg.Version, msize: int, version: string): (int, string); + +packdirsize: fn(d: Sys->Dir): int; +packdir: fn(d: Sys->Dir): array of byte; +unpackdir: fn(f: array of byte): (int, Sys->Dir); +dir2text: fn(d: Sys->Dir): string; +qid2text: fn(q: Sys->Qid): string; + +VERSION: con "9P2000"; +MAXWELEM: con 16; +NOTAG: con 16rFFFF; +NOFID: con ~0; +IOHDRSZ: con \fIimplementation-defined\f5; +DEFMSIZE: con \fIimplementation-defined\f5; +.EE +.SH DESCRIPTION +.B Ninep +provides a Limbo interface to send and receive messages of the 9P file service protocol, +described by Section 5 of this manual, a thorough reading of which +is advised before using this module. +.B Init +must be called before using any other function in the module. +.PP +A 9P client transmits requests to a server as `T-messages' +and receives replies in matching `R-messages'. +A T-message is here represented by values of the type +.BR Tmsg , +and an R-message by values of type +.BR Rmsg . +Every message has a +.B tag +value, and the alternatives of the pick adt represent the possible operation types of a T-message, +generally with parameter names and types corresponding to those described in section 5. +The exceptions are: +.B Tmsg.Write +and +.B Rmsg.Read +contain an array of byte, +.BR data , +to hold the data for the corresponding message, and the `count' parameter of the message is simply the length of that array; +and there is an alternative labelled +.B Readerror +that does not appear in the protocol but is used to represent input errors as described below. +Also note that values that are `unsigned' integers in the protocol are typically given signed integer +types in the Limbo representation (in particular, fids, qid paths, counts and offsets), and applications +should take appropriate care when manipulating them. +.PP +The following functions are provided by +.BR Tmsg: +.TP +.BI read( fd\fP,\fP\ msize ) +Read file descriptor +.I fd +to obtain exactly one T-message and return (a reference to) the corresponding +.BR Tmsg . +A nil value is returned on end of file. +Otherwise, if the read fails or the data read does not form a valid T-message, +the value returned will be a +.B Tmsg.Readerror +value in which the +.B error +member describes the error. +.I Msize +gives the maximum number of bytes in any acceptable T-message, +and should be the value negotiated in the exchange of +.B version +messages; +any incoming message larger than that will result in a diagnostic as a +.B Tmsg.Readerror +value. +An +.I msize +of 0 means `no limit negotiated' and should (only) be used until a message size +has been negotiated by exchange of +.IR version (5) +messages. +.TP +.IB t .pack() +Return an array of bytes containing the value of +.I t +in the machine-independent format described in Section 5. +It can return nil only if the message +.I t +is itself nil or has an invalid type. +.TP +.BI unpack( a ) +The array +.I a +is assumed to contain zero or more T-messages. +.B Unpack +attempts to unpack the first message, and returns a tuple of the form +.RI ( n , v ). +If successful, +.I n +is the number of bytes at the start of +.I a +used by the message, and +.I v +is the corresponding +.B Tmsg +value. +If +.I a +contains the prefix of a valid message but more data is required to complete it, +.I n +is zero (and +.I v +is nil); the caller will typically read more data, append it to +.IR a , +and try again. +If the message is invalid, +.I n +is -1 +and +.I v +is nil. +.TP +.IB t .packedsize() +Return the number of bytes required for the value of +.I t +when packed in its machine-independent format. +(Zero is returned if +.I t +is invalid.) +.TP +.IB t .text() +Return a printable string showing the contents of +.IR t , +for tracing or debugging. +.TP +.IB t .mtype() +Return the 9P message type of the message. +.PP +An R-message is represented by +.BR Rmsg . +Its member functions behave exactly as those for +.BR Tmsg , +except that they operate on R-messages not T-messages. +.PP +When a client reads a directory, the data returned in the reply must be formatted +as described in +.IR read (5): +an array of directory entries, one per file, with each entry formatted in +a machine-independent format. +An appropriate array value can be produced by +.B packdir +from a +.B Sys->Dir +structure, as used by +.IR sys-stat (2). +The space that packed representation will take can be calculated beforehand by +.BR packdirsize . +The server will usually fill the buffer for the reply to the read +with as many entries as will fit, +checking the space remaining against the result of +.B packdirsize +and if the value will fit, storing the result of +.BR packdir . +Given an array +.I a +containing at most one packed directory value (as produced by +.BR packdir ), +.B unpackdir +returns a tuple +.RI ( n,\ d ) +where +.I n +is \-1 if +.I a +is illegally formatted; +.I n +is zero if +.I a +does not contain a complete directory entry value; +and otherwise +.I n +is the number of bytes of +.I a +used to produce the unpacked +.B Dir +value +.I d . +.PP +The functions +.B dir2text +and +.B qid2text +produce printable strings showing the contents of the corresponding data structures, +for use when tracing or debugging. +.PP +Applications that acts as file servers will read T-messages and +reply with R-messages. +They can use +.B Tmsg.read +to read each T-message, build an +.B Rmsg +reply value +.IR r , +and use +.IB r .pack +to produce an array of bytes to be written in reply by +.B Sys->write +(see +.IR sys-read (2)). +.PP +A few specialised programs might need the lower-level function +.B readmsg +that underlies +.B Tmsg.read +and +.BR Rmsg.read . +It reads a single message, which can be either a T-message or R-message, +and returns it as an array of bytes, +which can then be unpacked using +.B Tmsg.unpack +or +.BR Rmsg.unpack . +.I Msize +is the negotiated message size, or 0 meaning `no limit'. +The predicate +.B istmsg +returns true if the contents of array +.I f +looks like a packed representation of a T-message, +judging only by its +.I type +byte. +.PP +When generating the +.B version +message (see +.IR version (5)), +the constant +.B NOTAG +can be used in +.B Tmsg.tag +and +.B Rmsg.tag +to represent `no tag value'. +The constant +.B VERSION +names the current version of the protocol, and can be +used as the value of +.BR Tmsg.version . +.PP +.B Compatible +can be used by a server to +compare its +.I msize +and +.I version +(which is typically +.BR VERSION ) +to those in the +.B Tmsg.Version +message received from a client, to decide its reply, +following the rules in +.IR version (5). +It returns a tuple +.RI ( m ", " v ) +with values for use in the +.B Rmsg.Version +reply. +.I M +is the lesser of +.I msize +and +.IB t .msize , +and +.I v +is the negotiated protocol version, or the value \f5"unknown"\f1 +if no version could be agreed. +The constant +.B DEFMSIZE +is a reasonable value for +.I msize +on current systems. +The resulting value +.I m +can subsequently be given to the various read functions as the limit +.IR msize . +The constant +.B IOHDRSZ +gives the amount to allow for protocol overhead, when limiting data size for +.B Tmsg.Write +and +.BR Rmsg.Read . +.PP +The constant +.B NOFID +can be used as the value of +.B afid +of the +.B attach +message when authentication is not required (see +.IR attach (5)). +.PP +The constant +.B MAXWELEM +gives the protocol-defined limit on the length of the arrays +.B Tmsg.names +and +.BR Rmsg.qids . +For specialised applications, the module defines constants +.BR Tversion , +.BR Rversion , +etc. for the message types of the protocol, and the +other constants mentioned in Section 5. +.SH SOURCE +.B /appl/lib/ninep.b +.SH SEE ALSO +.IR styxservers (2), +.IR intro (5) diff --git a/module/9p.m b/module/9p.m new file mode 100644 index 00000000..6201e638 --- /dev/null +++ b/module/9p.m @@ -0,0 +1,178 @@ +Ninep: module +{ + PATH: con "/dis/lib/ninep.dis"; + + VERSION: con "9P2000"; + MAXWELEM: con 16; + + NOTAG: con 16rFFFF; + NOFID: con int ~0; # 32 bits in this version of 9P + + BIT8SZ: con 1; + BIT16SZ: con 2; + BIT32SZ: con 4; + BIT64SZ: con 8; + QIDSZ: con BIT8SZ+BIT32SZ+BIT64SZ; + + STATFIXLEN: con BIT16SZ+QIDSZ+5*BIT16SZ+4*BIT32SZ+BIT64SZ; # amount of fixed length data in a stat buffer + IOHDRSZ: con 24; # room for Twrite/Rread header + DEFIOUNIT: con 8192; # `reasonable' iounit + DEFMSIZE: con IOHDRSZ+DEFIOUNIT; # usable default for fversion and iounit + + Tversion, # 100 + Rversion, + Tauth, # 102 + Rauth, + Tattach, # 104 + Rattach, + Terror, # 106, illegal + Rerror, + Tflush, #108 + Rflush, + Twalk, # 110 + Rwalk, + Topen, # 112 + Ropen, + Tcreate, # 114 + Rcreate, + Tread, # 116 + Rread, + Twrite, # 118 + Rwrite, + Tclunk, # 120 + Rclunk, + Tremove, # 122 + Rremove, + Tstat, # 124 + Rstat, + Twstat, #126 + Rwstat, + Tmax: con 100+iota; + + ERRMAX: con 128; + + OREAD: con 0; # open for read + OWRITE: con 1; # write + ORDWR: con 2; # read and write + OEXEC: con 3; # execute, == read but check execute permission + OTRUNC: con 16; # or'ed in (except for exec), truncate file first + ORCLOSE: con 64; # or'ed in, remove on close + + # mode bits in Dir.mode used by the protocol + DMDIR: con int 1<<31; # mode bit for directory + DMAPPEND: con int 1<<30; # mode bit for append-only files + DMEXCL: con int 1<<29; # mode bit for exclusive use files + DMAUTH: con int 1<<27; # mode bit for authentication files + + # Qid.qtype + QTDIR: con 16r80; + QTAPPEND: con 16r40; + QTEXCL: con 16r20; + QTAUTH: con 16r08; + QTFILE: con 16r00; + + Tmsg: adt { + tag: int; + pick { + Readerror => + error: string; # tag is unused in this case + Version => + msize: int; + version: string; + Auth => + afid: int; + uname, aname: string; + Attach => + fid, afid: int; + uname, aname: string; + Flush => + oldtag: int; + Walk => + fid, newfid: int; + names: array of string; + Open => + fid, mode: int; + Create => + fid: int; + name: string; + perm, mode: int; + Read => + fid: int; + offset: big; + count: int; + Write => + fid: int; + offset: big; + data: array of byte; + Clunk or + Stat or + Remove => + fid: int; + Wstat => + fid: int; + stat: Sys->Dir; + } + + read: fn(fd: ref Sys->FD, msize: int): ref Tmsg; + unpack: fn(a: array of byte): (int, ref Tmsg); + pack: fn(nil: self ref Tmsg): array of byte; + packedsize: fn(nil: self ref Tmsg): int; + text: fn(nil: self ref Tmsg): string; + mtype: fn(nil: self ref Tmsg): int; + }; + + Rmsg: adt { + tag: int; + pick { + Readerror => + error: string; # tag is unused in this case + Version => + msize: int; + version: string; + Auth => + aqid: Sys->Qid; + Attach => + qid: Sys->Qid; + Flush => + Error => + ename: string; + Clunk or + Remove or + Wstat => + Walk => + qids: array of Sys->Qid; + Create or + Open => + qid: Sys->Qid; + iounit: int; + Read => + data: array of byte; + Write => + count: int; + Stat => + stat: Sys->Dir; + } + + read: fn(fd: ref Sys->FD, msize: int): ref Rmsg; + unpack: fn(a: array of byte): (int, ref Rmsg); + pack: fn(nil: self ref Rmsg): array of byte; + packedsize: fn(nil: self ref Rmsg): int; + text: fn(nil: self ref Rmsg): string; + mtype: fn(nil: self ref Rmsg): int; + }; + + init: fn(); + + readmsg: fn(fd: ref Sys->FD, msize: int): (array of byte, string); + istmsg: fn(f: array of byte): int; + + compatible: fn(t: ref Tmsg.Version, msize: int, version: string): (int, string); + + packdirsize: fn(d: Sys->Dir): int; + packdir: fn(d: Sys->Dir): array of byte; + unpackdir: fn(f: array of byte): (int, Sys->Dir); + dir2text: fn(d: Sys->Dir): string; + qid2text: fn(q: Sys->Qid): string; + + utflen: fn(s: string): int; +}; |
