1implement ISO9660; 2 3include "sys.m"; 4 sys: Sys; 5 Dir, Qid, QTDIR, QTFILE, DMDIR: import sys; 6 7include "draw.m"; 8 9include "daytime.m"; 10 daytime: Daytime; 11 12include "string.m"; 13 str: String; 14 15include "styx.m"; 16 styx: Styx; 17 Rmsg, Tmsg: import styx; 18 19include "arg.m"; 20 21ISO9660: module 22{ 23 init: fn(nil: ref Draw->Context, nil: list of string); 24}; 25 26Sectorsize: con 2048; 27Maxname: con 256; 28 29Enonexist: con "file does not exist"; 30Eperm: con "permission denied"; 31Enofile: con "no file system specified"; 32Eauth: con "authentication failed"; 33Ebadfid: con "invalid fid"; 34Efidinuse: con "fid already in use"; 35Enotdir: con "not a directory"; 36Esyntax: con "file name syntax"; 37 38devname: string; 39 40chatty := 0; 41showstyx := 0; 42progname := "9660srv"; 43stderr: ref Sys->FD; 44noplan9 := 0; 45nojoliet := 0; 46norock := 0; 47 48usage() 49{ 50 sys->fprint(sys->fildes(2), "usage: %s [-rabc] [-9JR] [-s] cd_device dir\n", progname); 51 raise "fail:usage"; 52} 53 54init(nil: ref Draw->Context, args: list of string) 55{ 56 sys = load Sys Sys->PATH; 57 58 sys->pctl(Sys->FORKFD|Sys->NEWPGRP, nil); 59 stderr = sys->fildes(2); 60 61 if(args != nil) 62 progname = hd args; 63 styx = load Styx Styx->PATH; 64 if(styx == nil) 65 noload(Styx->PATH); 66 styx->init(); 67 68 if(args != nil) 69 progname = hd args; 70 mountopt := Sys->MREPL; 71 copt := 0; 72 stdio := 0; 73 74 arg := load Arg Arg->PATH; 75 if(arg == nil) 76 noload(Arg->PATH); 77 arg->init(args); 78 while((c := arg->opt()) != 0) 79 case c { 80 'v' or 'D' => chatty = 1; showstyx = 1; 81 'r' => mountopt = Sys->MREPL; 82 'a' => mountopt = Sys->MAFTER; 83 'b' => mountopt = Sys->MBEFORE; 84 'c' => copt = Sys->MCREATE; 85 's' => stdio = 1; 86 '9' => noplan9 = 1; 87 'J' => nojoliet = 1; 88 'R' => norock = 1; 89 * => usage(); 90 } 91 args = arg->argv(); 92 arg = nil; 93 94 if(args == nil || tl args == nil) 95 usage(); 96 what := hd args; 97 mountpt := hd tl args; 98 99 daytime = load Daytime Daytime->PATH; 100 if(daytime == nil) 101 noload(Daytime->PATH); 102 103 iobufinit(Sectorsize); 104 105 pip := array[2] of ref Sys->FD; 106 if(stdio){ 107 pip[0] = sys->fildes(0); 108 pip[1] = sys->fildes(1); 109 }else 110 if(sys->pipe(pip) < 0) 111 error(sys->sprint("can't create pipe: %r")); 112 113 devname = what; 114 115 sync := chan of int; 116 spawn fileserve(pip[1], sync); 117 <-sync; 118 119 if(sys->mount(pip[0], nil, mountpt, mountopt|copt, nil) < 0) { 120 sys->fprint(sys->fildes(2), "%s: mount %s %s failed: %r\n", progname, what, mountpt); 121 exit; 122 } 123} 124 125noload(s: string) 126{ 127 sys->fprint(sys->fildes(2), "%s: can't load %s: %r\n", progname, s); 128 raise "fail:load"; 129} 130 131error(p: string) 132{ 133 sys->fprint(sys->fildes(2), "9660srv: %s\n", p); 134 raise "fail:error"; 135} 136 137fileserve(rfd: ref Sys->FD, sync: chan of int) 138{ 139 sys->pctl(Sys->NEWFD|Sys->FORKNS, list of {2, rfd.fd}); 140 rfd = sys->fildes(rfd.fd); 141 stderr = sys->fildes(2); 142 sync <-= 1; 143 while((m := Tmsg.read(rfd, 0)) != nil){ 144 if(showstyx) 145 chat(sys->sprint("%s...", m.text())); 146 r: ref Rmsg; 147 pick t := m { 148 Readerror => 149 error(sys->sprint("mount read error: %s", t.error)); 150 Version => 151 r = rversion(t); 152 Auth => 153 r = rauth(t); 154 Flush => 155 r = rflush(t); 156 Attach => 157 r = rattach(t); 158 Walk => 159 r = rwalk(t); 160 Open => 161 r = ropen(t); 162 Create => 163 r = rcreate(t); 164 Read => 165 r = rread(t); 166 Write => 167 r = rwrite(t); 168 Clunk => 169 r = rclunk(t); 170 Remove => 171 r = rremove(t); 172 Stat => 173 r = rstat(t); 174 Wstat => 175 r = rwstat(t); 176 * => 177 error(sys->sprint("invalid T-message tag: %d", tagof m)); 178 } 179 pick e := r { 180 Error => 181 r.tag = m.tag; 182 } 183 rbuf := r.pack(); 184 if(rbuf == nil) 185 error("bad R-message conversion"); 186 if(showstyx) 187 chat(r.text()+"\n"); 188 if(sys->write(rfd, rbuf, len rbuf) != len rbuf) 189 error(sys->sprint("connection write error: %r")); 190 } 191 192 if(chatty) 193 chat("server end of file\n"); 194} 195 196E(s: string): ref Rmsg.Error 197{ 198 return ref Rmsg.Error(0, s); 199} 200 201rversion(t: ref Tmsg.Version): ref Rmsg 202{ 203 (msize, version) := styx->compatible(t, Styx->MAXRPC, Styx->VERSION); 204 return ref Rmsg.Version(t.tag, msize, version); 205} 206 207rauth(t: ref Tmsg.Auth): ref Rmsg 208{ 209 return ref Rmsg.Error(t.tag, "authentication not required"); 210} 211 212rflush(t: ref Tmsg.Flush): ref Rmsg 213{ 214 return ref Rmsg.Flush(t.tag); 215} 216 217rattach(t: ref Tmsg.Attach): ref Rmsg 218{ 219 dname := devname; 220 if(t.aname != "") 221 dname = t.aname; 222 (dev, err) := devattach(dname, Sys->OREAD, Sectorsize); 223 if(dev == nil) 224 return E(err); 225 226 xf := Xfs.new(dev); 227 root := cleanfid(t.fid); 228 root.qid = Sys->Qid(big 0, 0, Sys->QTDIR); 229 root.xf = xf; 230 err = root.attach(); 231 if(err != nil){ 232 clunkfid(t.fid); 233 return E(err); 234 } 235 xf.rootqid = root.qid; 236 return ref Rmsg.Attach(t.tag, root.qid); 237} 238 239walk1(f: ref Xfile, name: string): string 240{ 241 if(!(f.qid.qtype & Sys->QTDIR)) 242 return Enotdir; 243 case name { 244 "." => 245 return nil; # nop, but shouldn't happen 246 ".." => 247 if(f.qid.path==f.xf.rootqid.path) 248 return nil; 249 return f.walkup(); 250 * => 251 return f.walk(name); 252 } 253} 254 255rwalk(t: ref Tmsg.Walk): ref Rmsg 256{ 257 f:=findfid(t.fid); 258 if(f == nil) 259 return E(Ebadfid); 260 nf, sf: ref Xfile; 261 if(t.newfid != t.fid){ 262 nf = cleanfid(t.newfid); 263 if(nf == nil) 264 return E(Efidinuse); 265 f.clone(nf); 266 f = nf; 267 }else 268 sf = f.save(); 269 270 qids: array of Sys->Qid; 271 if(len t.names > 0){ 272 qids = array[len t.names] of Sys->Qid; 273 for(i := 0; i < len t.names; i++){ 274 e := walk1(f, t.names[i]); 275 if(e != nil){ 276 if(nf != nil){ 277 nf.clunk(); 278 clunkfid(t.newfid); 279 }else 280 f.restore(sf); 281 if(i == 0) 282 return E(e); 283 return ref Rmsg.Walk(t.tag, qids[0:i]); 284 } 285 qids[i] = f.qid; 286 } 287 } 288 return ref Rmsg.Walk(t.tag, qids); 289} 290 291ropen(t: ref Tmsg.Open): ref Rmsg 292{ 293 f := findfid(t.fid); 294 if(f == nil) 295 return E(Ebadfid); 296 if(f.flags&Omodes) 297 return E("open on open file"); 298 e := f.open(t.mode); 299 if(e != nil) 300 return E(e); 301 f.flags = openflags(t.mode); 302 return ref Rmsg.Open(t.tag, f.qid, Styx->MAXFDATA); 303} 304 305rcreate(t: ref Tmsg.Create): ref Rmsg 306{ 307 name := t.name; 308 if(name == "." || name == "..") 309 return E(Esyntax); 310 f := findfid(t.fid); 311 if(f == nil) 312 return E(Ebadfid); 313 if(f.flags&Omodes) 314 return E("create on open file"); 315 if(!(f.qid.qtype&Sys->QTDIR)) 316 return E("create in non-directory"); 317 e := f.create(name, t.perm, t.mode); 318 if(e != nil) 319 return E(e); 320 f.flags = openflags(t.mode); 321 return ref Rmsg.Create(t.tag, f.qid, Styx->MAXFDATA); 322} 323 324rread(t: ref Tmsg.Read): ref Rmsg 325{ 326 err: string; 327 328 f := findfid(t.fid); 329 if(f == nil) 330 return E(Ebadfid); 331 if (!(f.flags&Oread)) 332 return E("file not opened for reading"); 333 if(t.count < 0 || t.offset < big 0) 334 return E("negative offset or count"); 335 b := array[Styx->MAXFDATA] of byte; 336 count: int; 337 if(f.qid.qtype & Sys->QTDIR) 338 (count, err) = f.readdir(b, int t.offset, t.count); 339 else 340 (count, err) = f.read(b, int t.offset, t.count); 341 if(err != nil) 342 return E(err); 343 if(count != len b) 344 b = b[0:count]; 345 return ref Rmsg.Read(t.tag, b); 346} 347 348rwrite(nil: ref Tmsg.Write): ref Rmsg 349{ 350 return E(Eperm); 351} 352 353rclunk(t: ref Tmsg.Clunk): ref Rmsg 354{ 355 f := findfid(t.fid); 356 if(f == nil) 357 return E(Ebadfid); 358 f.clunk(); 359 clunkfid(t.fid); 360 return ref Rmsg.Clunk(t.tag); 361} 362 363rremove(t: ref Tmsg.Remove): ref Rmsg 364{ 365 f := findfid(t.fid); 366 if(f == nil) 367 return E(Ebadfid); 368 f.clunk(); 369 clunkfid(t.fid); 370 return E(Eperm); 371} 372 373rstat(t: ref Tmsg.Stat): ref Rmsg 374{ 375 f := findfid(t.fid); 376 if(f == nil) 377 return E(Ebadfid); 378 (dir, nil) := f.stat(); 379 return ref Rmsg.Stat(t.tag, *dir); 380} 381 382rwstat(nil: ref Tmsg.Wstat): ref Rmsg 383{ 384 return E(Eperm); 385} 386 387openflags(mode: int): int 388{ 389 flags := 0; 390 case mode & ~(Sys->OTRUNC|Sys->ORCLOSE) { 391 Sys->OREAD => 392 flags = Oread; 393 Sys->OWRITE => 394 flags = Owrite; 395 Sys->ORDWR => 396 flags = Oread|Owrite; 397 } 398 if(mode & Sys->ORCLOSE) 399 flags |= Orclose; 400 return flags; 401} 402 403chat(s: string) 404{ 405 if(chatty) 406 sys->fprint(stderr, "%s", s); 407} 408 409Fid: adt { 410 fid: int; 411 file: ref Xfile; 412}; 413 414FIDMOD: con 127; # prime 415fids := array[FIDMOD] of list of ref Fid; 416 417hashfid(fid: int): (ref Fid, array of list of ref Fid) 418{ 419 nl: list of ref Fid; 420 421 hp := fids[fid%FIDMOD:]; 422 nl = nil; 423 for(l := hp[0]; l != nil; l = tl l){ 424 f := hd l; 425 if(f.fid == fid){ 426 l = tl l; # excluding f 427 for(; nl != nil; nl = tl nl) 428 l = (hd nl) :: l; # put examined ones back, in order 429 hp[0] = l; 430 return (f, hp); 431 } else 432 nl = f :: nl; 433 } 434 return (nil, hp); 435} 436 437findfid(fid: int): ref Xfile 438{ 439 (f, hp) := hashfid(fid); 440 if(f == nil){ 441 chat("unassigned fid"); 442 return nil; 443 } 444 hp[0] = f :: hp[0]; 445 return f.file; 446} 447 448cleanfid(fid: int): ref Xfile 449{ 450 (f, hp) := hashfid(fid); 451 if(f != nil){ 452 chat("fid in use"); 453 return nil; 454 } 455 f = ref Fid; 456 f.fid = fid; 457 f.file = Xfile.new(); 458 hp[0] = f :: hp[0]; 459 return f.file.clean(); 460} 461 462clunkfid(fid: int) 463{ 464 (f, nil) := hashfid(fid); 465 if(f != nil) 466 f.file.clean(); 467} 468 469# 470# 471# 472 473Xfs: adt { 474 d: ref Device; 475 inuse: int; 476 issusp: int; # system use sharing protocol in use? 477 suspoff: int; # LEN_SKP, if so 478 isplan9: int; # has Plan 9-specific directory info 479 isrock: int; # is rock ridge 480 rootqid: Sys->Qid; 481 ptr: int; # tag for private data 482 483 new: fn(nil: ref Device): ref Xfs; 484 incref: fn(nil: self ref Xfs); 485 decref: fn(nil: self ref Xfs); 486}; 487 488Xfile: adt { 489 xf: ref Xfs; 490 flags: int; 491 qid: Sys->Qid; 492 ptr: ref Isofile; # tag for private data 493 494 new: fn(): ref Xfile; 495 clean: fn(nil: self ref Xfile): ref Xfile; 496 497 save: fn(nil: self ref Xfile): ref Xfile; 498 restore: fn(nil: self ref Xfile, s: ref Xfile); 499 500 attach: fn(nil: self ref Xfile): string; 501 clone: fn(nil: self ref Xfile, nil: ref Xfile); 502 walkup: fn(nil: self ref Xfile): string; 503 walk: fn(nil: self ref Xfile, nil: string): string; 504 open: fn(nil: self ref Xfile, nil: int): string; 505 create: fn(nil: self ref Xfile, nil: string, nil: int, nil: int): string; 506 readdir: fn(nil: self ref Xfile, nil: array of byte, nil: int, nil: int): (int, string); 507 read: fn(nil: self ref Xfile, nil: array of byte, nil: int, nil: int): (int, string); 508 write: fn(nil: self ref Xfile, nil: array of byte, nil: int, nil: int): (int, string); 509 clunk: fn(nil: self ref Xfile); 510 remove: fn(nil: self ref Xfile): string; 511 stat: fn(nil: self ref Xfile): (ref Sys->Dir, string); 512 wstat: fn(nil: self ref Xfile, nil: ref Sys->Dir): string; 513}; 514 515Oread, Owrite, Orclose: con 1<<iota; 516Omodes: con 3; # mask 517 518VOLDESC: con 16; # sector number 519 520Drec: adt { 521 reclen: int; 522 attrlen: int; 523 addr: int; # should be big? 524 size: int; # should be big? 525 date: array of byte; 526 time: int; 527 tzone: int; # not in high sierra 528 flags: int; 529 unitsize: int; 530 gapsize: int; 531 vseqno: int; 532 name: array of byte; 533 data: array of byte; # system extensions 534}; 535 536Isofile: adt { 537 fmt: int; # 'z' if iso, 'r' if high sierra 538 blksize: int; 539 offset: int; # true offset when reading directory 540 doffset: int; # styx offset when reading directory 541 d: ref Drec; 542}; 543 544Xfile.new(): ref Xfile 545{ 546 f := ref Xfile; 547 return f.clean(); 548} 549 550Xfile.clean(f: self ref Xfile): ref Xfile 551{ 552 if(f.xf != nil){ 553 f.xf.decref(); 554 f.xf = nil; 555 } 556 f.ptr = nil; 557 f.flags = 0; 558 f.qid = Qid(big 0, 0, 0); 559 return f; 560} 561 562Xfile.save(f: self ref Xfile): ref Xfile 563{ 564 s := ref Xfile; 565 *s = *f; 566 s.ptr = ref *f.ptr; 567 s.ptr.d = ref *f.ptr.d; 568 return s; 569} 570 571Xfile.restore(f: self ref Xfile, s: ref Xfile) 572{ 573 f.flags = s.flags; 574 f.qid = s.qid; 575 *f.ptr = *s.ptr; 576} 577 578Xfile.attach(root: self ref Xfile): string 579{ 580 fmt := 0; 581 blksize := 0; 582 haveplan9 := 0; 583 dirp: ref Block; 584 dp := ref Drec; 585 for(a:=VOLDESC;a<VOLDESC+100;a++){ 586 p := Block.get(root.xf.d, a); 587 if(p == nil){ 588 if(dirp != nil) 589 dirp.put(); 590 return "can't read volume descriptor"; 591 } 592 v := p.data; # Voldesc 593 if(eqs(v[0:7], "\u0001CD001\u0001")){ # ISO 594 if(dirp != nil) 595 dirp.put(); 596 dirp = p; 597 fmt = 'z'; 598 convM2Drec(v[156:], dp, 0); # v.z.desc.rootdir 599 blksize = l16(v[128:]); # v.z.desc.blksize 600 if(chatty) 601 chat(sys->sprint("iso, blksize=%d...", blksize)); 602 haveplan9 = eqs(v[8:8+6], "PLAN 9"); # v.z.boot.sysid 603 if(haveplan9){ 604 if(noplan9) { 605 chat("ignoring plan9"); 606 haveplan9 = 0; 607 }else{ 608 fmt = '9'; 609 chat("plan9 iso..."); 610 } 611 } 612 continue; 613 } 614 if(eqs(v[8:8+7], "\u0001CDROM\u0001")){ # high sierra 615 if(dirp != nil) 616 dirp.put(); 617 dirp = p; 618 fmt = 'r'; 619 convM2Drec(v[180:], dp, 1); # v.r.desc.rootdir 620 blksize = l16(v[136:]); # v.r.desc.blksize 621 if(chatty) 622 chat(sys->sprint("high sierra, blksize=%d...", blksize)); 623 continue; 624 } 625 if(haveplan9==0 && !nojoliet && eqs(v[0:7], "\u0002CD001\u0001")){ 626 q := v[88:]; # v.z.desc.escapes 627 if(q[0] == byte 16r25 && q[1] == byte 16r2F && 628 (q[2] == byte 16r40 || q[2] == byte 16r43 || q[2] == byte 16r45)){ # joliet, it appears 629 if(dirp != nil) 630 dirp.put(); 631 dirp = p; 632 fmt = 'J'; 633 convM2Drec(v[156:], dp, 0); # v.z.desc.rootdir 634 if(blksize != l16(v[128:])) # v.z.desc.blksize 635 sys->fprint(stderr, "9660srv: warning: suspicious Joliet block size: %d\n", l16(v[128:])); 636 chat("joliet..."); 637 continue; 638 } 639 }else{ 640 p.put(); 641 if(v[0] == byte 16rFF) 642 break; 643 } 644 } 645 646 if(fmt == 0){ 647 if(dirp != nil) 648 dirp.put(); 649 return "CD format not recognised"; 650 } 651 652 if(chatty) 653 showdrec(stderr, fmt, dp); 654 if(blksize > Sectorsize){ 655 dirp.put(); 656 return "blocksize too big"; 657 } 658 fp := iso(root); 659 root.xf.isplan9 = haveplan9; 660 fp.fmt = fmt; 661 fp.blksize = blksize; 662 fp.offset = 0; 663 fp.doffset = 0; 664 fp.d = dp; 665 root.qid.path = big dp.addr; 666 root.qid.qtype = QTDIR; 667 root.qid.vers = 0; 668 dirp.put(); 669 dp = ref Drec; 670 if(getdrec(root, dp) >= 0){ 671 s := dp.data; 672 n := len s; 673 if(n >= 7 && s[0] == byte 'S' && s[1] == byte 'P' && s[2] == byte 7 && 674 s[3] == byte 1 && s[4] == byte 16rBE && s[5] == byte 16rEF){ 675 root.xf.issusp = 1; 676 root.xf.suspoff = int s[6]; 677 n -= root.xf.suspoff; 678 s = s[root.xf.suspoff:]; 679 while(n >= 4){ 680 l := int s[2]; 681 if(s[0] == byte 'E' && s[1] == byte 'R'){ 682 if(int s[4] == 10 && eqs(s[8:18], "RRIP_1991A")) 683 root.xf.isrock = 1; 684 break; 685 } else if(s[0] == byte 'C' && s[1] == byte 'E' && int s[2] >= 28){ 686 (s, n) = getcontin(root.xf.d, s); 687 continue; 688 } else if(s[0] == byte 'R' && s[1] == byte 'R'){ 689 if(!norock) 690 root.xf.isrock = 1; 691 break; # can skip search for ER 692 } else if(s[0] == byte 'S' && s[1] == byte 'T') 693 break; 694 s = s[l:]; 695 n -= l; 696 } 697 } 698 } 699 if(root.xf.isrock) 700 chat("Rock Ridge..."); 701 fp.offset = 0; 702 fp.doffset = 0; 703 return nil; 704} 705 706Xfile.clone(oldf: self ref Xfile, newf: ref Xfile) 707{ 708 *newf = *oldf; 709 newf.ptr = nil; 710 newf.xf.incref(); 711 ip := iso(oldf); 712 np := iso(newf); 713 *np = *ip; # might not be right; shares ip.d 714} 715 716Xfile.walkup(f: self ref Xfile): string 717{ 718 pf := Xfile.new(); 719 ppf := Xfile.new(); 720 e := walkup(f, pf, ppf); 721 pf.clunk(); 722 ppf.clunk(); 723 return e; 724} 725 726walkup(f, pf, ppf: ref Xfile): string 727{ 728 e := opendotdot(f, pf); 729 if(e != nil) 730 return sys->sprint("can't open pf: %s", e); 731 paddr := iso(pf).d.addr; 732 if(iso(f).d.addr == paddr) 733 return nil; 734 e = opendotdot(pf, ppf); 735 if(e != nil) 736 return sys->sprint("can't open ppf: %s", e); 737 d := ref Drec; 738 while(getdrec(ppf, d) >= 0){ 739 if(d.addr == paddr){ 740 newdrec(f, d); 741 f.qid.path = big paddr; 742 f.qid.qtype = QTDIR; 743 f.qid.vers = 0; 744 return nil; 745 } 746 } 747 return "can't find addr of .."; 748} 749 750Xfile.walk(f: self ref Xfile, name: string): string 751{ 752 ip := iso(f); 753 if(!f.xf.isplan9){ 754 for(i := 0; i < len name; i++) 755 if(name[i] == ';') 756 break; 757 if(i >= Maxname) 758 i = Maxname-1; 759 name = name[0:i]; 760 } 761 if(chatty) 762 chat(sys->sprint("%d \"%s\"...", len name, name)); 763 ip.offset = 0; 764 dir := ref Dir; 765 d := ref Drec; 766 while(getdrec(f, d) >= 0) { 767 dvers := rzdir(f.xf, dir, ip.fmt, d); 768 if(name != dir.name) 769 continue; 770 newdrec(f, d); 771 f.qid.path = dir.qid.path; 772 f.qid.qtype = dir.qid.qtype; 773 f.qid.vers = dir.qid.vers; 774 if(dvers){ 775 # versions ignored 776 } 777 return nil; 778 } 779 return Enonexist; 780} 781 782Xfile.open(f: self ref Xfile, mode: int): string 783{ 784 if(mode != Sys->OREAD) 785 return Eperm; 786 ip := iso(f); 787 ip.offset = 0; 788 ip.doffset = 0; 789 return nil; 790} 791 792Xfile.create(nil: self ref Xfile, nil: string, nil: int, nil: int): string 793{ 794 return Eperm; 795} 796 797Xfile.readdir(f: self ref Xfile, buf: array of byte, offset: int, count: int): (int, string) 798{ 799 ip := iso(f); 800 d := ref Dir; 801 drec := ref Drec; 802 if(offset < ip.doffset){ 803 ip.offset = 0; 804 ip.doffset = 0; 805 } 806 rcnt := 0; 807 while(rcnt < count && getdrec(f, drec) >= 0){ 808 if(len drec.name == 1){ 809 if(drec.name[0] == byte 0) 810 continue; 811 if(drec.name[0] == byte 1) 812 continue; 813 } 814 rzdir(f.xf, d, ip.fmt, drec); 815 d.qid.vers = f.qid.vers; 816 a := styx->packdir(*d); 817 if(ip.doffset < offset){ 818 ip.doffset += len a; 819 continue; 820 } 821 if(rcnt+len a > count) 822 break; 823 buf[rcnt:] = a; # BOTCH: copy 824 rcnt += len a; 825 } 826 ip.doffset += rcnt; 827 return (rcnt, nil); 828} 829 830Xfile.read(f: self ref Xfile, buf: array of byte, offset: int, count: int): (int, string) 831{ 832 ip := iso(f); 833 if(offset >= ip.d.size) 834 return (0, nil); 835 if(offset+count > ip.d.size) 836 count = ip.d.size - offset; 837 addr := (ip.d.addr+ip.d.attrlen)*ip.blksize + offset; 838 o := addr % Sectorsize; 839 addr /= Sectorsize; 840 if(chatty) 841 chat(sys->sprint("d.addr=0x%x, addr=0x%x, o=0x%x...", ip.d.addr, addr, o)); 842 n := Sectorsize - o; 843 rcnt := 0; 844 while(count > 0){ 845 if(n > count) 846 n = count; 847 p := Block.get(f.xf.d, addr); 848 if(p == nil) 849 return (-1, "i/o error"); 850 buf[rcnt:] = p.data[o:o+n]; 851 p.put(); 852 count -= n; 853 rcnt += n; 854 addr++; 855 o = 0; 856 n = Sectorsize; 857 } 858 return (rcnt, nil); 859} 860 861Xfile.write(nil: self ref Xfile, nil: array of byte, nil: int, nil: int): (int, string) 862{ 863 return (-1, Eperm); 864} 865 866Xfile.clunk(f: self ref Xfile) 867{ 868 f.ptr = nil; 869} 870 871Xfile.remove(nil: self ref Xfile): string 872{ 873 return Eperm; 874} 875 876Xfile.stat(f: self ref Xfile): (ref Dir, string) 877{ 878 ip := iso(f); 879 d := ref Dir; 880 rzdir(f.xf, d, ip.fmt, ip.d); 881 d.qid.vers = f.qid.vers; 882 if(d.qid.path==f.xf.rootqid.path){ 883 d.qid.path = big 0; 884 d.qid.qtype = QTDIR; 885 } 886 return (d, nil); 887} 888 889Xfile.wstat(nil: self ref Xfile, nil: ref Dir): string 890{ 891 return Eperm; 892} 893 894Xfs.new(d: ref Device): ref Xfs 895{ 896 xf := ref Xfs; 897 xf.inuse = 1; 898 xf.d = d; 899 xf.isplan9 = 0; 900 xf.issusp = 0; 901 xf.isrock = 0; 902 xf.suspoff = 0; 903 xf.ptr = 0; 904 xf.rootqid = Qid(big 0, 0, QTDIR); 905 return xf; 906} 907 908Xfs.incref(xf: self ref Xfs) 909{ 910 xf.inuse++; 911} 912 913Xfs.decref(xf: self ref Xfs) 914{ 915 xf.inuse--; 916 if(xf.inuse == 0){ 917 if(xf.d != nil) 918 xf.d.detach(); 919 } 920} 921 922showdrec(fd: ref Sys->FD, fmt: int, d: ref Drec) 923{ 924 if(d.reclen == 0) 925 return; 926 sys->fprint(fd, "%d %d %d %d ", 927 d.reclen, d.attrlen, d.addr, d.size); 928 sys->fprint(fd, "%s 0x%2.2x %d %d %d ", 929 rdate(d.date, fmt), d.flags, 930 d.unitsize, d.gapsize, d.vseqno); 931 sys->fprint(fd, "%d %s", len d.name, nstr(d.name)); 932 syslen := len d.data; 933 if(syslen != 0) 934 sys->fprint(fd, " %s", nstr(d.data)); 935 sys->fprint(fd, "\n"); 936} 937 938newdrec(f: ref Xfile, dp: ref Drec) 939{ 940 x := iso(f); 941 n := ref Isofile; 942 n.fmt = x.fmt; 943 n.blksize = x.blksize; 944 n.offset = 0; 945 n.doffset = 0; 946 n.d = dp; 947 f.ptr = n; 948} 949 950getdrec(f: ref Xfile, d: ref Drec): int 951{ 952 if(f.ptr == nil) 953 return -1; 954 boff := 0; 955 ip := iso(f); 956 size := ip.d.size; 957 while(ip.offset<size){ 958 addr := (ip.d.addr+ip.d.attrlen)*ip.blksize + ip.offset; 959 boff = addr % Sectorsize; 960 if(boff > Sectorsize-34){ 961 ip.offset += Sectorsize-boff; 962 continue; 963 } 964 p := Block.get(f.xf.d, addr/Sectorsize); 965 if(p == nil) 966 return -1; 967 nb := int p.data[boff]; 968 if(nb >= 34) { 969 convM2Drec(p.data[boff:], d, ip.fmt=='r'); 970 #chat(sys->sprint("off %d", ip.offset)); 971 #showdrec(stderr, ip.fmt, d); 972 p.put(); 973 ip.offset += nb + (nb&1); 974 return 0; 975 } 976 p.put(); 977 p = nil; 978 ip.offset += Sectorsize-boff; 979 } 980 return -1; 981} 982 983# getcontin returns a slice of the Iobuf, valid until next i/o call 984getcontin(d: ref Device, a: array of byte): (array of byte, int) 985{ 986 bn := l32(a[4:]); 987 off := l32(a[12:]); 988 n := l32(a[20:]); 989 p := Block.get(d, bn); 990 if(p == nil) 991 return (nil, 0); 992 return (p.data[off:off+n], n); 993} 994 995iso(f: ref Xfile): ref Isofile 996{ 997 if(f.ptr == nil){ 998 f.ptr = ref Isofile; 999 f.ptr.d = ref Drec; 1000 } 1001 return f.ptr; 1002} 1003 1004opendotdot(f: ref Xfile, pf: ref Xfile): string 1005{ 1006 d := ref Drec; 1007 ip := iso(f); 1008 ip.offset = 0; 1009 if(getdrec(f, d) < 0) 1010 return "opendotdot: getdrec(.) failed"; 1011 if(len d.name != 1 || d.name[0] != byte 0) 1012 return "opendotdot: no . entry"; 1013 if(d.addr != ip.d.addr) 1014 return "opendotdot: bad . address"; 1015 if(getdrec(f, d) < 0) 1016 return "opendotdot: getdrec(..) failed"; 1017 if(len d.name != 1 || d.name[0] != byte 1) 1018 return "opendotdot: no .. entry"; 1019 1020 pf.xf = f.xf; 1021 pip := iso(pf); 1022 pip.fmt = ip.fmt; 1023 pip.blksize = ip.blksize; 1024 pip.offset = 0; 1025 pip.doffset = 0; 1026 pip.d = d; 1027 return nil; 1028} 1029 1030rzdir(fs: ref Xfs, d: ref Dir, fmt: int, dp: ref Drec): int 1031{ 1032 Hmode, Hname: con 1<<iota; 1033 vers := -1; 1034 have := 0; 1035 d.qid.path = big dp.addr; 1036 d.qid.vers = 0; 1037 d.qid.qtype = QTFILE; 1038 n := len dp.name; 1039 if(n == 1) { 1040 case int dp.name[0] { 1041 0 => d.name = "."; have |= Hname; 1042 1 => d.name = ".."; have |= Hname; 1043 * => d.name = ""; d.name[0] = tolower(int dp.name[0]); 1044 } 1045 } else { 1046 if(fmt == 'J'){ # Joliet, 16-bit Unicode 1047 d.name = ""; 1048 for(i:=0; i<n; i+=2){ 1049 r := (int dp.name[i]<<8) | int dp.name[i+1]; 1050 d.name[len d.name] = r; 1051 } 1052 }else{ 1053 if(n >= Maxname) 1054 n = Maxname-1; 1055 d.name = ""; 1056 for(i:=0; i<n && int dp.name[i] != '\r'; i++) 1057 d.name[i] = tolower(int dp.name[i]); 1058 } 1059 } 1060 1061 if(fs.isplan9 && dp.reclen>34+len dp.name) { 1062 # 1063 # get gid, uid, mode and possibly name 1064 # from plan9 directory extension 1065 # 1066 s := dp.data; 1067 n = int s[0]; 1068 if(n) 1069 d.name = string s[1:1+n]; 1070 l := 1+n; 1071 n = int s[l++]; 1072 d.uid = string s[l:l+n]; 1073 l += n; 1074 n = int s[l++]; 1075 d.gid = string s[l:l+n]; 1076 l += n; 1077 if(l & 1) 1078 l++; 1079 d.mode = l32(s[l:]); 1080 if(d.mode & DMDIR) 1081 d.qid.qtype = QTDIR; 1082 } else { 1083 d.mode = 8r444; 1084 case fmt { 1085 'z' => 1086 if(fs.isrock) 1087 d.gid = "ridge"; 1088 else 1089 d.gid = "iso"; 1090 'r' => 1091 d.gid = "sierra"; 1092 'J' => 1093 d.gid = "joliet"; 1094 * => 1095 d.gid = "???"; 1096 } 1097 flags := dp.flags; 1098 if(flags & 2){ 1099 d.qid.qtype = QTDIR; 1100 d.mode |= DMDIR|8r111; 1101 } 1102 d.uid = "cdrom"; 1103 for(i := 0; i < len d.name; i++) 1104 if(d.name[i] == ';') { 1105 vers = int string d.name[i+1:]; # inefficient 1106 d.name = d.name[0:i]; # inefficient 1107 break; 1108 } 1109 n = len dp.data - fs.suspoff; 1110 if(fs.isrock && n >= 4){ 1111 s := dp.data[fs.suspoff:]; 1112 nm := 0; 1113 while(n >= 4 && have != (Hname|Hmode)){ 1114 l := int s[2]; 1115 if(s[0] == byte 'P' && s[1] == byte 'X' && s[3] == byte 1){ 1116 # posix file attributes 1117 mode := l32(s[4:12]); 1118 d.mode = mode & 8r777; 1119 if((mode & 8r170000) == 8r0040000){ 1120 d.mode |= DMDIR; 1121 d.qid.qtype = QTDIR; 1122 } 1123 have |= Hmode; 1124 } else if(s[0] == byte 'N' && s[1] == byte 'M' && s[3] == byte 1){ 1125 # alternative name 1126 flags = int s[4]; 1127 if((flags & ~1) == 0){ 1128 if(nm == 0){ 1129 d.name = string s[5:l]; 1130 nm = 1; 1131 } else 1132 d.name += string s[5:l]; 1133 if(flags == 0) 1134 have |= Hname; # no more 1135 } 1136 } else if(s[0] == byte 'C' && s[1] == byte 'E' && int s[2] >= 28){ 1137 (s, n) = getcontin(fs.d, s); 1138 continue; 1139 } else if(s[0] == byte 'S' && s[1] == byte 'T') 1140 break; 1141 n -= l; 1142 s = s[l:]; 1143 } 1144 } 1145 } 1146 d.length = big 0; 1147 if((d.mode & DMDIR) == 0) 1148 d.length = big dp.size; 1149 d.dtype = 0; 1150 d.dev = 0; 1151 d.atime = dp.time; 1152 d.mtime = d.atime; 1153 return vers; 1154} 1155 1156convM2Drec(a: array of byte, d: ref Drec, highsierra: int) 1157{ 1158 d.reclen = int a[0]; 1159 d.attrlen = int a[1]; 1160 d.addr = int l32(a[2:10]); 1161 d.size = int l32(a[10:18]); 1162 d.time = gtime(a[18:24]); 1163 d.date = array[7] of byte; 1164 d.date[0:] = a[18:25]; 1165 if(highsierra){ 1166 d.tzone = 0; 1167 d.flags = int a[24]; 1168 d.unitsize = 0; 1169 d.gapsize = 0; 1170 d.vseqno = 0; 1171 } else { 1172 d.tzone = int a[24]; 1173 d.flags = int a[25]; 1174 d.unitsize = int a[26]; 1175 d.gapsize = int a[27]; 1176 d.vseqno = l32(a[28:32]); 1177 } 1178 n := int a[32]; 1179 d.name = array[n] of byte; 1180 d.name[0:] = a[33:33+n]; 1181 n += 33; 1182 if(n & 1) 1183 n++; # check this 1184 syslen := d.reclen - n; 1185 if(syslen > 0){ 1186 d.data = array[syslen] of byte; 1187 d.data[0:] = a[n:n+syslen]; 1188 } else 1189 d.data = nil; 1190} 1191 1192nstr(p: array of byte): string 1193{ 1194 q := ""; 1195 n := len p; 1196 for(i := 0; i < n; i++){ 1197 if(int p[i] == '\\') 1198 q[len q] = '\\'; 1199 if(' ' <= int p[i] && int p[i] <= '~') 1200 q[len q] = int p[i]; 1201 else 1202 q += sys->sprint("\\%2.2ux", int p[i]); 1203 } 1204 return q; 1205} 1206 1207rdate(p: array of byte, fmt: int): string 1208{ 1209 c: int; 1210 1211 s := sys->sprint("%2.2d.%2.2d.%2.2d %2.2d:%2.2d:%2.2d", 1212 int p[0], int p[1], int p[2], int p[3], int p[4], int p[5]); 1213 if(fmt == 'z'){ 1214 htz := int p[6]; 1215 if(htz >= 128){ 1216 htz = 256-htz; 1217 c = '-'; 1218 }else 1219 c = '+'; 1220 s += sys->sprint(" (%c%.1f)", c, real htz/2.0); 1221 } 1222 return s; 1223} 1224 1225dmsize := array[] of { 1226 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 1227}; 1228 1229dysize(y: int): int 1230{ 1231 if((y%4) == 0) 1232 return 366; 1233 return 365; 1234} 1235 1236gtime(p: array of byte): int # yMdhms 1237{ 1238 y:=int p[0]; M:=int p[1]; d:=int p[2]; 1239 h:=int p[3]; m:=int p[4]; s:=int p[5];; 1240 if(y < 70) 1241 return 0; 1242 if(M < 1 || M > 12) 1243 return 0; 1244 if(d < 1 || d > dmsize[M-1]) 1245 return 0; 1246 if(h > 23) 1247 return 0; 1248 if(m > 59) 1249 return 0; 1250 if(s > 59) 1251 return 0; 1252 y += 1900; 1253 t := 0; 1254 for(i:=1970; i<y; i++) 1255 t += dysize(i); 1256 if(dysize(y)==366 && M >= 3) 1257 t++; 1258 M--; 1259 while(M-- > 0) 1260 t += dmsize[M]; 1261 t += d-1; 1262 t = 24*t + h; 1263 t = 60*t + m; 1264 t = 60*t + s; 1265 return t; 1266} 1267 1268l16(p: array of byte): int 1269{ 1270 v := (int p[1]<<8)| int p[0]; 1271 if (v >= 16r8000) 1272 v -= 16r10000; 1273 return v; 1274} 1275 1276l32(p: array of byte): int 1277{ 1278 return (((((int p[3]<<8)| int p[2])<<8)| int p[1])<<8)| int p[0]; 1279} 1280 1281eqs(a: array of byte, b: string): int 1282{ 1283 if(len a != len b) 1284 return 0; 1285 for(i := 0; i < len a; i++) 1286 if(int a[i] != b[i]) 1287 return 0; 1288 return 1; 1289} 1290 1291tolower(c: int): int 1292{ 1293 if(c >= 'A' && c <= 'Z') 1294 return c-'A' + 'a'; 1295 return c; 1296} 1297 1298# 1299# I/O buffers 1300# 1301 1302Device: adt { 1303 inuse: int; # attach count 1304 name: string; # of underlying file 1305 fd: ref Sys->FD; 1306 sectorsize: int; 1307 qid: Sys->Qid; # (qid,dtype,dev) identify uniquely 1308 dtype: int; 1309 dev: int; 1310 1311 detach: fn(nil: self ref Device); 1312}; 1313 1314Block: adt { 1315 dev: ref Device; 1316 addr: int; 1317 data: array of byte; 1318 1319 # internal 1320 next: cyclic ref Block; 1321 prev: cyclic ref Block; 1322 busy: int; 1323 1324 get: fn(nil: ref Device, addr: int): ref Block; 1325 put: fn(nil: self ref Block); 1326}; 1327 1328devices: list of ref Device; 1329 1330NIOB: con 100; # for starters 1331HIOB: con 127; # prime 1332 1333hiob := array[HIOB] of list of ref Block; # hash buckets 1334iohead: ref Block; 1335iotail: ref Block; 1336bufsize := 0; 1337 1338iobufinit(bsize: int) 1339{ 1340 bufsize = bsize; 1341 for(i:=0; i<NIOB; i++) 1342 newblock(); 1343} 1344 1345newblock(): ref Block 1346{ 1347 p := ref Block; 1348 p.busy = 0; 1349 p.addr = -1; 1350 p.dev = nil; 1351 p.data = array[bufsize] of byte; 1352 p.next = iohead; 1353 if(iohead != nil) 1354 iohead.prev = p; 1355 iohead = p; 1356 if(iotail == nil) 1357 iotail = p; 1358 return p; 1359} 1360 1361Block.get(dev: ref Device, addr: int): ref Block 1362{ 1363 p: ref Block; 1364 1365 dh := hiob[addr%HIOB:]; 1366 for(l := dh[0]; l != nil; l = tl l) { 1367 p = hd l; 1368 if(p.addr == addr && p.dev == dev) { 1369 p.busy++; 1370 return p; 1371 } 1372 } 1373 # Find a non-busy buffer from the tail 1374 for(p = iotail; p != nil && p.busy; p = p.prev) 1375 ; 1376 if(p == nil) 1377 p = newblock(); 1378 1379 # Delete from hash chain 1380 if(p.addr >= 0) { 1381 hp := hiob[p.addr%HIOB:]; 1382 l = nil; 1383 for(f := hp[0]; f != nil; f = tl f) 1384 if(hd f != p) 1385 l = (hd f) :: l; 1386 hp[0] = l; 1387 } 1388 1389 # Hash and fill 1390 p.addr = addr; 1391 p.dev = dev; 1392 p.busy++; 1393 sys->seek(dev.fd, big addr*big dev.sectorsize, 0); 1394 if(sys->read(dev.fd, p.data, dev.sectorsize) != dev.sectorsize){ 1395 p.addr = -1; # stop caching 1396 p.put(); 1397 purge(dev); 1398 return nil; 1399 } 1400 dh[0] = p :: dh[0]; 1401 return p; 1402} 1403 1404Block.put(p: self ref Block) 1405{ 1406 p.busy--; 1407 if(p.busy < 0) 1408 panic("Block.put"); 1409 1410 if(p == iohead) 1411 return; 1412 1413 # Link onto head for lru 1414 if(p.prev != nil) 1415 p.prev.next = p.next; 1416 else 1417 iohead = p.next; 1418 1419 if(p.next != nil) 1420 p.next.prev = p.prev; 1421 else 1422 iotail = p.prev; 1423 1424 p.prev = nil; 1425 p.next = iohead; 1426 iohead.prev = p; 1427 iohead = p; 1428} 1429 1430purge(dev: ref Device) 1431{ 1432 for(i := 0; i < HIOB; i++){ 1433 l := hiob[i]; 1434 hiob[i] = nil; 1435 for(; l != nil; l = tl l){ # reverses bucket's list, but never mind 1436 p := hd l; 1437 if(p.dev == dev) 1438 p.busy = 0; 1439 else 1440 hiob[i] = p :: hiob[i]; 1441 } 1442 } 1443} 1444 1445devattach(name: string, mode: int, sectorsize: int): (ref Device, string) 1446{ 1447 if(sectorsize > bufsize) 1448 return (nil, "sector size too big"); 1449 fd := sys->open(name, mode); 1450 if(fd == nil) 1451 return(nil, sys->sprint("%s: can't open: %r", name)); 1452 (rc, dir) := sys->fstat(fd); 1453 if(rc < 0) 1454 return (nil, sys->sprint("%r")); 1455 for(dl := devices; dl != nil; dl = tl dl){ 1456 d := hd dl; 1457 if(d.qid.path != dir.qid.path || d.qid.vers != dir.qid.vers) 1458 continue; 1459 if(d.dtype != dir.dtype || d.dev != dir.dev) 1460 continue; 1461 d.inuse++; 1462 if(chatty) 1463 sys->print("inuse=%d, \"%s\", dev=%H...\n", d.inuse, d.name, d.fd); 1464 return (d, nil); 1465 } 1466 if(chatty) 1467 sys->print("alloc \"%s\", dev=%H...\n", name, fd); 1468 d := ref Device; 1469 d.inuse = 1; 1470 d.name = name; 1471 d.qid = dir.qid; 1472 d.dtype = dir.dtype; 1473 d.dev = dir.dev; 1474 d.fd = fd; 1475 d.sectorsize = sectorsize; 1476 devices = d :: devices; 1477 return (d, nil); 1478} 1479 1480Device.detach(d: self ref Device) 1481{ 1482 d.inuse--; 1483 if(d.inuse < 0) 1484 panic("putxdata"); 1485 if(chatty) 1486 sys->print("decref=%d, \"%s\", dev=%H...\n", d.inuse, d.name, d.fd); 1487 if(d.inuse == 0){ 1488 if(chatty) 1489 sys->print("purge...\n"); 1490 purge(d); 1491 dl := devices; 1492 devices = nil; 1493 for(; dl != nil; dl = tl dl) 1494 if((hd dl) != d) 1495 devices = (hd dl) :: devices; 1496 } 1497} 1498 1499panic(s: string) 1500{ 1501 sys->print("panic: %s\n", s); 1502 a: array of byte; 1503 a[5] = byte 0; # trap 1504} 1505