1implement Ninep; 2 3include "sys.m"; 4 sys: Sys; 5 6include "9p.m"; 7 8STR: con BIT16SZ; # string length 9TAG: con BIT16SZ; 10FID: con BIT32SZ; 11QID: con BIT8SZ+BIT32SZ+BIT64SZ; 12LEN: con BIT16SZ; # stat and qid array lengths 13COUNT: con BIT32SZ; 14OFFSET: con BIT64SZ; 15 16H: 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# 21hdrlen := array[Tmax] of 22{ 23Tversion => H+COUNT+STR, # size[4] Tversion tag[2] msize[4] version[s] 24Rversion => H+COUNT+STR, # size[4] Rversion tag[2] msize[4] version[s] 25 26Tauth => H+FID+STR+STR, # size[4] Tauth tag[2] afid[4] uname[s] aname[s] 27Rauth => H+QID, # size[4] Rauth tag[2] aqid[13] 28 29Rerror => H+STR, # size[4] Rerror tag[2] ename[s] 30 31Tflush => H+TAG, # size[4] Tflush tag[2] oldtag[2] 32Rflush => H, # size[4] Rflush tag[2] 33 34Tattach => H+FID+FID+STR+STR, # size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s] 35Rattach => H+QID, # size[4] Rattach tag[2] qid[13] 36 37Twalk => H+FID+FID+LEN, # size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s]) 38Rwalk => H+LEN, # size[4] Rwalk tag[2] nwqid[2] nwqid*(wqid[13]) 39 40Topen => H+FID+BIT8SZ, # size[4] Topen tag[2] fid[4] mode[1] 41Ropen => H+QID+COUNT, # size[4] Ropen tag[2] qid[13] iounit[4] 42 43Tcreate => H+FID+STR+BIT32SZ+BIT8SZ, # size[4] Tcreate tag[2] fid[4] name[s] perm[4] mode[1] 44Rcreate => H+QID+COUNT, # size[4] Rcreate tag[2] qid[13] iounit[4] 45 46Tread => H+FID+OFFSET+COUNT, # size[4] Tread tag[2] fid[4] offset[8] count[4] 47Rread => H+COUNT, # size[4] Rread tag[2] count[4] data[count] 48 49Twrite => H+FID+OFFSET+COUNT, # size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count] 50Rwrite => H+COUNT, # size[4] Rwrite tag[2] count[4] 51 52Tclunk => H+FID, # size[4] Tclunk tag[2] fid[4] 53Rclunk => H, # size[4] Rclunk tag[2] 54 55Tremove => H+FID, # size[4] Tremove tag[2] fid[4] 56Rremove => H, # size[4] Rremove tag[2] 57 58Tstat => H+FID, # size[4] Tstat tag[2] fid[4] 59Rstat => H+LEN, # size[4] Rstat tag[2] stat[n] 60 61Twstat => H+FID+LEN, # size[4] Twstat tag[2] fid[4] stat[n] 62Rwstat => H, # size[4] Rwstat tag[2] 63}; 64 65init() 66{ 67 sys = load Sys Sys->PATH; 68} 69 70utflen(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 83packdirsize(d: Sys->Dir): int 84{ 85 return STATFIXLEN+utflen(d.name)+utflen(d.uid)+utflen(d.gid)+utflen(d.muid); 86} 87 88packdir(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 134pqid(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 155pstring(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 165p32(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 174p64(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 189unpackdir(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 228gqid(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 240g32(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 245g64(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 252gstring(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 264ttag2type := array[] of { 265tagof Tmsg.Readerror => 0, 266tagof Tmsg.Version => Tversion, 267tagof Tmsg.Auth => Tauth, 268tagof Tmsg.Attach => Tattach, 269tagof Tmsg.Flush => Tflush, 270tagof Tmsg.Walk => Twalk, 271tagof Tmsg.Open => Topen, 272tagof Tmsg.Create => Tcreate, 273tagof Tmsg.Read => Tread, 274tagof Tmsg.Write => Twrite, 275tagof Tmsg.Clunk => Tclunk, 276tagof Tmsg.Stat => Tstat, 277tagof Tmsg.Remove => Tremove, 278tagof Tmsg.Wstat => Twstat, 279}; 280 281Tmsg.mtype(t: self ref Tmsg): int 282{ 283 return ttag2type[tagof t]; 284} 285 286Tmsg.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 312Tmsg.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 392Tmsg.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 415Decode: 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 499tmsgname := array[] of { 500tagof Tmsg.Readerror => "Readerror", 501tagof Tmsg.Version => "Version", 502tagof Tmsg.Auth => "Auth", 503tagof Tmsg.Attach => "Attach", 504tagof Tmsg.Flush => "Flush", 505tagof Tmsg.Walk => "Walk", 506tagof Tmsg.Open => "Open", 507tagof Tmsg.Create => "Create", 508tagof Tmsg.Read => "Read", 509tagof Tmsg.Write => "Write", 510tagof Tmsg.Clunk => "Clunk", 511tagof Tmsg.Stat => "Stat", 512tagof Tmsg.Remove => "Remove", 513tagof Tmsg.Wstat => "Wstat", 514}; 515 516Tmsg.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 565Tmsg.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 578rtag2type := array[] of { 579tagof Rmsg.Version => Rversion, 580tagof Rmsg.Auth => Rauth, 581tagof Rmsg.Error => Rerror, 582tagof Rmsg.Flush => Rflush, 583tagof Rmsg.Attach => Rattach, 584tagof Rmsg.Walk => Rwalk, 585tagof Rmsg.Open => Ropen, 586tagof Rmsg.Create => Rcreate, 587tagof Rmsg.Read => Rread, 588tagof Rmsg.Write => Rwrite, 589tagof Rmsg.Clunk => Rclunk, 590tagof Rmsg.Remove => Rremove, 591tagof Rmsg.Stat => Rstat, 592tagof Rmsg.Wstat => Rwstat, 593}; 594 595Rmsg.mtype(r: self ref Rmsg): int 596{ 597 return rtag2type[tagof r]; 598} 599 600Rmsg.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 621Rmsg.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 689Rmsg.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 774rmsgname := array[] of { 775tagof Rmsg.Version => "Version", 776tagof Rmsg.Auth => "Auth", 777tagof Rmsg.Attach => "Attach", 778tagof Rmsg.Error => "Error", 779tagof Rmsg.Flush => "Flush", 780tagof Rmsg.Walk => "Walk", 781tagof Rmsg.Create => "Create", 782tagof Rmsg.Open => "Open", 783tagof Rmsg.Read => "Read", 784tagof Rmsg.Write => "Write", 785tagof Rmsg.Clunk => "Clunk", 786tagof Rmsg.Remove => "Remove", 787tagof Rmsg.Stat => "Stat", 788tagof Rmsg.Wstat => "Wstat", 789}; 790 791Rmsg.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 837Rmsg.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 850Rmsg.write(m: self ref Rmsg, fd: ref Sys->FD, msize: int): int 851{ 852 if(msize == 0) 853 m = ref Rmsg.Error(m.tag, "Tversion not seen"); 854 d := m.pack(); 855 if(msize != 0 && len d > msize){ 856 m = ref Rmsg.Error(m.tag, "9P reply didn't fit"); 857 d = m.pack(); 858 } 859 n := len d; 860 if(sys->write(fd, d, n) != n) 861 return -1; 862 return 0; 863} 864 865dir2text(d: Sys->Dir): string 866{ 867 return sys->sprint("Dir(\"%s\",\"%s\",\"%s\",%s,8r%uo,%d,%d,%bd,16r%ux,%d)", 868 d.name, d.uid, d.gid, qid2text(d.qid), d.mode, d.atime, d.mtime, d.length, d.dtype, d.dev); 869} 870 871qid2text(q: Sys->Qid): string 872{ 873 return sys->sprint("Qid(16r%ubx,%d,16r%.2ux)", q.path, q.vers, q.qtype); 874} 875 876readmsg(fd: ref Sys->FD, msglim: int): (array of byte, string) 877{ 878 if(msglim <= 0) 879 msglim = DEFMSIZE; 880 sbuf := array[BIT32SZ] of byte; 881 if((n := sys->readn(fd, sbuf, BIT32SZ)) != BIT32SZ){ 882 if(n == 0) 883 return (nil, nil); 884 return (nil, sys->sprint("%r")); 885 } 886 ml := (int sbuf[1] << 8) | int sbuf[0]; 887 ml |= ((int sbuf[3] << 8) | int sbuf[2]) << 16; 888 if(ml <= BIT32SZ) 889 return (nil, "invalid 9P message size"); 890 if(ml > msglim) 891 return (nil, "9P message longer than agreed"); 892 buf := array[ml] of byte; 893 buf[0:] = sbuf; 894 if((n = sys->readn(fd, buf[BIT32SZ:], ml-BIT32SZ)) != ml-BIT32SZ){ 895 if(n == 0) 896 return (nil, "9P message truncated"); 897 return (nil, sys->sprint("%r")); 898 } 899 return (buf, nil); 900} 901 902istmsg(f: array of byte): int 903{ 904 if(len f < H) 905 return -1; 906 return (int f[BIT32SZ] & 1) == 0; 907} 908 909compatible(t: ref Tmsg.Version, msize: int, version: string): (int, string) 910{ 911 if(version == nil) 912 version = VERSION; 913 if(t.msize < msize) 914 msize = t.msize; 915 v := t.version; 916 if(len v < 2 || v[0:2] != "9P") 917 return (msize, "unknown"); 918 for(i:=2; i<len v; i++) 919 if((c := v[i]) == '.'){ 920 v = v[0:i]; 921 break; 922 }else if(!(c >= '0' && c <= '9')) 923 return (msize, "unknown"); # fussier than Plan 9 924 if(v < VERSION) 925 return (msize, "unknown"); 926 if(v < version) 927 version = v; 928 return (msize, version); 929} 930