| @@ -0,0 +1,914 @@ |
| 1 | +implement Ninep; |
| 2 | + |
| 3 | +include "sys.m"; |
| 4 | + sys: Sys; |
| 5 | + |
| 6 | +include "9p.m"; |
| 7 | + |
| 8 | +STR: con BIT16SZ; # string length |
| 9 | +TAG: con BIT16SZ; |
| 10 | +FID: con BIT32SZ; |
| 11 | +QID: con BIT8SZ+BIT32SZ+BIT64SZ; |
| 12 | +LEN: con BIT16SZ; # stat and qid array lengths |
| 13 | +COUNT: con BIT32SZ; |
| 14 | +OFFSET: con BIT64SZ; |
| 15 | + |
| 16 | +H: con BIT32SZ+BIT8SZ+BIT16SZ; # minimum header length: size[4] type tag[2] |
| 17 | + |
| 18 | +# |
| 19 | +# the following array could be shorter if it were indexed by (type-Tversion) |
| 20 | +# |
| 21 | +hdrlen := array[Tmax] of |
| 22 | +{ |
| 23 | +Tversion => H+COUNT+STR, # size[4] Tversion tag[2] msize[4] version[s] |
| 24 | +Rversion => H+COUNT+STR, # size[4] Rversion tag[2] msize[4] version[s] |
| 25 | + |
| 26 | +Tauth => H+FID+STR+STR, # size[4] Tauth tag[2] afid[4] uname[s] aname[s] |
| 27 | +Rauth => H+QID, # size[4] Rauth tag[2] aqid[13] |
| 28 | + |
| 29 | +Rerror => H+STR, # size[4] Rerror tag[2] ename[s] |
| 30 | + |
| 31 | +Tflush => H+TAG, # size[4] Tflush tag[2] oldtag[2] |
| 32 | +Rflush => H, # size[4] Rflush tag[2] |
| 33 | + |
| 34 | +Tattach => H+FID+FID+STR+STR, # size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s] |
| 35 | +Rattach => H+QID, # size[4] Rattach tag[2] qid[13] |
| 36 | + |
| 37 | +Twalk => H+FID+FID+LEN, # size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s]) |
| 38 | +Rwalk => H+LEN, # size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13]) |
| 39 | + |
| 40 | +Topen => H+FID+BIT8SZ, # size[4] Topen tag[2] fid[4] mode[1] |
| 41 | +Ropen => H+QID+COUNT, # size[4] Ropen tag[2] qid[13] iounit[4] |
| 42 | + |
| 43 | +Tcreate => H+FID+STR+BIT32SZ+BIT8SZ, # size[4] Tcreate tag[2] fid[4] name[s] perm[4] mode[1] |
| 44 | +Rcreate => H+QID+COUNT, # size[4] Rcreate tag[2] qid[13] iounit[4] |
| 45 | + |
| 46 | +Tread => H+FID+OFFSET+COUNT, # size[4] Tread tag[2] fid[4] offset[8] count[4] |
| 47 | +Rread => H+COUNT, # size[4] Rread tag[2] count[4] data[count] |
| 48 | + |
| 49 | +Twrite => H+FID+OFFSET+COUNT, # size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count] |
| 50 | +Rwrite => H+COUNT, # size[4] Rwrite tag[2] count[4] |
| 51 | + |
| 52 | +Tclunk => H+FID, # size[4] Tclunk tag[2] fid[4] |
| 53 | +Rclunk => H, # size[4] Rclunk tag[2] |
| 54 | + |
| 55 | +Tremove => H+FID, # size[4] Tremove tag[2] fid[4] |
| 56 | +Rremove => H, # size[4] Rremove tag[2] |
| 57 | + |
| 58 | +Tstat => H+FID, # size[4] Tstat tag[2] fid[4] |
| 59 | +Rstat => H+LEN, # size[4] Rstat tag[2] stat[n] |
| 60 | + |
| 61 | +Twstat => H+FID+LEN, # size[4] Twstat tag[2] fid[4] stat[n] |
| 62 | +Rwstat => H, # size[4] Rwstat tag[2] |
| 63 | +}; |
| 64 | + |
| 65 | +init() |
| 66 | +{ |
| 67 | + sys = load Sys Sys->PATH; |
| 68 | +} |
| 69 | + |
| 70 | +utflen(s: string): int |
| 71 | +{ |
| 72 | + # the domain is 16-bit unicode only, which is all that Inferno now implements |
| 73 | + n := l := len s; |
| 74 | + for(i:=0; i<l; i++) |
| 75 | + if((c := s[i]) > 16r7F){ |
| 76 | + n++; |
| 77 | + if(c > 16r7FF) |
| 78 | + n++; |
| 79 | + } |
| 80 | + return n; |
| 81 | +} |
| 82 | + |
| 83 | +packdirsize(d: Sys->Dir): int |
| 84 | +{ |
| 85 | + return STATFIXLEN+utflen(d.name)+utflen(d.uid)+utflen(d.gid)+utflen(d.muid); |
| 86 | +} |
| 87 | + |
| 88 | +packdir(f: Sys->Dir): array of byte |
| 89 | +{ |
| 90 | + ds := packdirsize(f); |
| 91 | + a := array[ds] of byte; |
| 92 | + # size[2] |
| 93 | + a[0] = byte (ds-LEN); |
| 94 | + a[1] = byte ((ds-LEN)>>8); |
| 95 | + # type[2] |
| 96 | + a[2] = byte f.dtype; |
| 97 | + a[3] = byte (f.dtype>>8); |
| 98 | + # dev[4] |
| 99 | + a[4] = byte f.dev; |
| 100 | + a[5] = byte (f.dev>>8); |
| 101 | + a[6] = byte (f.dev>>16); |
| 102 | + a[7] = byte (f.dev>>24); |
| 103 | + # qid.type[1] |
| 104 | + # qid.vers[4] |
| 105 | + # qid.path[8] |
| 106 | + pqid(a, 8, f.qid); |
| 107 | + # mode[4] |
| 108 | + a[21] = byte f.mode; |
| 109 | + a[22] = byte (f.mode>>8); |
| 110 | + a[23] = byte (f.mode>>16); |
| 111 | + a[24] = byte (f.mode>>24); |
| 112 | + # atime[4] |
| 113 | + a[25] = byte f.atime; |
| 114 | + a[26] = byte (f.atime>>8); |
| 115 | + a[27] = byte (f.atime>>16); |
| 116 | + a[28] = byte (f.atime>>24); |
| 117 | + # mtime[4] |
| 118 | + a[29] = byte f.mtime; |
| 119 | + a[30] = byte (f.mtime>>8); |
| 120 | + a[31] = byte (f.mtime>>16); |
| 121 | + a[32] = byte (f.mtime>>24); |
| 122 | + # length[8] |
| 123 | + p64(a, 33, big f.length); |
| 124 | + # name[s] |
| 125 | + i := pstring(a, 33+BIT64SZ, f.name); |
| 126 | + i = pstring(a, i, f.uid); |
| 127 | + i = pstring(a, i, f.gid); |
| 128 | + i = pstring(a, i, f.muid); |
| 129 | + if(i != len a) |
| 130 | + raise "assertion: Ninep->packdir: bad count"; # can't happen unless packedsize is wrong |
| 131 | + return a; |
| 132 | +} |
| 133 | + |
| 134 | +pqid(a: array of byte, o: int, q: Sys->Qid): int |
| 135 | +{ |
| 136 | + a[o] = byte q.qtype; |
| 137 | + v := q.vers; |
| 138 | + a[o+1] = byte v; |
| 139 | + a[o+2] = byte (v>>8); |
| 140 | + a[o+3] = byte (v>>16); |
| 141 | + a[o+4] = byte (v>>24); |
| 142 | + v = int q.path; |
| 143 | + a[o+5] = byte v; |
| 144 | + a[o+6] = byte (v>>8); |
| 145 | + a[o+7] = byte (v>>16); |
| 146 | + a[o+8] = byte (v>>24); |
| 147 | + v = int (q.path >> 32); |
| 148 | + a[o+9] = byte v; |
| 149 | + a[o+10] = byte (v>>8); |
| 150 | + a[o+11] = byte (v>>16); |
| 151 | + a[o+12] = byte (v>>24); |
| 152 | + return o+QID; |
| 153 | +} |
| 154 | + |
| 155 | +pstring(a: array of byte, o: int, s: string): int |
| 156 | +{ |
| 157 | + sa := array of byte s; # could do conversion ourselves |
| 158 | + n := len sa; |
| 159 | + a[o] = byte n; |
| 160 | + a[o+1] = byte (n>>8); |
| 161 | + a[o+2:] = sa; |
| 162 | + return o+LEN+n; |
| 163 | +} |
| 164 | + |
| 165 | +p32(a: array of byte, o: int, v: int): int |
| 166 | +{ |
| 167 | + a[o] = byte v; |
| 168 | + a[o+1] = byte (v>>8); |
| 169 | + a[o+2] = byte (v>>16); |
| 170 | + a[o+3] = byte (v>>24); |
| 171 | + return o+BIT32SZ; |
| 172 | +} |
| 173 | + |
| 174 | +p64(a: array of byte, o: int, b: big): int |
| 175 | +{ |
| 176 | + i := int b; |
| 177 | + a[o] = byte i; |
| 178 | + a[o+1] = byte (i>>8); |
| 179 | + a[o+2] = byte (i>>16); |
| 180 | + a[o+3] = byte (i>>24); |
| 181 | + i = int (b>>32); |
| 182 | + a[o+4] = byte i; |
| 183 | + a[o+5] = byte (i>>8); |
| 184 | + a[o+6] = byte (i>>16); |
| 185 | + a[o+7] = byte (i>>24); |
| 186 | + return o+BIT64SZ; |
| 187 | +} |
| 188 | + |
| 189 | +unpackdir(a: array of byte): (int, Sys->Dir) |
| 190 | +{ |
| 191 | + dir: Sys->Dir; |
| 192 | + |
| 193 | + if(len a < STATFIXLEN) |
| 194 | + return (0, dir); |
| 195 | + # size[2] |
| 196 | + sz := ((int a[1] << 8) | int a[0])+LEN; # bytes this packed dir should occupy |
| 197 | + if(len a < sz) |
| 198 | + return (0, dir); |
| 199 | + # type[2] |
| 200 | + dir.dtype = (int a[3]<<8) | int a[2]; |
| 201 | + # dev[4] |
| 202 | + dir.dev = (((((int a[7] << 8) | int a[6]) << 8) | int a[5]) << 8) | int a[4]; |
| 203 | + # qid.type[1] |
| 204 | + # qid.vers[4] |
| 205 | + # qid.path[8] |
| 206 | + dir.qid = gqid(a, 8); |
| 207 | + # mode[4] |
| 208 | + dir.mode = (((((int a[24] << 8) | int a[23]) << 8) | int a[22]) << 8) | int a[21]; |
| 209 | + # atime[4] |
| 210 | + dir.atime = (((((int a[28] << 8) | int a[27]) << 8) | int a[26]) << 8) | int a[25]; |
| 211 | + # mtime[4] |
| 212 | + dir.mtime = (((((int a[32] << 8) | int a[31]) << 8) | int a[30]) << 8) | int a[29]; |
| 213 | + # length[8] |
| 214 | + v0 := (((((int a[36] << 8) | int a[35]) << 8) | int a[34]) << 8) | int a[33]; |
| 215 | + v1 := (((((int a[40] << 8) | int a[39]) << 8) | int a[38]) << 8) | int a[37]; |
| 216 | + dir.length = (big v1 << 32) | (big v0 & 16rFFFFFFFF); |
| 217 | + # name[s], uid[s], gid[s], muid[s] |
| 218 | + i: int; |
| 219 | + (dir.name, i) = gstring(a, 41); |
| 220 | + (dir.uid, i) = gstring(a, i); |
| 221 | + (dir.gid, i) = gstring(a, i); |
| 222 | + (dir.muid, i) = gstring(a, i); |
| 223 | + if(i != sz) |
| 224 | + return (0, dir); |
| 225 | + return (i, dir); |
| 226 | +} |
| 227 | + |
| 228 | +gqid(f: array of byte, i: int): Sys->Qid |
| 229 | +{ |
| 230 | + qtype := int f[i]; |
| 231 | + vers := (((((int f[i+4] << 8) | int f[i+3]) << 8) | int f[i+2]) << 8) | int f[i+1]; |
| 232 | + i += BIT8SZ+BIT32SZ; |
| 233 | + path0 := (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i]; |
| 234 | + i += BIT32SZ; |
| 235 | + path1 := (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i]; |
| 236 | + path := (big path1 << 32) | (big path0 & 16rFFFFFFFF); |
| 237 | + return (path, vers, qtype); |
| 238 | +} |
| 239 | + |
| 240 | +g32(f: array of byte, i: int): int |
| 241 | +{ |
| 242 | + return (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i]; |
| 243 | +} |
| 244 | + |
| 245 | +g64(f: array of byte, i: int): big |
| 246 | +{ |
| 247 | + b0 := (((((int f[i+3] << 8) | int f[i+2]) << 8) | int f[i+1]) << 8) | int f[i]; |
| 248 | + b1 := (((((int f[i+7] << 8) | int f[i+6]) << 8) | int f[i+5]) << 8) | int f[i+4]; |
| 249 | + return (big b1 << 32) | (big b0 & 16rFFFFFFFF); |
| 250 | +} |
| 251 | + |
| 252 | +gstring(a: array of byte, o: int): (string, int) |
| 253 | +{ |
| 254 | + if(o < 0 || o+STR > len a) |
| 255 | + return (nil, -1); |
| 256 | + l := (int a[o+1] << 8) | int a[o]; |
| 257 | + o += STR; |
| 258 | + e := o+l; |
| 259 | + if(e > len a) |
| 260 | + return (nil, -1); |
| 261 | + return (string a[o:e], e); |
| 262 | +} |
| 263 | + |
| 264 | +ttag2type := array[] of { |
| 265 | +tagof Tmsg.Readerror => 0, |
| 266 | +tagof Tmsg.Version => Tversion, |
| 267 | +tagof Tmsg.Auth => Tauth, |
| 268 | +tagof Tmsg.Attach => Tattach, |
| 269 | +tagof Tmsg.Flush => Tflush, |
| 270 | +tagof Tmsg.Walk => Twalk, |
| 271 | +tagof Tmsg.Open => Topen, |
| 272 | +tagof Tmsg.Create => Tcreate, |
| 273 | +tagof Tmsg.Read => Tread, |
| 274 | +tagof Tmsg.Write => Twrite, |
| 275 | +tagof Tmsg.Clunk => Tclunk, |
| 276 | +tagof Tmsg.Stat => Tstat, |
| 277 | +tagof Tmsg.Remove => Tremove, |
| 278 | +tagof Tmsg.Wstat => Twstat, |
| 279 | +}; |
| 280 | + |
| 281 | +Tmsg.mtype(t: self ref Tmsg): int |
| 282 | +{ |
| 283 | + return ttag2type[tagof t]; |
| 284 | +} |
| 285 | + |
| 286 | +Tmsg.packedsize(t: self ref Tmsg): int |
| 287 | +{ |
| 288 | + mtype := ttag2type[tagof t]; |
| 289 | + if(mtype <= 0) |
| 290 | + return 0; |
| 291 | + ml := hdrlen[mtype]; |
| 292 | + pick m := t { |
| 293 | + Version => |
| 294 | + ml += utflen(m.version); |
| 295 | + Auth => |
| 296 | + ml += utflen(m.uname)+utflen(m.aname); |
| 297 | + Attach => |
| 298 | + ml += utflen(m.uname)+utflen(m.aname); |
| 299 | + Walk => |
| 300 | + for(i:=0; i<len m.names; i++) |
| 301 | + ml += STR+utflen(m.names[i]); |
| 302 | + Create => |
| 303 | + ml += utflen(m.name); |
| 304 | + Write => |
| 305 | + ml += len m.data; |
| 306 | + Wstat => |
| 307 | + ml += packdirsize(m.stat); |
| 308 | + } |
| 309 | + return ml; |
| 310 | +} |
| 311 | + |
| 312 | +Tmsg.pack(t: self ref Tmsg): array of byte |
| 313 | +{ |
| 314 | + if(t == nil) |
| 315 | + return nil; |
| 316 | + ds := t.packedsize(); |
| 317 | + if(ds <= 0) |
| 318 | + return nil; |
| 319 | + d := array[ds] of byte; |
| 320 | + d[0] = byte ds; |
| 321 | + d[1] = byte (ds>>8); |
| 322 | + d[2] = byte (ds>>16); |
| 323 | + d[3] = byte (ds>>24); |
| 324 | + d[4] = byte ttag2type[tagof t]; |
| 325 | + d[5] = byte t.tag; |
| 326 | + d[6] = byte (t.tag >> 8); |
| 327 | + pick m := t { |
| 328 | + Version => |
| 329 | + p32(d, H, m.msize); |
| 330 | + pstring(d, H+COUNT, m.version); |
| 331 | + Auth => |
| 332 | + p32(d, H, m.afid); |
| 333 | + o := pstring(d, H+FID, m.uname); |
| 334 | + pstring(d, o, m.aname); |
| 335 | + Flush => |
| 336 | + v := m.oldtag; |
| 337 | + d[H] = byte v; |
| 338 | + d[H+1] = byte (v>>8); |
| 339 | + Attach => |
| 340 | + p32(d, H, m.fid); |
| 341 | + p32(d, H+FID, m.afid); |
| 342 | + o := pstring(d, H+2*FID, m.uname); |
| 343 | + pstring(d, o, m.aname); |
| 344 | + Walk => |
| 345 | + d[H] = byte m.fid; |
| 346 | + d[H+1] = byte (m.fid>>8); |
| 347 | + d[H+2] = byte (m.fid>>16); |
| 348 | + d[H+3] = byte (m.fid>>24); |
| 349 | + d[H+FID] = byte m.newfid; |
| 350 | + d[H+FID+1] = byte (m.newfid>>8); |
| 351 | + d[H+FID+2] = byte (m.newfid>>16); |
| 352 | + d[H+FID+3] = byte (m.newfid>>24); |
| 353 | + n := len m.names; |
| 354 | + d[H+2*FID] = byte n; |
| 355 | + d[H+2*FID+1] = byte (n>>8); |
| 356 | + o := H+2*FID+LEN; |
| 357 | + for(i := 0; i < n; i++) |
| 358 | + o = pstring(d, o, m.names[i]); |
| 359 | + Open => |
| 360 | + p32(d, H, m.fid); |
| 361 | + d[H+FID] = byte m.mode; |
| 362 | + Create => |
| 363 | + p32(d, H, m.fid); |
| 364 | + o := pstring(d, H+FID, m.name); |
| 365 | + p32(d, o, m.perm); |
| 366 | + d[o+BIT32SZ] = byte m.mode; |
| 367 | + Read => |
| 368 | + p32(d, H, m.fid); |
| 369 | + p64(d, H+FID, m.offset); |
| 370 | + p32(d, H+FID+OFFSET, m.count); |
| 371 | + Write => |
| 372 | + p32(d, H, m.fid); |
| 373 | + p64(d, H+FID, m.offset); |
| 374 | + n := len m.data; |
| 375 | + p32(d, H+FID+OFFSET, n); |
| 376 | + d[H+FID+OFFSET+COUNT:] = m.data; |
| 377 | + Clunk or Remove or Stat => |
| 378 | + p32(d, H, m.fid); |
| 379 | + Wstat => |
| 380 | + p32(d, H, m.fid); |
| 381 | + stat := packdir(m.stat); |
| 382 | + n := len stat; |
| 383 | + d[H+FID] = byte n; |
| 384 | + d[H+FID+1] = byte (n>>8); |
| 385 | + d[H+FID+LEN:] = stat; |
| 386 | + * => |
| 387 | + raise sys->sprint("assertion: Ninep->Tmsg.pack: bad tag: %d", tagof t); |
| 388 | + } |
| 389 | + return d; |
| 390 | +} |
| 391 | + |
| 392 | +Tmsg.unpack(f: array of byte): (int, ref Tmsg) |
| 393 | +{ |
| 394 | + if(len f < H) |
| 395 | + return (0, nil); |
| 396 | + size := (int f[1] << 8) | int f[0]; |
| 397 | + size |= ((int f[3] << 8) | int f[2]) << 16; |
| 398 | + if(len f != size){ |
| 399 | + if(len f < size) |
| 400 | + return (0, nil); # need more data |
| 401 | + f = f[0:size]; # trim to exact length |
| 402 | + } |
| 403 | + mtype := int f[4]; |
| 404 | + if(mtype >= len hdrlen || (mtype&1) != 0 || size < hdrlen[mtype]) |
| 405 | + return (-1, nil); |
| 406 | + |
| 407 | + tag := (int f[6] << 8) | int f[5]; |
| 408 | + fid := 0; |
| 409 | + if(hdrlen[mtype] >= H+FID) |
| 410 | + fid = g32(f, H); # fid is always in same place: extract it once for all if there |
| 411 | + |
| 412 | + # return out of each case body for a legal message; |
| 413 | + # break out of the case for an illegal one |
| 414 | + |
| 415 | +Decode: |
| 416 | + case mtype { |
| 417 | + * => |
| 418 | + sys->print("styx: Tmsg.unpack: bad type %d\n", mtype); |
| 419 | + Tversion => |
| 420 | + msize := fid; |
| 421 | + (version, o) := gstring(f, H+COUNT); |
| 422 | + if(o <= 0) |
| 423 | + break; |
| 424 | + return (o, ref Tmsg.Version(tag, msize, version)); |
| 425 | + Tauth => |
| 426 | + (uname, o1) := gstring(f, H+FID); |
| 427 | + (aname, o2) := gstring(f, o1); |
| 428 | + if(o2 <= 0) |
| 429 | + break; |
| 430 | + return (o2, ref Tmsg.Auth(tag, fid, uname, aname)); |
| 431 | + Tflush => |
| 432 | + oldtag := (int f[H+1] << 8) | int f[H]; |
| 433 | + return (H+TAG, ref Tmsg.Flush(tag, oldtag)); |
| 434 | + Tattach => |
| 435 | + afid := g32(f, H+FID); |
| 436 | + (uname, o1) := gstring(f, H+2*FID); |
| 437 | + (aname, o2) := gstring(f, o1); |
| 438 | + if(o2 <= 0) |
| 439 | + break; |
| 440 | + return (o2, ref Tmsg.Attach(tag, fid, afid, uname, aname)); |
| 441 | + Twalk => |
| 442 | + newfid := g32(f, H+FID); |
| 443 | + n := (int f[H+2*FID+1] << 8) | int f[H+2*FID]; |
| 444 | + if(n > MAXWELEM) |
| 445 | + break; |
| 446 | + o := H+2*FID+LEN; |
| 447 | + names: array of string = nil; |
| 448 | + if(n > 0){ |
| 449 | + names = array[n] of string; |
| 450 | + for(i:=0; i<n; i++){ |
| 451 | + (names[i], o) = gstring(f, o); |
| 452 | + if(o <= 0) |
| 453 | + break Decode; |
| 454 | + } |
| 455 | + } |
| 456 | + return (o, ref Tmsg.Walk(tag, fid, newfid, names)); |
| 457 | + Topen => |
| 458 | + return (H+FID+BIT8SZ, ref Tmsg.Open(tag, fid, int f[H+FID])); |
| 459 | + Tcreate => |
| 460 | + (name, o) := gstring(f, H+FID); |
| 461 | + if(o <= 0 || o+BIT32SZ+BIT8SZ > len f) |
| 462 | + break; |
| 463 | + perm := g32(f, o); |
| 464 | + o += BIT32SZ; |
| 465 | + mode := int f[o++]; |
| 466 | + return (o, ref Tmsg.Create(tag, fid, name, perm, mode)); |
| 467 | + Tread => |
| 468 | + offset := g64(f, H+FID); |
| 469 | + count := g32(f, H+FID+OFFSET); |
| 470 | + return (H+FID+OFFSET+COUNT, ref Tmsg.Read(tag, fid, offset, count)); |
| 471 | + Twrite => |
| 472 | + offset := g64(f, H+FID); |
| 473 | + count := g32(f, H+FID+OFFSET); |
| 474 | + O: con H+FID+OFFSET+COUNT; |
| 475 | + if(count > len f-O) |
| 476 | + break; |
| 477 | + data := f[O:O+count]; |
| 478 | + return (O+count, ref Tmsg.Write(tag, fid, offset, data)); |
| 479 | + Tclunk => |
| 480 | + return (H+FID, ref Tmsg.Clunk(tag, fid)); |
| 481 | + Tremove => |
| 482 | + return (H+FID, ref Tmsg.Remove(tag, fid)); |
| 483 | + Tstat => |
| 484 | + return (H+FID, ref Tmsg.Stat(tag, fid)); |
| 485 | + Twstat => |
| 486 | + n := int (f[H+FID+1]<<8) | int f[H+FID]; |
| 487 | + if(len f < H+FID+LEN+n) |
| 488 | + break; |
| 489 | + (ds, stat) := unpackdir(f[H+FID+LEN:]); |
| 490 | + if(ds != n){ |
| 491 | + sys->print("Ninep->Tmsg.unpack: wstat count: %d/%d\n", ds, n); # temporary |
| 492 | + break; |
| 493 | + } |
| 494 | + return (H+FID+LEN+n, ref Tmsg.Wstat(tag, fid, stat)); |
| 495 | + } |
| 496 | + return (-1, nil); # illegal |
| 497 | +} |
| 498 | + |
| 499 | +tmsgname := array[] of { |
| 500 | +tagof Tmsg.Readerror => "Readerror", |
| 501 | +tagof Tmsg.Version => "Version", |
| 502 | +tagof Tmsg.Auth => "Auth", |
| 503 | +tagof Tmsg.Attach => "Attach", |
| 504 | +tagof Tmsg.Flush => "Flush", |
| 505 | +tagof Tmsg.Walk => "Walk", |
| 506 | +tagof Tmsg.Open => "Open", |
| 507 | +tagof Tmsg.Create => "Create", |
| 508 | +tagof Tmsg.Read => "Read", |
| 509 | +tagof Tmsg.Write => "Write", |
| 510 | +tagof Tmsg.Clunk => "Clunk", |
| 511 | +tagof Tmsg.Stat => "Stat", |
| 512 | +tagof Tmsg.Remove => "Remove", |
| 513 | +tagof Tmsg.Wstat => "Wstat", |
| 514 | +}; |
| 515 | + |
| 516 | +Tmsg.text(t: self ref Tmsg): string |
| 517 | +{ |
| 518 | + if(t == nil) |
| 519 | + return "nil"; |
| 520 | + s := sys->sprint("Tmsg.%s(%ud", tmsgname[tagof t], t.tag); |
| 521 | + pick m:= t { |
| 522 | + * => |
| 523 | + return s + ",ILLEGAL)"; |
| 524 | + Readerror => |
| 525 | + return s + sys->sprint(",\"%s\")", m.error); |
| 526 | + Version => |
| 527 | + return s + sys->sprint(",%d,\"%s\")", m.msize, m.version); |
| 528 | + Auth => |
| 529 | + return s + sys->sprint(",%ud,\"%s\",\"%s\")", m.afid, m.uname, m.aname); |
| 530 | + Flush => |
| 531 | + return s + sys->sprint(",%ud)", m.oldtag); |
| 532 | + Attach => |
| 533 | + return s + sys->sprint(",%ud,%ud,\"%s\",\"%s\")", m.fid, m.afid, m.uname, m.aname); |
| 534 | + Walk => |
| 535 | + s += sys->sprint(",%ud,%ud", m.fid, m.newfid); |
| 536 | + if(len m.names != 0){ |
| 537 | + s += ",array[] of {"; |
| 538 | + for(i := 0; i < len m.names; i++){ |
| 539 | + c := ","; |
| 540 | + if(i == 0) |
| 541 | + c = ""; |
| 542 | + s += sys->sprint("%s\"%s\"", c, m.names[i]); |
| 543 | + } |
| 544 | + s += "}"; |
| 545 | + }else |
| 546 | + s += ",nil"; |
| 547 | + return s + ")"; |
| 548 | + Open => |
| 549 | + return s + sys->sprint(",%ud,%d)", m.fid, m.mode); |
| 550 | + Create => |
| 551 | + return s + sys->sprint(",%ud,\"%s\",8r%uo,%d)", m.fid, m.name, m.perm, m.mode); |
| 552 | + Read => |
| 553 | + return s + sys->sprint(",%ud,%bd,%ud)", m.fid, m.offset, m.count); |
| 554 | + Write => |
| 555 | + return s + sys->sprint(",%ud,%bd,array[%d] of byte)", m.fid, m.offset, len m.data); |
| 556 | + Clunk or |
| 557 | + Remove or |
| 558 | + Stat => |
| 559 | + return s + sys->sprint(",%ud)", m.fid); |
| 560 | + Wstat => |
| 561 | + return s + sys->sprint(",%ud,%s)", m.fid, dir2text(m.stat)); |
| 562 | + } |
| 563 | +} |
| 564 | + |
| 565 | +Tmsg.read(fd: ref Sys->FD, msglim: int): ref Tmsg |
| 566 | +{ |
| 567 | + (msg, err) := readmsg(fd, msglim); |
| 568 | + if(err != nil) |
| 569 | + return ref Tmsg.Readerror(0, err); |
| 570 | + if(msg == nil) |
| 571 | + return nil; |
| 572 | + (nil, m) := Tmsg.unpack(msg); |
| 573 | + if(m == nil) |
| 574 | + return ref Tmsg.Readerror(0, "bad 9P T-message format"); |
| 575 | + return m; |
| 576 | +} |
| 577 | + |
| 578 | +rtag2type := array[] of { |
| 579 | +tagof Rmsg.Version => Rversion, |
| 580 | +tagof Rmsg.Auth => Rauth, |
| 581 | +tagof Rmsg.Error => Rerror, |
| 582 | +tagof Rmsg.Flush => Rflush, |
| 583 | +tagof Rmsg.Attach => Rattach, |
| 584 | +tagof Rmsg.Walk => Rwalk, |
| 585 | +tagof Rmsg.Open => Ropen, |
| 586 | +tagof Rmsg.Create => Rcreate, |
| 587 | +tagof Rmsg.Read => Rread, |
| 588 | +tagof Rmsg.Write => Rwrite, |
| 589 | +tagof Rmsg.Clunk => Rclunk, |
| 590 | +tagof Rmsg.Remove => Rremove, |
| 591 | +tagof Rmsg.Stat => Rstat, |
| 592 | +tagof Rmsg.Wstat => Rwstat, |
| 593 | +}; |
| 594 | + |
| 595 | +Rmsg.mtype(r: self ref Rmsg): int |
| 596 | +{ |
| 597 | + return rtag2type[tagof r]; |
| 598 | +} |
| 599 | + |
| 600 | +Rmsg.packedsize(r: self ref Rmsg): int |
| 601 | +{ |
| 602 | + mtype := rtag2type[tagof r]; |
| 603 | + if(mtype <= 0) |
| 604 | + return 0; |
| 605 | + ml := hdrlen[mtype]; |
| 606 | + pick m := r { |
| 607 | + Version => |
| 608 | + ml += utflen(m.version); |
| 609 | + Error => |
| 610 | + ml += utflen(m.ename); |
| 611 | + Walk => |
| 612 | + ml += QID*len m.qids; |
| 613 | + Read => |
| 614 | + ml += len m.data; |
| 615 | + Stat => |
| 616 | + ml += packdirsize(m.stat); |
| 617 | + } |
| 618 | + return ml; |
| 619 | +} |
| 620 | + |
| 621 | +Rmsg.pack(r: self ref Rmsg): array of byte |
| 622 | +{ |
| 623 | + if(r == nil) |
| 624 | + return nil; |
| 625 | + ps := r.packedsize(); |
| 626 | + if(ps <= 0) |
| 627 | + return nil; |
| 628 | + d := array[ps] of byte; |
| 629 | + d[0] = byte ps; |
| 630 | + d[1] = byte (ps>>8); |
| 631 | + d[2] = byte (ps>>16); |
| 632 | + d[3] = byte (ps>>24); |
| 633 | + d[4] = byte rtag2type[tagof r]; |
| 634 | + d[5] = byte r.tag; |
| 635 | + d[6] = byte (r.tag >> 8); |
| 636 | + pick m := r { |
| 637 | + Version => |
| 638 | + p32(d, H, m.msize); |
| 639 | + pstring(d, H+BIT32SZ, m.version); |
| 640 | + Auth => |
| 641 | + pqid(d, H, m.aqid); |
| 642 | + Flush or |
| 643 | + Clunk or |
| 644 | + Remove or |
| 645 | + Wstat => |
| 646 | + ; # nothing more required |
| 647 | + Error => |
| 648 | + pstring(d, H, m.ename); |
| 649 | + Attach => |
| 650 | + pqid(d, H, m.qid); |
| 651 | + Walk => |
| 652 | + n := len m.qids; |
| 653 | + d[H] = byte n; |
| 654 | + d[H+1] = byte (n>>8); |
| 655 | + o := H+LEN; |
| 656 | + for(i:=0; i<n; i++){ |
| 657 | + pqid(d, o, m.qids[i]); |
| 658 | + o += QID; |
| 659 | + } |
| 660 | + Create or |
| 661 | + Open => |
| 662 | + pqid(d, H, m.qid); |
| 663 | + p32(d, H+QID, m.iounit); |
| 664 | + Read => |
| 665 | + v := len m.data; |
| 666 | + d[H] = byte v; |
| 667 | + d[H+1] = byte (v>>8); |
| 668 | + d[H+2] = byte (v>>16); |
| 669 | + d[H+3] = byte (v>>24); |
| 670 | + d[H+4:] = m.data; |
| 671 | + Write => |
| 672 | + v := m.count; |
| 673 | + d[H] = byte v; |
| 674 | + d[H+1] = byte (v>>8); |
| 675 | + d[H+2] = byte (v>>16); |
| 676 | + d[H+3] = byte (v>>24); |
| 677 | + Stat => |
| 678 | + stat := packdir(m.stat); |
| 679 | + v := len stat; |
| 680 | + d[H] = byte v; |
| 681 | + d[H+1] = byte (v>>8); |
| 682 | + d[H+2:] = stat; # should avoid copy? |
| 683 | + * => |
| 684 | + raise sys->sprint("assertion: Ninep->Rmsg.pack: missed case: tag %d", tagof r); |
| 685 | + } |
| 686 | + return d; |
| 687 | +} |
| 688 | + |
| 689 | +Rmsg.unpack(f: array of byte): (int, ref Rmsg) |
| 690 | +{ |
| 691 | + if(len f < H) |
| 692 | + return (0, nil); |
| 693 | + size := (int f[1] << 8) | int f[0]; |
| 694 | + size |= ((int f[3] << 8) | int f[2]) << 16; # size includes itself |
| 695 | + if(len f != size){ |
| 696 | + if(len f < size) |
| 697 | + return (0, nil); # need more data |
| 698 | + f = f[0:size]; # trim to exact length |
| 699 | + } |
| 700 | + mtype := int f[4]; |
| 701 | + if(mtype >= len hdrlen || (mtype&1) == 0 || size < hdrlen[mtype]) |
| 702 | + return (-1, nil); |
| 703 | + |
| 704 | + tag := (int f[6] << 8) | int f[5]; |
| 705 | + |
| 706 | + # return out of each case body for a legal message; |
| 707 | + # break out of the case for an illegal one |
| 708 | + |
| 709 | + case mtype { |
| 710 | + * => |
| 711 | + sys->print("Ninep->Rmsg.unpack: bad type %d\n", mtype); # temporary |
| 712 | + Rversion => |
| 713 | + msize := g32(f, H); |
| 714 | + (version, o) := gstring(f, H+BIT32SZ); |
| 715 | + if(o <= 0) |
| 716 | + break; |
| 717 | + return (o, ref Rmsg.Version(tag, msize, version)); |
| 718 | + Rauth => |
| 719 | + return (H+QID, ref Rmsg.Auth(tag, gqid(f, H))); |
| 720 | + Rflush => |
| 721 | + return (H, ref Rmsg.Flush(tag)); |
| 722 | + Rerror => |
| 723 | + (ename, o) := gstring(f, H); |
| 724 | + if(o <= 0) |
| 725 | + break; |
| 726 | + return (o, ref Rmsg.Error(tag, ename)); |
| 727 | + Rclunk => |
| 728 | + return (H, ref Rmsg.Clunk(tag)); |
| 729 | + Rremove => |
| 730 | + return (H, ref Rmsg.Remove(tag)); |
| 731 | + Rwstat=> |
| 732 | + return (H, ref Rmsg.Wstat(tag)); |
| 733 | + Rattach => |
| 734 | + return (H+QID, ref Rmsg.Attach(tag, gqid(f, H))); |
| 735 | + Rwalk => |
| 736 | + nqid := (int f[H+1] << 8) | int f[H]; |
| 737 | + if(len f < H+LEN+nqid*QID) |
| 738 | + break; |
| 739 | + o := H+LEN; |
| 740 | + qids := array[nqid] of Sys->Qid; |
| 741 | + for(i:=0; i<nqid; i++){ |
| 742 | + qids[i] = gqid(f, o); |
| 743 | + o += QID; |
| 744 | + } |
| 745 | + return (o, ref Rmsg.Walk(tag, qids)); |
| 746 | + Ropen => |
| 747 | + return (H+QID+COUNT, ref Rmsg.Open(tag, gqid(f, H), g32(f, H+QID))); |
| 748 | + Rcreate=> |
| 749 | + return (H+QID+COUNT, ref Rmsg.Create(tag, gqid(f, H), g32(f, H+QID))); |
| 750 | + Rread => |
| 751 | + count := g32(f, H); |
| 752 | + if(len f < H+COUNT+count) |
| 753 | + break; |
| 754 | + data := f[H+COUNT:H+COUNT+count]; |
| 755 | + return (H+COUNT+count, ref Rmsg.Read(tag, data)); |
| 756 | + Rwrite => |
| 757 | + return (H+COUNT, ref Rmsg.Write(tag, g32(f, H))); |
| 758 | + Rstat => |
| 759 | + n := (int f[H+1] << 8) | int f[H]; |
| 760 | + if(len f < H+LEN+n) |
| 761 | + break; |
| 762 | + (ds, d) := unpackdir(f[H+LEN:]); |
| 763 | + if(ds <= 0) |
| 764 | + break; |
| 765 | + if(ds != n){ |
| 766 | + sys->print("Ninep->Rmsg.unpack: stat count: %d/%d\n", ds, n); # temporary |
| 767 | + break; |
| 768 | + } |
| 769 | + return (H+LEN+n, ref Rmsg.Stat(tag, d)); |
| 770 | + } |
| 771 | + return (-1, nil); # illegal |
| 772 | +} |
| 773 | + |
| 774 | +rmsgname := array[] of { |
| 775 | +tagof Rmsg.Version => "Version", |
| 776 | +tagof Rmsg.Auth => "Auth", |
| 777 | +tagof Rmsg.Attach => "Attach", |
| 778 | +tagof Rmsg.Error => "Error", |
| 779 | +tagof Rmsg.Flush => "Flush", |
| 780 | +tagof Rmsg.Walk => "Walk", |
| 781 | +tagof Rmsg.Create => "Create", |
| 782 | +tagof Rmsg.Open => "Open", |
| 783 | +tagof Rmsg.Read => "Read", |
| 784 | +tagof Rmsg.Write => "Write", |
| 785 | +tagof Rmsg.Clunk => "Clunk", |
| 786 | +tagof Rmsg.Remove => "Remove", |
| 787 | +tagof Rmsg.Stat => "Stat", |
| 788 | +tagof Rmsg.Wstat => "Wstat", |
| 789 | +}; |
| 790 | + |
| 791 | +Rmsg.text(r: self ref Rmsg): string |
| 792 | +{ |
| 793 | + if(sys == nil) |
| 794 | + sys = load Sys Sys->PATH; |
| 795 | + if(r == nil) |
| 796 | + return "nil"; |
| 797 | + s := sys->sprint("Rmsg.%s(%ud", rmsgname[tagof r], r.tag); |
| 798 | + pick m := r { |
| 799 | + * => |
| 800 | + return s + "ERROR)"; |
| 801 | + Readerror => |
| 802 | + return s + sys->sprint(",\"%s\")", m.error); |
| 803 | + Version => |
| 804 | + return s + sys->sprint(",%d,\"%s\")", m.msize, m.version); |
| 805 | + Auth => |
| 806 | + return s+sys->sprint(",%s)", qid2text(m.aqid)); |
| 807 | + Error => |
| 808 | + return s+sys->sprint(",\"%s\")", m.ename); |
| 809 | + Flush or |
| 810 | + Clunk or |
| 811 | + Remove or |
| 812 | + Wstat => |
| 813 | + return s+")"; |
| 814 | + Attach => |
| 815 | + return s+sys->sprint(",%s)", qid2text(m.qid)); |
| 816 | + Walk => |
| 817 | + s += ",array[] of {"; |
| 818 | + for(i := 0; i < len m.qids; i++){ |
| 819 | + c := ""; |
| 820 | + if(i != 0) |
| 821 | + c = ","; |
| 822 | + s += sys->sprint("%s%s", c, qid2text(m.qids[i])); |
| 823 | + } |
| 824 | + return s+"})"; |
| 825 | + Create or |
| 826 | + Open => |
| 827 | + return s+sys->sprint(",%s,%d)", qid2text(m.qid), m.iounit); |
| 828 | + Read => |
| 829 | + return s+sys->sprint(",array[%d] of byte)", len m.data); |
| 830 | + Write => |
| 831 | + return s+sys->sprint(",%d)", m.count); |
| 832 | + Stat => |
| 833 | + return s+sys->sprint(",%s)", dir2text(m.stat)); |
| 834 | + } |
| 835 | +} |
| 836 | + |
| 837 | +Rmsg.read(fd: ref Sys->FD, msglim: int): ref Rmsg |
| 838 | +{ |
| 839 | + (msg, err) := readmsg(fd, msglim); |
| 840 | + if(err != nil) |
| 841 | + return ref Rmsg.Readerror(0, err); |
| 842 | + if(msg == nil) |
| 843 | + return nil; |
| 844 | + (nil, m) := Rmsg.unpack(msg); |
| 845 | + if(m == nil) |
| 846 | + return ref Rmsg.Readerror(0, "bad 9P R-message format"); |
| 847 | + return m; |
| 848 | +} |
| 849 | + |
| 850 | +dir2text(d: Sys->Dir): string |
| 851 | +{ |
| 852 | + return sys->sprint("Dir(\"%s\",\"%s\",\"%s\",%s,8r%uo,%d,%d,%bd,16r%ux,%d)", |
| 853 | + d.name, d.uid, d.gid, qid2text(d.qid), d.mode, d.atime, d.mtime, d.length, d.dtype, d.dev); |
| 854 | +} |
| 855 | + |
| 856 | +qid2text(q: Sys->Qid): string |
| 857 | +{ |
| 858 | + return sys->sprint("Qid(16r%ubx,%d,16r%.2ux)", q.path, q.vers, q.qtype); |
| 859 | +} |
| 860 | + |
| 861 | +readmsg(fd: ref Sys->FD, msglim: int): (array of byte, string) |
| 862 | +{ |
| 863 | + if(msglim <= 0) |
| 864 | + msglim = DEFMSIZE; |
| 865 | + sbuf := array[BIT32SZ] of byte; |
| 866 | + if((n := sys->readn(fd, sbuf, BIT32SZ)) != BIT32SZ){ |
| 867 | + if(n == 0) |
| 868 | + return (nil, nil); |
| 869 | + return (nil, sys->sprint("%r")); |
| 870 | + } |
| 871 | + ml := (int sbuf[1] << 8) | int sbuf[0]; |
| 872 | + ml |= ((int sbuf[3] << 8) | int sbuf[2]) << 16; |
| 873 | + if(ml <= BIT32SZ) |
| 874 | + return (nil, "invalid 9P message size"); |
| 875 | + if(ml > msglim) |
| 876 | + return (nil, "9P message longer than agreed"); |
| 877 | + buf := array[ml] of byte; |
| 878 | + buf[0:] = sbuf; |
| 879 | + if((n = sys->readn(fd, buf[BIT32SZ:], ml-BIT32SZ)) != ml-BIT32SZ){ |
| 880 | + if(n == 0) |
| 881 | + return (nil, "9P message truncated"); |
| 882 | + return (nil, sys->sprint("%r")); |
| 883 | + } |
| 884 | + return (buf, nil); |
| 885 | +} |
| 886 | + |
| 887 | +istmsg(f: array of byte): int |
| 888 | +{ |
| 889 | + if(len f < H) |
| 890 | + return -1; |
| 891 | + return (int f[BIT32SZ] & 1) == 0; |
| 892 | +} |
| 893 | + |
| 894 | +compatible(t: ref Tmsg.Version, msize: int, version: string): (int, string) |
| 895 | +{ |
| 896 | + if(version == nil) |
| 897 | + version = VERSION; |
| 898 | + if(t.msize < msize) |
| 899 | + msize = t.msize; |
| 900 | + v := t.version; |
| 901 | + if(len v < 2 || v[0:2] != "9P") |
| 902 | + return (msize, "unknown"); |
| 903 | + for(i:=2; i<len v; i++) |
| 904 | + if((c := v[i]) == '.'){ |
| 905 | + v = v[0:i]; |
| 906 | + break; |
| 907 | + }else if(!(c >= '0' && c <= '9')) |
| 908 | + return (msize, "unknown"); # fussier than Plan 9 |
| 909 | + if(v < VERSION) |
| 910 | + return (msize, "unknown"); |
| 911 | + if(v < version) |
| 912 | + version = v; |
| 913 | + return (msize, version); |
| 914 | +} |