1implement Dossrv; 2 3include "sys.m"; 4 sys: Sys; 5 6include "draw.m"; 7 8include "arg.m"; 9 10include "daytime.m"; 11 daytime: Daytime; 12 13include "styx.m"; 14 styx: Styx; 15 Tmsg, Rmsg: import styx; 16 17Dossrv: module 18{ 19 init: fn(ctxt: ref Draw->Context, args: list of string); 20 system: fn(ctxt: ref Draw->Context, args: list of string): string; 21}; 22 23arg0 := "dossrv"; 24 25deffile: string; 26pflag := 0; 27debug := 0; 28 29usage(iscmd: int): string 30{ 31 sys->fprint(sys->fildes(2), "usage: %s [-v] [-s] [-F] [-c] [-S secpertrack] [-f devicefile] [-m mountpoint]\n", arg0); 32 if(iscmd) 33 raise "fail:usage"; 34 return "usage"; 35} 36 37init(nil: ref Draw->Context, args: list of string) 38{ 39 e := init2(nil, args, 1); 40 if(e != nil){ 41 sys->fprint(sys->fildes(2), "%s: %s\n", arg0, e); 42 raise "fail:error"; 43 } 44} 45 46system(nil: ref Draw->Context, args: list of string): string 47{ 48 e := init2(nil, args, 0); 49 if(e != nil) 50 sys->fprint(sys->fildes(2), "%s: %s\n", arg0, e); 51 return e; 52} 53 54nomod(s: string): string 55{ 56 return sys->sprint("can't load %s: %r", s); 57} 58 59init2(nil: ref Draw->Context, args: list of string, iscmd: int): string 60{ 61 sys = load Sys Sys->PATH; 62 63 pipefd := array[2] of ref Sys->FD; 64 65 srvfile := "/n/dos"; 66 deffile = ""; # no default, for safety 67 sectors := 0; 68 stdin := 0; 69 70 arg := load Arg Arg->PATH; 71 if(arg == nil) 72 return nomod(Arg->PATH); 73 arg->init(args); 74 arg0 = arg->progname(); 75 while((o := arg->opt()) != 0) { 76 case o { 77 'v' => 78 if(debug & STYX_MESS) 79 debug |= VERBOSE; 80 debug |= STYX_MESS; 81 'F' => 82 debug |= FAT_INFO; 83 'c' => 84 debug |= CLUSTER_INFO; 85 iodebug = 1; 86 'S' => 87 s := arg->arg(); 88 if(s != nil && s[0]>='0' && s[0]<='9') 89 sectors = int s; 90 else 91 return usage(iscmd); 92 's' => 93 stdin = 1; 94 'f' => 95 deffile = arg->arg(); 96 if(deffile == nil) 97 return usage(iscmd); 98 'm' => 99 srvfile = arg->arg(); 100 if(srvfile == nil) 101 return usage(iscmd); 102 'p' => 103 pflag++; 104 * => 105 return usage(iscmd); 106 } 107 } 108 args = arg->argv(); 109 arg = nil; 110 111 if(deffile == "" || !stdin && srvfile == "") 112 return usage(iscmd); 113 114 styx = load Styx Styx->PATH; 115 if(styx == nil) 116 return nomod(Styx->PATH); 117 styx->init(); 118 119 daytime = load Daytime Daytime->PATH; 120 if(daytime == nil) 121 return nomod(Daytime->PATH); 122 123 iotrackinit(sectors); 124 125 if(!stdin) { 126 if(sys->pipe(pipefd) < 0) 127 return sys->sprint("can't create pipe: %r"); 128 }else{ 129 pipefd[0] = nil; 130 pipefd[1] = sys->fildes(1); 131 } 132 133 dossetup(); 134 135 spawn dossrv(pipefd[1]); 136 137 if(!stdin) { 138 if(sys->mount(pipefd[0], nil, srvfile, sys->MREPL|sys->MCREATE, deffile) < 0) 139 return sys->sprint("mount %s: %r", srvfile); 140 } 141 142 return nil; 143} 144 145# 146# Styx server 147# 148 149 Enevermind, 150 Eformat, 151 Eio, 152 Enomem, 153 Enonexist, 154 Enotdir, 155 Enofid, 156 Efidopen, 157 Efidinuse, 158 Eexist, 159 Eperm, 160 Enofilsys, 161 Eauth, 162 Econtig, 163 Efull, 164 Eopen, 165 Ephase: con iota; 166 167errmsg := array[] of { 168 Enevermind => "never mind", 169 Eformat => "unknown format", 170 Eio => "I/O error", 171 Enomem => "server out of memory", 172 Enonexist => "file does not exist", 173 Enotdir => "not a directory", 174 Enofid => "no such fid", 175 Efidopen => "fid already open", 176 Efidinuse => "fid in use", 177 Eexist => "file exists", 178 Eperm => "permission denied", 179 Enofilsys => "no file system device specified", 180 Eauth => "authentication failed", 181 Econtig => "out of contiguous disk space", 182 Efull => "file system full", 183 Eopen => "invalid open mode", 184 Ephase => "phase error -- directory entry not found", 185}; 186 187e(n: int): ref Rmsg.Error 188{ 189 if(n < 0 || n >= len errmsg) 190 return ref Rmsg.Error(0, "it's thermal problems"); 191 return ref Rmsg.Error(0, errmsg[n]); 192} 193 194dossrv(rfd: ref Sys->FD) 195{ 196 sys->pctl(Sys->NEWFD, rfd.fd :: 2 :: nil); 197 rfd = sys->fildes(rfd.fd); 198 while((t := Tmsg.read(rfd, 0)) != nil){ 199 if(debug & STYX_MESS) 200 chat(sys->sprint("%s...", t.text())); 201 202 r: ref Rmsg; 203 pick m := t { 204 Readerror => 205 panic(sys->sprint("mount read error: %s", m.error)); 206 Version => 207 r = rversion(m); 208 Auth => 209 r = rauth(m); 210 Flush => 211 r = rflush(m); 212 Attach => 213 r = rattach(m); 214 Walk => 215 r = rwalk(m); 216 Open => 217 r = ropen(m); 218 Create => 219 r = rcreate(m); 220 Read => 221 r = rread(m); 222 Write => 223 r = rwrite(m); 224 Clunk => 225 r = rclunk(m); 226 Remove => 227 r = rremove(m); 228 Stat => 229 r = rstat(m); 230 Wstat => 231 r = rwstat(m); 232 * => 233 panic("Styx mtype"); 234 } 235 pick m := r { 236 Error => 237 r.tag = t.tag; 238 } 239 rbuf := r.pack(); 240 if(rbuf == nil) 241 panic("Rmsg.pack"); 242 if(debug & STYX_MESS) 243 chat(sys->sprint("%s\n", r.text())); 244 if(sys->write(rfd, rbuf, len rbuf) != len rbuf) 245 panic("mount write"); 246 } 247 248 if(debug & STYX_MESS) 249 chat("server EOF\n"); 250} 251 252rversion(t: ref Tmsg.Version): ref Rmsg 253{ 254 (msize, version) := styx->compatible(t, Styx->MAXRPC, Styx->VERSION); 255 return ref Rmsg.Version(t.tag, msize, version); 256} 257 258rauth(t: ref Tmsg.Auth): ref Rmsg 259{ 260 return ref Rmsg.Error(t.tag, "authentication not required"); 261} 262 263rflush(t: ref Tmsg.Flush): ref Rmsg 264{ 265 return ref Rmsg.Flush(t.tag); 266} 267 268rattach(t: ref Tmsg.Attach): ref Rmsg 269{ 270 root := xfile(t.fid, Clean); 271 if(root == nil) 272 return e(Eio); 273 if(t.aname == nil) 274 t.aname = deffile; 275 (xf, ec) := getxfs(t.aname); 276 root.xf = xf; 277 if(xf == nil) { 278 if(root!=nil) 279 xfile(t.fid, Clunk); 280 return ref Rmsg.Error(t.tag, ec); 281 } 282 if(xf.fmt == 0 && dosfs(xf) < 0){ 283 if(root!=nil) 284 xfile(t.fid, Clunk); 285 return e(Eformat); 286 } 287 288 root.qid = Sys->Qid(big 0, 0, Sys->QTDIR); 289 root.xf.rootqid = root.qid; 290 return ref Rmsg.Attach(t.tag, root.qid); 291} 292 293clone(ofl: ref Xfile, newfid: int): ref Xfile 294{ 295 nfl := xfile(newfid, Clean); 296 next := nfl.next; 297 *nfl = *ofl; 298 nfl.ptr = nil; 299 nfl.next = next; 300 nfl.fid = newfid; 301 refxfs(nfl.xf, 1); 302 if(ofl.ptr != nil){ 303 dp := ref *ofl.ptr; 304 dp.p = nil; 305 dp.d = nil; 306 nfl.ptr = dp; 307 } 308 return nfl; 309} 310 311walk1(f: ref Xfile, name: string): ref Rmsg.Error 312{ 313 if((f.qid.qtype & Sys->QTDIR) == 0){ 314 if(debug) 315 chat(sys->sprint("qid.path=0x%bx...", f.qid.path)); 316 return e(Enotdir); 317 } 318 319 if(name == ".") # can't happen 320 return nil; 321 322 if(name== "..") { 323 if(f.qid.path == f.xf.rootqid.path) { 324 if (debug) 325 chat("walkup from root..."); 326 return nil; 327 } 328 (r,dp) := walkup(f); 329 if(r < 0) 330 return e(Enonexist); 331 332 f.ptr = dp; 333 if(dp.addr == 0) { 334 f.qid.path = f.xf.rootqid.path; 335 f.qid.qtype = Sys->QTFILE; 336 } else { 337 f.qid.path = QIDPATH(dp); 338 f.qid.qtype = Sys->QTDIR; 339 } 340 } else { 341 if(getfile(f) < 0) 342 return e(Enonexist); 343 (r,dp) := searchdir(f, name, 0,1); 344 putfile(f); 345 if(r < 0) 346 return e(Enonexist); 347 348 f.ptr = dp; 349 f.qid.path = QIDPATH(dp); 350 f.qid.qtype = Sys->QTFILE; 351 if(dp.addr == 0) 352 f.qid.path = f.xf.rootqid.path; 353 else { 354 d := Dosdir.arr2Dd(dp.p.iobuf[dp.offset:dp.offset+DOSDIRSIZE]); 355 if((int d.attr & DDIR) != 0) 356 f.qid.qtype = Sys->QTDIR; 357 } 358 putfile(f); 359 } 360 return nil; 361} 362 363rwalk(t: ref Tmsg.Walk): ref Rmsg 364{ 365 f := xfile(t.fid, Asis); 366 if(f==nil) { 367 if(debug) 368 chat("no xfile..."); 369 return e(Enofid); 370 } 371 nf: ref Xfile; 372 if(t.newfid != t.fid) 373 f = nf = clone(f, t.newfid); 374 qids: array of Sys->Qid; 375 if(len t.names > 0){ 376 savedqid := f.qid; 377 savedptr := f.ptr; 378 qids = array[len t.names] of Sys->Qid; 379 for(i := 0; i < len t.names; i++){ 380 e := walk1(f, t.names[i]); 381 if(e != nil){ 382 f.qid = savedqid; 383 f.ptr = savedptr; 384 if(nf != nil) 385 xfile(t.newfid, Clunk); 386 if(i == 0) 387 return e; 388 return ref Rmsg.Walk(t.tag, qids[0:i]); 389 } 390 qids[i] = f.qid; 391 } 392 } 393 return ref Rmsg.Walk(t.tag, qids); 394} 395 396ropen(t: ref Tmsg.Open): ref Rmsg 397{ 398 attr: int; 399 400 omode := 0; 401 f := xfile(t.fid, Asis); 402 if(f == nil) 403 return e(Enofid); 404 if((f.flags&Omodes) != 0) 405 return e(Efidopen); 406 407 dp := f.ptr; 408 if(dp.paddr && (t.mode & Styx->ORCLOSE) != 0) { 409 # check on parent directory of file to be deleted 410 p := getsect(f.xf, dp.paddr); 411 if(p == nil) 412 return e(Eio); 413 # 11 is the attr byte offset in a FAT directory entry 414 attr = int p.iobuf[dp.poffset+11]; 415 putsect(p); 416 if((attr & int DRONLY) != 0) 417 return e(Eperm); 418 omode |= Orclose; 419 } else if(t.mode & Styx->ORCLOSE) 420 omode |= Orclose; 421 422 if(getfile(f) < 0) 423 return e(Enonexist); 424 425 if(dp.addr != 0) { 426 d := Dosdir.arr2Dd(dp.p.iobuf[dp.offset:dp.offset+DOSDIRSIZE]); 427 attr = int d.attr; 428 } else 429 attr = int DDIR; 430 431 case t.mode & 7 { 432 Styx->OREAD or 433 Styx->OEXEC => 434 omode |= Oread; 435 Styx->ORDWR => 436 omode |= Oread; 437 omode |= Owrite; 438 if(attr & int (DRONLY|DDIR)) { 439 putfile(f); 440 return e(Eperm); 441 } 442 Styx->OWRITE => 443 omode |= Owrite; 444 if(attr & int (DRONLY|DDIR)) { 445 putfile(f); 446 return e(Eperm); 447 } 448 * => 449 putfile(f); 450 return e(Eopen); 451 } 452 453 if(t.mode & Styx->OTRUNC) { 454 if((attr & int DDIR)!=0 || (attr & int DRONLY) != 0) { 455 putfile(f); 456 return e(Eperm); 457 } 458 459 if(truncfile(f) < 0) { 460 putfile(f); 461 return e(Eio); 462 } 463 } 464 465 f.flags |= omode; 466 putfile(f); 467 return ref Rmsg.Open(t.tag, f.qid, Styx->MAXFDATA); 468} 469 470mkdentry(xf: ref Xfs, ndp: ref Dosptr, name: string, sname: string, islong: int, nattr: byte, start: array of byte, length: array of byte): int 471{ 472 ndp.p = getsect(xf, ndp.addr); 473 if(ndp.p == nil) 474 return Eio; 475 if(islong && (r := putlongname(xf, ndp, name, sname)) < 0){ 476 putsect(ndp.p); 477 if(r == -2) 478 return Efull; 479 return Eio; 480 } 481 482 nd := ref Dosdir(". "," ",byte 0,array[10] of { * => byte 0}, 483 array[2] of { * => byte 0}, array[2] of { * => byte 0}, 484 array[2] of { * => byte 0},array[4] of { * => byte 0}); 485 486 nd.attr = nattr; 487 puttime(nd); 488 nd.start[0: ] = start[0: 2]; 489 nd.length[0: ] = length[0: 4]; 490 491 if(islong) 492 putname(sname[0:8]+"."+sname[8:11], nd); 493 else 494 putname(name, nd); 495 ndp.p.iobuf[ndp.offset: ] = Dosdir.Dd2arr(nd); 496 ndp.p.flags |= BMOD; 497 return 0; 498} 499 500rcreate(t: ref Tmsg.Create): ref Rmsg 501{ 502 bp: ref Dosbpb; 503 omode:=0; 504 start:=0; 505 sname := ""; 506 islong :=0; 507 508 f := xfile(t.fid, Asis); 509 if(f == nil) 510 return e(Enofid); 511 if((f.flags&Omodes) != 0) 512 return e(Efidopen); 513 if(getfile(f)<0) 514 return e(Eio); 515 516 pdp := f.ptr; 517 if(pdp.addr != 0) 518 pd := Dosdir.arr2Dd(pdp.p.iobuf[pdp.offset:pdp.offset+DOSDIRSIZE]); 519 else 520 pd = nil; 521 522 if(pd != nil) 523 attr := int pd.attr; 524 else 525 attr = DDIR; 526 527 if(!(attr & DDIR) || (attr & DRONLY)) { 528 putfile(f); 529 return e(Eperm); 530 } 531 532 if(t.mode & Styx->ORCLOSE) 533 omode |= Orclose; 534 535 case (t.mode & 7) { 536 Styx->OREAD or 537 Styx->OEXEC => 538 omode |= Oread; 539 Styx->OWRITE or 540 Styx->ORDWR => 541 if ((t.mode & 7) == Styx->ORDWR) 542 omode |= Oread; 543 omode |= Owrite; 544 if(t.perm & Sys->DMDIR){ 545 putfile(f); 546 return e(Eperm); 547 } 548 * => 549 putfile(f); 550 return e(Eopen); 551 } 552 553 if(t.name=="." || t.name=="..") { 554 putfile(f); 555 return e(Eperm); 556 } 557 558 (r,ndp) := searchdir(f, t.name, 1, 1); 559 if(r < 0) { 560 putfile(f); 561 if(r == -2) 562 return e(Efull); 563 return e(Eexist); 564 } 565 566 nds := name2de(t.name); 567 if(nds > 0) { 568 # long file name, find "new" short name 569 i := 1; 570 for(;;) { 571 sname = long2short(t.name, i); 572 (r1, tmpdp) := searchdir(f, sname, 0, 0); 573 if(r1 < 0) 574 break; 575 putsect(tmpdp.p); 576 i++; 577 } 578 islong = 1; 579 } 580 581 # allocate first cluster, if making directory 582 if(t.perm & Sys->DMDIR) { 583 bp = f.xf.ptr; 584 start = falloc(f.xf); 585 if(start <= 0) { 586 putfile(f); 587 return e(Efull); 588 } 589 } 590 591 # now we're committed 592 if(pd != nil) { 593 puttime(pd); 594 pdp.p.flags |= BMOD; 595 } 596 597 f.ptr = ndp; 598 ndp.p = getsect(f.xf, ndp.addr); 599 if(ndp.p == nil || 600 islong && putlongname(f.xf, ndp, t.name, sname) < 0){ 601 putsect(pdp.p); 602 if(ndp.p != nil) 603 putsect(ndp.p); 604 return e(Eio); 605 } 606 607 nd := ref Dosdir(". "," ",byte 0,array[10] of { * => byte 0}, 608 array[2] of { * => byte 0}, array[2] of { * => byte 0}, 609 array[2] of { * => byte 0},array[4] of { * => byte 0}); 610 611 if((t.perm & 8r222) == 0) 612 nd.attr |= byte DRONLY; 613 614 puttime(nd); 615 nd.start[0] = byte start; 616 nd.start[1] = byte (start>>8); 617 618 if(islong) 619 putname(sname[0:8]+"."+sname[8:11], nd); 620 else 621 putname(t.name, nd); 622 623 f.qid.path = QIDPATH(ndp); 624 if(t.perm & Sys->DMDIR) { 625 nd.attr |= byte DDIR; 626 f.qid.qtype |= Sys->QTDIR; 627 xp := getsect(f.xf, bp.dataaddr+(start-2)*bp.clustsize); 628 if(xp == nil) { 629 if(ndp.p!=nil) 630 putfile(f); 631 putsect(pdp.p); 632 return e(Eio); 633 } 634 xd := ref *nd; 635 xd.name = ". "; 636 xd.ext = " "; 637 xp.iobuf[0:] = Dosdir.Dd2arr(xd); 638 if(pd!=nil) 639 xd = ref *pd; 640 else{ 641 xd = ref Dosdir(".. "," ",byte 0, 642 array[10] of { * => byte 0}, 643 array[2] of { * => byte 0}, 644 array[2] of { * => byte 0}, 645 array[2] of { * => byte 0}, 646 array[4] of { * => byte 0}); 647 648 puttime(xd); 649 xd.attr = byte DDIR; 650 } 651 xd.name=".. "; 652 xd.ext=" "; 653 xp.iobuf[DOSDIRSIZE:] = Dosdir.Dd2arr(xd); 654 xp.flags |= BMOD; 655 putsect(xp); 656 }else 657 f.qid.qtype = Sys->QTFILE; 658 659 ndp.p.flags |= BMOD; 660 tmp := Dosdir.Dd2arr(nd); 661 ndp.p.iobuf[ndp.offset:]= tmp; 662 putfile(f); 663 putsect(pdp.p); 664 665 f.flags |= omode; 666 return ref Rmsg.Create(t.tag, f.qid, Styx->MAXFDATA); 667} 668 669rread(t: ref Tmsg.Read): ref Rmsg 670{ 671 r: int; 672 data: array of byte; 673 674 if(((f:=xfile(t.fid, Asis))==nil) || 675 (f.flags&Oread == 0)) 676 return e(Eio); 677 678 if((f.qid.qtype & Sys->QTDIR) != 0) { 679 if(getfile(f) < 0) 680 return e(Eio); 681 (r, data) = readdir(f, int t.offset, t.count); 682 } else { 683 if(getfile(f) < 0) 684 return e(Eio); 685 (r,data) = readfile(f, int t.offset, t.count); 686 } 687 putfile(f); 688 689 if(r < 0) 690 return e(Eio); 691 return ref Rmsg.Read(t.tag, data[0:r]); 692} 693 694rwrite(t: ref Tmsg.Write): ref Rmsg 695{ 696 if(((f:=xfile(t.fid, Asis))==nil) || 697 !(f.flags&Owrite)) 698 return e(Eio); 699 if(getfile(f) < 0) 700 return e(Eio); 701 r := writefile(f, t.data, int t.offset, len t.data); 702 putfile(f); 703 if(r < 0){ 704 if(r == -2) 705 return e(Efull); 706 return e(Eio); 707 } 708 return ref Rmsg.Write(t.tag, r); 709} 710 711rclunk(t: ref Tmsg.Clunk): ref Rmsg 712{ 713 xfile(t.fid, Clunk); 714 sync(); 715 return ref Rmsg.Clunk(t.tag); 716} 717 718doremove(f: ref Xfs, dp: ref Dosptr) 719{ 720 dp.p.iobuf[dp.offset] = byte DOSEMPTY; 721 dp.p.flags |= BMOD; 722 for(prevdo := dp.offset-DOSDIRSIZE; prevdo >= 0; prevdo-=DOSDIRSIZE){ 723 if (dp.p.iobuf[prevdo+11] != byte DLONG) 724 break; 725 dp.p.iobuf[prevdo] = byte DOSEMPTY; 726 } 727 728 if (prevdo <= 0 && dp.prevaddr != -1){ 729 p := getsect(f,dp.prevaddr); 730 for(prevdo = f.ptr.sectsize-DOSDIRSIZE; prevdo >= 0; prevdo-=DOSDIRSIZE) { 731 if(p.iobuf[prevdo+11] != byte DLONG) 732 break; 733 p.iobuf[prevdo] = byte DOSEMPTY; 734 p.flags |= BMOD; 735 } 736 putsect(p); 737 } 738} 739 740rremove(t: ref Tmsg.Remove): ref Rmsg 741{ 742 f := xfile(t.fid, Asis); 743 if(f == nil) 744 return e(Enofid); 745 746 if(!f.ptr.addr) { 747 if(debug) 748 chat("root..."); 749 xfile(t.fid, Clunk); 750 sync(); 751 return e(Eperm); 752 } 753 754 # check on parent directory of file to be deleted 755 parp := getsect(f.xf, f.ptr.paddr); 756 if(parp == nil) { 757 xfile(t.fid, Clunk); 758 sync(); 759 return e(Eio); 760 } 761 762 pard := Dosdir.arr2Dd(parp.iobuf[f.ptr.poffset:f.ptr.poffset+DOSDIRSIZE]); 763 if(f.ptr.paddr && (int pard.attr & DRONLY)) { 764 if(debug) 765 chat("parent read-only..."); 766 putsect(parp); 767 xfile(t.fid, Clunk); 768 sync(); 769 return e(Eperm); 770 } 771 772 if(getfile(f) < 0){ 773 if(debug) 774 chat("getfile failed..."); 775 putsect(parp); 776 xfile(t.fid, Clunk); 777 sync(); 778 return e(Eio); 779 } 780 781 dattr := int f.ptr.p.iobuf[f.ptr.offset+11]; 782 if(dattr & DDIR && emptydir(f) < 0){ 783 if(debug) 784 chat("non-empty dir..."); 785 putfile(f); 786 putsect(parp); 787 xfile(t.fid, Clunk); 788 sync(); 789 return e(Eperm); 790 } 791 if(f.ptr.paddr == 0 && dattr&DRONLY) { 792 if(debug) 793 chat("read-only file in root directory..."); 794 putfile(f); 795 putsect(parp); 796 xfile(t.fid, Clunk); 797 sync(); 798 return e(Eperm); 799 } 800 801 doremove(f.xf, f.ptr); 802 803 if(f.ptr.paddr) { 804 puttime(pard); 805 parp.flags |= BMOD; 806 } 807 808 parp.iobuf[f.ptr.poffset:] = Dosdir.Dd2arr(pard); 809 putsect(parp); 810 err := 0; 811 if(truncfile(f) < 0) 812 err = Eio; 813 814 putfile(f); 815 xfile(t.fid, Clunk); 816 sync(); 817 if(err) 818 return e(err); 819 return ref Rmsg.Remove(t.tag); 820} 821 822rstat(t: ref Tmsg.Stat): ref Rmsg 823{ 824 f := xfile(t.fid, Asis); 825 if(f == nil) 826 return e(Enofid); 827 if(getfile(f) < 0) 828 return e(Eio); 829 dir := dostat(f); 830 putfile(f); 831 return ref Rmsg.Stat(t.tag, *dir); 832} 833 834dostat(f: ref Xfile): ref Sys->Dir 835{ 836 islong :=0; 837 prevdo: int; 838 longnamebuf:=""; 839 840 # get file info. 841 dir := getdir(f.ptr.p.iobuf[f.ptr.offset:f.ptr.offset+DOSDIRSIZE], 842 f.ptr.addr, f.ptr.offset); 843 # get previous entry 844 if(f.ptr.prevaddr == -1) { 845 # maybe extended, but will never cross sector boundary... 846 # short filename at beginning of sector.. 847 if(f.ptr.offset!=0) { 848 for(prevdo = f.ptr.offset-DOSDIRSIZE; prevdo >=0; prevdo-=DOSDIRSIZE) { 849 prevdattr := f.ptr.p.iobuf[prevdo+11]; 850 if(prevdattr != byte DLONG) 851 break; 852 islong = 1; 853 longnamebuf += getnamesect(f.ptr.p.iobuf[prevdo:prevdo+DOSDIRSIZE]); 854 } 855 } 856 } else { 857 # extended and will cross sector boundary. 858 for(prevdo = f.ptr.offset-DOSDIRSIZE; prevdo >=0; prevdo-=DOSDIRSIZE) { 859 prevdattr := f.ptr.p.iobuf[prevdo+11]; 860 if(prevdattr != byte DLONG) 861 break; 862 islong = 1; 863 longnamebuf += getnamesect(f.ptr.p.iobuf[prevdo:prevdo+DOSDIRSIZE]); 864 } 865 if (prevdo < 0) { 866 p := getsect(f.xf,f.ptr.prevaddr); 867 for(prevdo = f.xf.ptr.sectsize-DOSDIRSIZE; prevdo >=0; prevdo-=DOSDIRSIZE){ 868 prevdattr := p.iobuf[prevdo+11]; 869 if(prevdattr != byte DLONG) 870 break; 871 islong = 1; 872 longnamebuf += getnamesect(p.iobuf[prevdo:prevdo+DOSDIRSIZE]); 873 } 874 putsect(p); 875 } 876 } 877 if(islong) 878 dir.name = longnamebuf; 879 return dir; 880} 881 882nameok(elem: string): int 883{ 884 isfrog := array[256] of { 885 # NUL 886 1, 1, 1, 1, 1, 1, 1, 1, 887 # BKS 888 1, 1, 1, 1, 1, 1, 1, 1, 889 # DLE 890 1, 1, 1, 1, 1, 1, 1, 1, 891 # CAN 892 1, 1, 1, 1, 1, 1, 1, 1, 893# ' ' => 1, 894 '/' => 1, 16r7f => 1, * => 0 895 }; 896 897 for(i:=0; i < len elem; i++) { 898 if(isfrog[elem[i]]) 899 return -1; 900 } 901 return 0; 902} 903 904rwstat(t: ref Tmsg.Wstat): ref Rmsg 905{ 906 f := xfile(t.fid, Asis); 907 if(f == nil) 908 return e(Enofid); 909 910 if(getfile(f) < 0) 911 return e(Eio); 912 913 dp := f.ptr; 914 915 if(dp.addr == 0){ # root 916 putfile(f); 917 return e(Eperm); 918 } 919 920 changes := 0; 921 dir := dostat(f); 922 wdir := ref t.stat; 923 924 if(dir.uid != wdir.uid || dir.gid != wdir.gid){ 925 putfile(f); 926 return e(Eperm); 927 } 928 929 if(dir.mtime != wdir.mtime || ((dir.mode^wdir.mode) & 8r777)) 930 changes = 1; 931 932 if((wdir.mode & 7) != ((wdir.mode >> 3) & 7) 933 || (wdir.mode & 7) != ((wdir.mode >> 6) & 7)){ 934 putfile(f); 935 return e(Eperm); 936 } 937 938 if(dir.name != wdir.name){ 939 # temporarily disable this 940 # g.errno = Eperm; 941 # putfile(f); 942 # return; 943 944 # 945 # grab parent directory of file to be changed and check for write perm 946 # rename also disallowed for read-only files in root directory 947 # 948 parp := getsect(f.xf, dp.paddr); 949 if(parp == nil){ 950 putfile(f); 951 return e(Eio); 952 } 953 # pard := Dosdir.arr2Dd(parp.iobuf[dp.poffset: dp.poffset+DOSDIRSIZE]); 954 pardattr := int parp.iobuf[dp.poffset+11]; 955 dpd := Dosdir.arr2Dd(dp.p.iobuf[dp.offset: dp.offset+DOSDIRSIZE]); 956 if(dp.paddr != 0 && int pardattr & DRONLY 957 || dp.paddr == 0 && int dpd.attr & DRONLY){ 958 putsect(parp); 959 putfile(f); 960 return e(Eperm); 961 } 962 963 # 964 # retrieve info from old entry 965 # 966 oaddr := dp.addr; 967 ooffset := dp.offset; 968 d := dpd; 969# od := *d; 970 # start := getstart(f.xf, d); 971 start := d.start; 972 length := d.length; 973 attr := d.attr; 974 975 # 976 # temporarily release file to allow other directory ops: 977 # walk to parent, validate new name 978 # then remove old entry 979 # 980 putfile(f); 981 pf := ref *f; 982 pdp := ref Dosptr(dp.paddr, dp.poffset, 0, 0, 0, 0, -1, -1, parp, nil); 983 # if(pdp.addr != 0) 984 # pdpd := Dosdir.arr2Dd(parp.iobuf[pdp.offset: pdp.offset+DOSDIRSIZE]); 985 # else 986 # pdpd = nil; 987 pf.ptr = pdp; 988 if(wdir.name == "." || wdir.name == ".."){ 989 putsect(parp); 990 return e(Eperm); 991 } 992 islong := 0; 993 sname := ""; 994 nds := name2de(wdir.name); 995 if(nds > 0) { 996 # long file name, find "new" short name 997 i := 1; 998 for(;;) { 999 sname = long2short(wdir.name, i); 1000 (r1, tmpdp) := searchdir(f, sname, 0, 0); 1001 if(r1 < 0) 1002 break; 1003 putsect(tmpdp.p); 1004 i++; 1005 } 1006 islong = 1; 1007 }else{ 1008 (b, e) := dosname(wdir.name); 1009 sname = b+e; 1010 } 1011 # (r, ndp) := searchdir(pf, wdir.name, 1, 1); 1012 # if(r < 0){ 1013 # putsect(parp); 1014 # g.errno = Eperm; 1015 # return; 1016 # } 1017 if(getfile(f) < 0){ 1018 putsect(parp); 1019 return e(Eio); 1020 } 1021 doremove(f.xf, dp); 1022 putfile(f); 1023 1024 # 1025 # search for dir entry again, since we may be able to use the old slot, 1026 # and we need to set up the naddr field if a long name spans the block. 1027 # create new entry. 1028 # 1029 r := 0; 1030 (r, dp) = searchdir(pf, sname, 1, islong); 1031 if(r < 0){ 1032 putsect(parp); 1033 return e(Ephase); 1034 } 1035 if((r = mkdentry(pf.xf, dp, wdir.name, sname, islong, attr, start, length)) != 0){ 1036 putsect(parp); 1037 return e(r); 1038 } 1039 putsect(parp); 1040 1041 # 1042 # relocate up other fids to the same file, if it moved 1043 # 1044 f.ptr = dp; 1045 f.qid.path = QIDPATH(dp); 1046 if(oaddr != dp.addr || ooffset != dp.offset) 1047 dosptrreloc(f, dp, oaddr, ooffset); 1048 changes = 1; 1049 # f = nil; 1050 } 1051 1052 if(changes){ 1053 d := Dosdir.arr2Dd(dp.p.iobuf[dp.offset:dp.offset+DOSDIRSIZE]); 1054 putdir(d, wdir); 1055 dp.p.iobuf[dp.offset: ] = Dosdir.Dd2arr(d); 1056 dp.p.flags |= BMOD; 1057 } 1058 if(f != nil) 1059 putfile(f); 1060 sync(); 1061 return ref Rmsg.Wstat(t.tag); 1062} 1063 1064# 1065# FAT file system format 1066# 1067 1068Dospart: adt { 1069 active: byte; 1070 hstart: byte; 1071 cylstart: array of byte; 1072 typ: byte; 1073 hend: byte; 1074 cylend: array of byte; 1075 start: array of byte; 1076 length: array of byte; 1077}; 1078 1079Dosboot: adt { 1080 arr2Db: fn(arr: array of byte): ref Dosboot; 1081 magic: array of byte; 1082 version: array of byte; 1083 sectsize: array of byte; 1084 clustsize: byte; 1085 nresrv: array of byte; 1086 nfats: byte; 1087 rootsize: array of byte; 1088 volsize: array of byte; 1089 mediadesc: byte; 1090 fatsize: array of byte; 1091 trksize: array of byte; 1092 nheads: array of byte; 1093 nhidden: array of byte; 1094 bigvolsize: array of byte; 1095 driveno: byte; 1096 bootsig: byte; 1097 volid: array of byte; 1098 label: array of byte; 1099}; 1100 1101Dosbpb: adt { 1102 sectsize: int; # in bytes 1103 clustsize: int; # in sectors 1104 nresrv: int; # sectors 1105 nfats: int; # usually 2 1106 rootsize: int; # number of entries 1107 volsize: int; # in sectors 1108 mediadesc: int; 1109 fatsize: int; # in sectors 1110 fatclusters: int; 1111 fatbits: int; # 12 or 16 1112 fataddr: int; #big; # sector number 1113 rootaddr: int; #big; 1114 dataaddr: int; #big; 1115 freeptr: int; #big; # next free cluster candidate 1116}; 1117 1118Dosdir: adt { 1119 Dd2arr: fn(d: ref Dosdir): array of byte; 1120 arr2Dd: fn(arr: array of byte): ref Dosdir; 1121 name: string; 1122 ext: string; 1123 attr: byte; 1124 reserved: array of byte; 1125 time: array of byte; 1126 date: array of byte; 1127 start: array of byte; 1128 length: array of byte; 1129}; 1130 1131Dosptr: adt { 1132 addr: int; # of file's directory entry 1133 offset: int; 1134 paddr: int; # of parent's directory entry 1135 poffset: int; 1136 iclust: int; # ordinal within file 1137 clust: int; 1138 prevaddr: int; 1139 naddr: int; 1140 p: ref Iosect; 1141 d: ref Dosdir; 1142}; 1143 1144Asis, Clean, Clunk: con iota; 1145 1146FAT12: con byte 16r01; 1147FAT16: con byte 16r04; 1148FATHUGE: con byte 16r06; 1149DMDDO: con 16r54; 1150DRONLY: con 16r01; 1151DHIDDEN: con 16r02; 1152DSYSTEM: con 16r04; 1153DVLABEL: con 16r08; 1154DDIR: con 16r10; 1155DARCH: con 16r20; 1156DLONG: con DRONLY | DHIDDEN | DSYSTEM | DVLABEL; 1157DMLONG: con DLONG | DDIR | DARCH; 1158 1159DOSDIRSIZE: con 32; 1160DOSEMPTY: con 16rE5; 1161DOSRUNES: con 13; 1162 1163FATRESRV: con 2; 1164 1165Oread: con 1; 1166Owrite: con 2; 1167Orclose: con 4; 1168Omodes: con 3; 1169 1170VERBOSE, STYX_MESS, FAT_INFO, CLUSTER_INFO: con (1 << iota); 1171 1172nowt, nowt1: int; 1173tzoff: int; 1174 1175# 1176# because we map all incoming short names from all upper to all lower case, 1177# and FAT cannot store mixed case names in short name form, 1178# we'll declare upper case as unacceptable to decide whether a long name 1179# is needed on output. thus, long names are always written in the case 1180# in the system call, and are always read back as written; short names 1181# are produced by the common case of writing all lower case letters 1182# 1183isdos := array[256] of { 1184 'a' to 'z' => 1, 'A' to 'Z' => 0, '0' to '9' => 1, 1185 ' ' => 1, '$' => 1, '%' => 1, '"' => 1, '-' => 1, '_' => 1, '@' => 1, 1186 '~' => 1, '`' => 1, '!' => 1, '(' => 1, ')' => 1, '{' => 1, '}' => 1, '^' => 1, 1187 '#' => 1, '&' => 1, 1188 * => 0 1189}; 1190 1191dossetup() 1192{ 1193 nowt = daytime->now(); 1194 nowt1 = sys->millisec(); 1195 tzoff = daytime->local(0).tzoff; 1196} 1197 1198# make xf into a Dos file system... or die trying to. 1199dosfs(xf: ref Xfs): int 1200{ 1201 mbroffset := 0; 1202 i: int; 1203 p: ref Iosect; 1204 1205Dmddo: 1206 for(;;) { 1207 for(i=2; i>0; i--) { 1208 p = getsect(xf, 0); 1209 if(p == nil) 1210 return -1; 1211 1212 if((mbroffset == 0) && (p.iobuf[0] == byte 16re9)) 1213 break; 1214 1215 # Check if the jump displacement (magic[1]) is too 1216 # short for a FAT. DOS 4.0 MBR has a displacement of 8. 1217 if(p.iobuf[0] == byte 16reb && 1218 p.iobuf[2] == byte 16r90 && 1219 p.iobuf[1] != byte 16r08) 1220 break; 1221 1222 if(i < 2 || 1223 p.iobuf[16r1fe] != byte 16r55 || 1224 p.iobuf[16r1ff] != byte 16raa) { 1225 i = 0; 1226 break; 1227 } 1228 1229 dp := 16r1be; 1230 for(j:=4; j>0; j--) { 1231 if(debug) { 1232 chat(sys->sprint("16r%2.2ux (%d,%d) 16r%2.2ux (%d,%d) %d %d...", 1233 int p.iobuf[dp], int p.iobuf[dp+1], 1234 bytes2short(p.iobuf[dp+2: dp+4]), 1235 int p.iobuf[dp+4], int p.iobuf[dp+5], 1236 bytes2short(p.iobuf[dp+6: dp+8]), 1237 bytes2int(p.iobuf[dp+8: dp+12]), 1238 bytes2int(p.iobuf[dp+12:dp+16]))); 1239 } 1240 1241 # Check for a disc-manager partition in the MBR. 1242 # Real MBR is at lba 63. Unfortunately it starts 1243 # with 16rE9, hence the check above against magic. 1244 if(int p.iobuf[dp+4] == DMDDO) { 1245 mbroffset = 63*Sectorsize; 1246 putsect(p); 1247 purgebuf(xf); 1248 xf.offset += mbroffset; 1249 break Dmddo; 1250 } 1251 1252 # Make sure it really is the right type, other 1253 # filesystems can look like a FAT 1254 # (e.g. OS/2 BOOT MANAGER). 1255 if(p.iobuf[dp+4] == FAT12 || 1256 p.iobuf[dp+4] == FAT16 || 1257 p.iobuf[dp+4] == FATHUGE) 1258 break; 1259 dp+=16; 1260 } 1261 1262 if(j <= 0) { 1263 if(debug) 1264 chat("no active partition..."); 1265 putsect(p); 1266 return -1; 1267 } 1268 1269 offset := bytes2int(p.iobuf[dp+8:dp+12])* Sectorsize; 1270 putsect(p); 1271 purgebuf(xf); 1272 xf.offset = mbroffset+offset; 1273 } 1274 break; 1275 } 1276 if(i <= 0) { 1277 if(debug) 1278 chat("bad magic..."); 1279 putsect(p); 1280 return -1; 1281 } 1282 1283 b := Dosboot.arr2Db(p.iobuf); 1284 if(debug & FAT_INFO) 1285 bootdump(b); 1286 1287 bp := ref Dosbpb; 1288 xf.ptr = bp; 1289 xf.fmt = 1; 1290 1291 bp.sectsize = bytes2short(b.sectsize); 1292 bp.clustsize = int b.clustsize; 1293 bp.nresrv = bytes2short(b.nresrv); 1294 bp.nfats = int b.nfats; 1295 bp.rootsize = bytes2short(b.rootsize); 1296 bp.volsize = bytes2short(b.volsize); 1297 if(bp.volsize == 0) 1298 bp.volsize = bytes2int(b.bigvolsize); 1299 bp.mediadesc = int b.mediadesc; 1300 bp.fatsize = bytes2short(b.fatsize); 1301 1302 bp.fataddr = int bp.nresrv; 1303 bp.rootaddr = bp.fataddr + bp.nfats*bp.fatsize; 1304 i = bp.rootsize*DOSDIRSIZE + bp.sectsize-1; 1305 i /= bp.sectsize; 1306 bp.dataaddr = bp.rootaddr + i; 1307 bp.fatclusters = FATRESRV+(bp.volsize - bp.dataaddr)/bp.clustsize; 1308 if(bp.fatclusters < 4087) 1309 bp.fatbits = 12; 1310 else 1311 bp.fatbits = 16; 1312 bp.freeptr = 2; 1313 if(debug & FAT_INFO){ 1314 chat(sys->sprint("fatbits=%d (%d clusters)...", 1315 bp.fatbits, bp.fatclusters)); 1316 for(i=0; i< int b.nfats; i++) 1317 chat(sys->sprint("fat %d: %d...", 1318 i, bp.fataddr+i*bp.fatsize)); 1319 chat(sys->sprint("root: %d...", bp.rootaddr)); 1320 chat(sys->sprint("data: %d...", bp.dataaddr)); 1321 } 1322 putsect(p); 1323 return 0; 1324} 1325 1326QIDPATH(dp: ref Dosptr): big 1327{ 1328 return big (dp.addr*(Sectorsize/DOSDIRSIZE) + dp.offset/DOSDIRSIZE); 1329} 1330 1331isroot(addr: int): int 1332{ 1333 return addr == 0; 1334} 1335 1336getfile(f: ref Xfile): int 1337{ 1338 dp := f.ptr; 1339 if(dp.p!=nil) 1340 panic("getfile"); 1341 if(dp.addr < 0) 1342 panic("getfile address"); 1343 p := getsect(f.xf, dp.addr); 1344 if(p == nil) 1345 return -1; 1346 1347 dp.d = nil; 1348 if(!isroot(dp.addr)) { 1349 if(f.qid.path != QIDPATH(dp)){ 1350 if(debug) { 1351 chat(sys->sprint("qid mismatch f=0x%x d=0x%x...", 1352 int f.qid.path, int QIDPATH(dp))); 1353 } 1354 putsect(p); 1355 return -1; 1356 } 1357 # dp.d = Dosdir.arr2Dd(p.iobuf[dp.offset:dp.offset+DOSDIRSIZE]); 1358 } 1359 dp.p = p; 1360 return 0; 1361} 1362 1363putfile(f: ref Xfile) 1364{ 1365 dp := f.ptr; 1366 if(dp.p==nil) 1367 panic("putfile"); 1368 putsect(dp.p); 1369 dp.p = nil; 1370 dp.d = nil; 1371} 1372 1373getstart(nil: ref Xfs, d: ref Dosdir): int 1374{ 1375 start := bytes2short(d.start); 1376# if(xf.isfat32) 1377# start |= bytes2short(d.hstart)<<16; 1378 return start; 1379} 1380 1381putstart(nil: ref Xfs, d: ref Dosdir, start: int) 1382{ 1383 d.start[0] = byte start; 1384 d.start[1] = byte (start>>8); 1385# if(xf.isfat32){ 1386# d.hstart[0] = start>>16; 1387# d.hstart[1] = start>>24; 1388# } 1389} 1390 1391# 1392# return the disk cluster for the iclust cluster in f 1393# 1394fileclust(f: ref Xfile, iclust: int, cflag: int): int 1395{ 1396 1397# bp := f.xf.ptr; 1398 dp := f.ptr; 1399 if(isroot(dp.addr)) 1400 return -1; # root directory for old FAT format does not start on a cluster boundary 1401 d := dp.d; 1402 if(d == nil){ 1403 if(dp.p == nil) 1404 panic("fileclust"); 1405 d = Dosdir.arr2Dd(dp.p.iobuf[dp.offset:dp.offset+DOSDIRSIZE]); 1406 } 1407 next := 0; 1408 start := getstart(f.xf, d); 1409 if(start == 0) { 1410 if(!cflag) 1411 return -1; 1412 start = falloc(f.xf); 1413 if(start <= 0) 1414 return -1; 1415 puttime(d); 1416 putstart(f.xf, d, start); 1417 dp.p.iobuf[dp.offset:] = Dosdir.Dd2arr(d); 1418 dp.p.flags |= BMOD; 1419 dp.clust = 0; 1420 } 1421 1422 clust, nskip: int; 1423 if(dp.clust == 0 || iclust < dp.iclust) { 1424 clust = start; 1425 nskip = iclust; 1426 } else { 1427 clust = dp.clust; 1428 nskip = iclust - dp.iclust; 1429 } 1430 1431 if(debug & CLUSTER_INFO && nskip > 0) 1432 chat(sys->sprint("clust %d, skip %d...", clust, nskip)); 1433 1434 if(clust <= 0) 1435 return -1; 1436 1437 if(nskip > 0) { 1438 while(--nskip >= 0) { 1439 next = getfat(f.xf, clust); 1440 if(debug & CLUSTER_INFO) 1441 chat(sys->sprint(".%d", next)); 1442 if(next <= 0){ 1443 if(!cflag) 1444 break; 1445 next = falloc(f.xf); 1446 if(next <= 0) 1447 return -1; 1448 putfat(f.xf, clust, next); 1449 } 1450 clust = next; 1451 } 1452 if(next <= 0) 1453 return -1; 1454 dp.clust = clust; 1455 dp.iclust = iclust; 1456 } 1457 if(debug & CLUSTER_INFO) 1458 chat(sys->sprint(" clust(%d)=0x%x...", iclust, clust)); 1459 return clust; 1460} 1461 1462# 1463# return the disk sector for the isect disk sector in f, 1464# allocating space if necessary and cflag is set 1465# 1466fileaddr(f: ref Xfile, isect: int, cflag: int): int 1467{ 1468 bp := f.xf.ptr; 1469 dp := f.ptr; 1470 if(isroot(dp.addr)) { 1471 if(isect*bp.sectsize >= bp.rootsize*DOSDIRSIZE) 1472 return -1; 1473 return bp.rootaddr + isect; 1474 } 1475 clust := fileclust(f, isect/bp.clustsize, cflag); 1476 if(clust < 0) 1477 return -1; 1478 return clust2sect(bp, clust) + isect%bp.clustsize; 1479} 1480 1481# 1482# look for a directory entry matching name 1483# always searches for long names which match a short name 1484# 1485# if creating (cflag is set), set address of available slot and allocate next cluster if necessary 1486# 1487searchdir(f: ref Xfile, name: string, cflag: int, lflag: int): (int, ref Dosptr) 1488{ 1489 xf := f.xf; 1490 bp := xf.ptr; 1491 addr1 := -1; 1492 addr2 := -1; 1493 prevaddr1 := -1; 1494 o1 := 0; 1495 dp := ref Dosptr(0,0,0,0,0,0,-1,-1,nil,nil); # prevaddr and naddr are -1 1496 dp.paddr = f.ptr.addr; 1497 dp.poffset = f.ptr.offset; 1498 islong :=0; 1499 buf := ""; 1500 1501 need := 1; 1502 if(lflag && cflag) 1503 need += name2de(name); 1504 if(!lflag) { 1505 name = name[0:8]+"."+name[8:11]; 1506 i := len name -1; 1507 while(i >= 0 && (name[i]==' ' || name[i] == '.')) 1508 i--; 1509 name = name[0:i+1]; 1510 } 1511 1512 addr := -1; 1513 prevaddr: int; 1514 have := 0; 1515 for(isect:=0;; isect++) { 1516 prevaddr = addr; 1517 addr = fileaddr(f, isect, cflag); 1518 if(addr < 0) 1519 break; 1520 p := getsect(xf, addr); 1521 if(p == nil) 1522 break; 1523 for(o:=0; o<bp.sectsize; o+=DOSDIRSIZE) { 1524 dattr := int p.iobuf[o+11]; 1525 dname0 := p.iobuf[o]; 1526 if(dname0 == byte 16r00) { 1527 if(debug) 1528 chat("end dir(0)..."); 1529 putsect(p); 1530 if(!cflag) 1531 return (-1, nil); 1532 1533 # 1534 # addr1 and o1 are the start of the dirs 1535 # addr2 is the optional second cluster used if the long name 1536 # entry does not fit within the addr1 cluster 1537 # have tells us the number of contiguous free dirs 1538 # starting at addr1.o1; need is the number needed to hold the long name 1539 # 1540 if(addr1 < 0){ 1541 addr1 = addr; 1542 prevaddr1 = prevaddr; 1543 o1 = o; 1544 } 1545 nleft := (bp.sectsize-o)/DOSDIRSIZE; 1546 if(addr2 < 0 && nleft+have < need){ 1547 addr2 = fileaddr(f, isect+1, cflag); 1548 if(addr2 < 0){ 1549 if(debug) 1550 chat("end dir(2)..."); 1551 return (-2, nil); 1552 } 1553 }else if(addr2 < 0) 1554 addr2 = addr; 1555 if(addr2 == addr1) 1556 addr2 = -1; 1557 if(debug) 1558 chat(sys->sprint("allocate addr1=%d,%d addr2=%d for %s nleft=%d have=%d need=%d", addr1, o1, addr2, name, nleft, have, need)); 1559 dp.addr = addr1; 1560 dp.offset = o1; 1561 dp.prevaddr = prevaddr1; 1562 dp.naddr = addr2; 1563 return (0, dp); 1564 } 1565 1566 if(dname0 == byte DOSEMPTY) { 1567 if(debug) 1568 chat("empty..."); 1569 have++; 1570 if(addr1 == -1){ 1571 addr1 = addr; 1572 o1 = o; 1573 prevaddr1 = prevaddr; 1574 } 1575 if(addr2 == -1 && have >= need) 1576 addr2 = addr; 1577 continue; 1578 } 1579 have = 0; 1580 if(addr2 == -1) 1581 addr1 = -1; 1582 1583 if(0 && lflag && debug) 1584 dirdump(p.iobuf[o:o+DOSDIRSIZE],addr,o); 1585 1586 if((dattr & DMLONG) == DLONG) { 1587 if(!islong) 1588 buf = ""; 1589 islong = 1; 1590 buf = getnamesect(p.iobuf[o:o+DOSDIRSIZE]) + buf; # getnamesect should return sum 1591 continue; 1592 } 1593 if(dattr & DVLABEL) { 1594 islong = 0; 1595 continue; 1596 } 1597 1598 if(!islong || !lflag) 1599 buf = getname(p.iobuf[o:o+DOSDIRSIZE]); 1600 islong = 0; 1601 1602 if(debug) 1603 chat(sys->sprint("cmp: [%s] [%s]", buf, name)); 1604 if(mystrcmp(buf, name) != 0) { 1605 buf=""; 1606 continue; 1607 } 1608 if(debug) 1609 chat("found\n"); 1610 1611 if(cflag) { 1612 putsect(p); 1613 return (-1,nil); 1614 } 1615 1616 dp.addr = addr; 1617 dp.prevaddr = prevaddr; 1618 dp.offset = o; 1619 dp.p = p; 1620 #dp.d = Dosdir.arr2Dd(p.iobuf[o:o+DOSDIRSIZE]); 1621 return (0, dp); 1622 } 1623 putsect(p); 1624 } 1625 if(debug) 1626 chat("end dir(1)..."); 1627 if(!cflag) 1628 return (-1, nil); 1629 # 1630 # end of root directory or end of non-root directory on cluster boundary 1631 # 1632 if(addr1 < 0){ 1633 addr1 = fileaddr(f, isect, 1); 1634 if(addr1 < 0) 1635 return (-2, nil); 1636 prevaddr1 = prevaddr; 1637 o1 = 0; 1638 }else{ 1639 if(addr2 < 0 && have < need){ 1640 addr2 = fileaddr(f, isect, 1); 1641 if(addr2 < 0) 1642 return (-2, nil); 1643 } 1644 } 1645 if(addr2 == addr1) 1646 addr2 = -1; 1647 dp.addr = addr1; 1648 dp.offset = o1; 1649 dp.prevaddr = prevaddr1; 1650 dp.naddr = addr2; 1651 return (0, dp); 1652} 1653 1654emptydir(f: ref Xfile): int 1655{ 1656 for(isect:=0;; isect++) { 1657 addr := fileaddr(f, isect, 0); 1658 if(addr < 0) 1659 break; 1660 1661 p := getsect(f.xf, addr); 1662 if(p == nil) 1663 return -1; 1664 1665 for(o:=0; o<f.xf.ptr.sectsize; o+=DOSDIRSIZE) { 1666 dname0 := p.iobuf[o]; 1667 dattr := int p.iobuf[o+11]; 1668 1669 if(dname0 == byte 16r00) { 1670 putsect(p); 1671 return 0; 1672 } 1673 1674 if(dname0 == byte DOSEMPTY || dname0 == byte '.') 1675 continue; 1676 1677 if(dattr & DVLABEL) 1678 continue; # ignore any long name entries: it's empty if there are no short ones 1679 1680 putsect(p); 1681 return -1; 1682 } 1683 putsect(p); 1684 } 1685 return 0; 1686} 1687 1688readdir(f:ref Xfile, offset: int, count: int): (int, array of byte) 1689{ 1690 xf := f.xf; 1691 bp := xf.ptr; 1692 rcnt := 0; 1693 buf := array[Styx->MAXFDATA] of byte; 1694 islong :=0; 1695 longnamebuf:=""; 1696 1697 if(count <= 0) 1698 return (0, nil); 1699 1700Read: 1701 for(isect:=0;; isect++) { 1702 addr := fileaddr(f, isect, 0); 1703 if(addr < 0) 1704 break; 1705 p := getsect(xf, addr); 1706 if(p == nil) 1707 return (-1,nil); 1708 1709 for(o:=0; o<bp.sectsize; o+=DOSDIRSIZE) { 1710 dname0 := int p.iobuf[o]; 1711 dattr := int p.iobuf[o+11]; 1712 1713 if(dname0 == 16r00) { 1714 putsect(p); 1715 break Read; 1716 } 1717 1718 if(dname0 == DOSEMPTY) 1719 continue; 1720 1721 if(dname0 == '.') { 1722 dname1 := int p.iobuf[o+1]; 1723 if(dname1 == ' ' || dname1 == 0) 1724 continue; 1725 dname2 := int p.iobuf[o+2]; 1726 if(dname1 == '.' && 1727 (dname2 == ' ' || dname2 == 0)) 1728 continue; 1729 } 1730 1731 if((dattr & DMLONG) == DLONG) { 1732 if(!islong) 1733 longnamebuf = ""; 1734 longnamebuf = getnamesect(p.iobuf[o:o+DOSDIRSIZE]) + longnamebuf; 1735 islong = 1; 1736 continue; 1737 } 1738 if(dattr & DVLABEL) { 1739 islong = 0; 1740 continue; 1741 } 1742 1743 dir := getdir(p.iobuf[o:o+DOSDIRSIZE], addr, o); 1744 if(islong) { 1745 dir.name = longnamebuf; 1746 longnamebuf = ""; 1747 islong = 0; 1748 } 1749 d := styx->packdir(*dir); 1750 if(offset > 0) { 1751 offset -= len d; 1752 islong = 0; 1753 continue; 1754 } 1755 if(rcnt+len d > count){ 1756 putsect(p); 1757 break Read; 1758 } 1759 buf[rcnt:] = d; 1760 rcnt += len d; 1761 if(rcnt >= count) { 1762 putsect(p); 1763 break Read; 1764 } 1765 } 1766 putsect(p); 1767 } 1768 1769 return (rcnt, buf[0:rcnt]); 1770} 1771 1772walkup(f: ref Xfile): (int, ref Dosptr) 1773{ 1774 bp := f.xf.ptr; 1775 dp := f.ptr; 1776 o: int; 1777 ndp:= ref Dosptr(0,0,0,0,0,0,-1,-1,nil,nil); 1778 ndp.addr = dp.paddr; 1779 ndp.offset = dp.poffset; 1780 1781 if(debug) 1782 chat(sys->sprint("walkup: paddr=0x%x...", dp.paddr)); 1783 1784 if(dp.paddr == 0) 1785 return (0,ndp); 1786 1787 p := getsect(f.xf, dp.paddr); 1788 if(p == nil) 1789 return (-1,nil); 1790 1791 if(debug) 1792 dirdump(p.iobuf[dp.poffset:dp.poffset+DOSDIRSIZE],dp.paddr,dp.poffset); 1793 1794 xd := Dosdir.arr2Dd(p.iobuf[dp.poffset:dp.poffset+DOSDIRSIZE]); 1795 start := getstart(f.xf, xd); 1796 if(debug & CLUSTER_INFO) 1797 if(debug) 1798 chat(sys->sprint("start=0x%x...", start)); 1799 putsect(p); 1800 if(start == 0) 1801 return (-1,nil); 1802 1803 # 1804 # check that parent's . points to itself 1805 # 1806 p = getsect(f.xf, bp.dataaddr + (start-2)*bp.clustsize); 1807 if(p == nil) 1808 return (-1,nil); 1809 1810 if(debug) 1811 dirdump(p.iobuf,0,0); 1812 1813 xd = Dosdir.arr2Dd(p.iobuf); 1814 if(p.iobuf[0]!= byte '.' || 1815 p.iobuf[1]!= byte ' ' || 1816 start != getstart(f.xf, xd)) { 1817 if(p!=nil) 1818 putsect(p); 1819 return (-1,nil); 1820 } 1821 1822 if(debug) 1823 dirdump(p.iobuf[DOSDIRSIZE:],0,0); 1824 1825 # 1826 # parent's .. is the next entry, and has start of parent's parent 1827 # 1828 xd = Dosdir.arr2Dd(p.iobuf[DOSDIRSIZE:]); 1829 if(p.iobuf[32] != byte '.' || p.iobuf[33] != byte '.') { 1830 if(p != nil) 1831 putsect(p); 1832 return (-1,nil); 1833 } 1834 1835 # 1836 # we're done if parent is root 1837 # 1838 pstart := getstart(f.xf, xd); 1839 putsect(p); 1840 if(pstart == 0) 1841 return (0, ndp); 1842 1843 # 1844 # check that parent's . points to itself 1845 # 1846 p = getsect(f.xf, clust2sect(bp, pstart)); 1847 if(p == nil) { 1848 if(debug) 1849 chat(sys->sprint("getsect %d failed\n", pstart)); 1850 return (-1,nil); 1851 } 1852 if(debug) 1853 dirdump(p.iobuf,0,0); 1854 xd = Dosdir.arr2Dd(p.iobuf); 1855 if(p.iobuf[0]!= byte '.' || 1856 p.iobuf[1]!=byte ' ' || 1857 pstart!=getstart(f.xf, xd)) { 1858 if(p != nil) 1859 putsect(p); 1860 return (-1,nil); 1861 } 1862 1863 # 1864 # parent's parent's .. is the next entry, and has start of parent's parent's parent 1865 # 1866 if(debug) 1867 dirdump(p.iobuf[DOSDIRSIZE:],0,0); 1868 1869 xd = Dosdir.arr2Dd(p.iobuf[DOSDIRSIZE:]); 1870 if(xd.name[0] != '.' || xd.name[1] != '.') { 1871 if(p != nil) 1872 putsect(p); 1873 return (-1,nil); 1874 } 1875 ppstart :=getstart(f.xf, xd); 1876 putsect(p); 1877 1878 # 1879 # open parent's parent's parent, and walk through it until parent's paretn is found 1880 # need this to find parent's parent's addr and offset 1881 # 1882 ppclust := ppstart; 1883 # TO DO: FAT32 1884 if(ppclust != 0) 1885 k := clust2sect(bp, ppclust); 1886 else 1887 k = bp.rootaddr; 1888 p = getsect(f.xf, k); 1889 if(p == nil) { 1890 if(debug) 1891 chat(sys->sprint("getsect %d failed\n", k)); 1892 return (-1,nil); 1893 } 1894 1895 if(debug) 1896 dirdump(p.iobuf,0,0); 1897 1898 if(ppstart) { 1899 xd = Dosdir.arr2Dd(p.iobuf); 1900 if(p.iobuf[0]!= byte '.' || 1901 p.iobuf[1]!= byte ' ' || 1902 ppstart!=getstart(f.xf, xd)) { 1903 if(p!=nil) 1904 putsect(p); 1905 return (-1,nil); 1906 } 1907 } 1908 1909 for(so:=1; ;so++) { 1910 for(o=0; o<bp.sectsize; o+=DOSDIRSIZE) { 1911 xdname0 := p.iobuf[o]; 1912 if(xdname0 == byte 16r00) { 1913 if(debug) 1914 chat("end dir\n"); 1915 if(p != nil) 1916 putsect(p); 1917 return (-1,nil); 1918 } 1919 1920 if(xdname0 == byte DOSEMPTY) 1921 continue; 1922 1923 #xd = Dosdir.arr2Dd(p.iobuf[o:o+DOSDIRSIZE]); 1924 xdstart:= p.iobuf[o+26:o+28]; # TO DO: getstart 1925 if(bytes2short(xdstart) == pstart) { 1926 putsect(p); 1927 ndp.paddr = k; 1928 ndp.poffset = o; 1929 return (0,ndp); 1930 } 1931 } 1932 if(ppclust) { 1933 if(so%bp.clustsize == 0) { 1934 ppstart = getfat(f.xf, ppstart); 1935 if(ppstart < 0){ 1936 if(debug) 1937 chat(sys->sprint("getfat %d fail\n", 1938 ppstart)); 1939 if(p != nil) 1940 putsect(p); 1941 return (-1,nil); 1942 } 1943 } 1944 k = clust2sect(bp, ppclust) + 1945 so%bp.clustsize; 1946 } 1947 else { 1948 if(so*bp.sectsize >= bp.rootsize*DOSDIRSIZE) { 1949 if(p != nil) 1950 putsect(p); 1951 return (-1,nil); 1952 } 1953 k = bp.rootaddr + so; 1954 } 1955 putsect(p); 1956 p = getsect(f.xf, k); 1957 if(p == nil) { 1958 if(debug) 1959 chat(sys->sprint("getsect %d failed\n", k)); 1960 return (-1,nil); 1961 } 1962 } 1963 putsect(p); 1964 ndp.paddr = k; 1965 ndp.poffset = o; 1966 return (0,ndp); 1967} 1968 1969readfile(f: ref Xfile, offset: int, count: int): (int, array of byte) 1970{ 1971 xf := f.xf; 1972 bp := xf.ptr; 1973 dp := f.ptr; 1974 1975 length := bytes2int(dp.p.iobuf[dp.offset+28:dp.offset+32]); 1976 rcnt := 0; 1977 if(offset >= length) 1978 return (0,nil); 1979 buf := array[Styx->MAXFDATA] of byte; 1980 if(offset+count >= length) 1981 count = length - offset; 1982 isect := offset/bp.sectsize; 1983 o := offset%bp.sectsize; 1984 while(count > 0) { 1985 addr := fileaddr(f, isect++, 0); 1986 if(addr < 0) 1987 break; 1988 c := bp.sectsize - o; 1989 if(c > count) 1990 c = count; 1991 p := getsect(xf, addr); 1992 if(p == nil) 1993 return (-1, nil); 1994 buf[rcnt:] = p.iobuf[o:o+c]; 1995 putsect(p); 1996 count -= c; 1997 rcnt += c; 1998 o = 0; 1999 } 2000 return (rcnt, buf[0:rcnt]); 2001} 2002 2003writefile(f: ref Xfile, buf: array of byte, offset,count: int): int 2004{ 2005 xf := f.xf; 2006 bp := xf.ptr; 2007 dp := f.ptr; 2008 addr := 0; 2009 c: int; 2010 rcnt := 0; 2011 p: ref Iosect; 2012 2013 d := dp.d; 2014 if(d == nil) 2015 d = Dosdir.arr2Dd(dp.p.iobuf[dp.offset:dp.offset+DOSDIRSIZE]); 2016 isect := offset/bp.sectsize; 2017 2018 o := offset%bp.sectsize; 2019 while(count > 0) { 2020 addr = fileaddr(f, isect++, 1); 2021 if(addr < 0) 2022 break; 2023 c = bp.sectsize - o; 2024 if(c > count) 2025 c = count; 2026 if(c == bp.sectsize){ 2027 p = getosect(xf, addr); 2028 if(p == nil) 2029 return -1; 2030 p.flags = 0; 2031 }else{ 2032 p = getsect(xf, addr); 2033 if(p == nil) 2034 return -1; 2035 } 2036 p.iobuf[o:] = buf[rcnt:rcnt+c]; 2037 p.flags |= BMOD; 2038 putsect(p); 2039 count -= c; 2040 rcnt += c; 2041 o = 0; 2042 } 2043 if(rcnt <= 0 && addr < 0) 2044 return -2; 2045 length := 0; 2046 dlen := bytes2int(d.length); 2047 if(rcnt > 0) 2048 length = offset+rcnt; 2049 else if(dp.addr && dp.clust) { 2050 c = bp.clustsize*bp.sectsize; 2051 if(dp.iclust > (dlen+c-1)/c) 2052 length = c*dp.iclust; 2053 } 2054 if(length > dlen) { 2055 d.length[0] = byte length; 2056 d.length[1] = byte (length>>8); 2057 d.length[2] = byte (length>>16); 2058 d.length[3] = byte (length>>24); 2059 } 2060 puttime(d); 2061 dp.p.flags |= BMOD; 2062 dp.p.iobuf[dp.offset:] = Dosdir.Dd2arr(d); 2063 return rcnt; 2064} 2065 2066truncfile(f: ref Xfile): int 2067{ 2068 xf := f.xf; 2069# bp := xf.ptr; 2070 dp := f.ptr; 2071 d := Dosdir.arr2Dd(dp.p.iobuf[dp.offset:dp.offset+DOSDIRSIZE]); 2072 2073 clust := getstart(f.xf, d); 2074 putstart(f.xf, d, 0); 2075 while(clust > 0) { 2076 next := getfat(xf, clust); 2077 putfat(xf, clust, 0); 2078 clust = next; 2079 } 2080 2081 d.length[0] = byte 0; 2082 d.length[1] = byte 0; 2083 d.length[2] = byte 0; 2084 d.length[3] = byte 0; 2085 2086 dp.p.iobuf[dp.offset:] = Dosdir.Dd2arr(d); 2087 dp.iclust = 0; 2088 dp.clust = 0; 2089 dp.p.flags |= BMOD; 2090 2091 return 0; 2092} 2093 2094getdir(arr: array of byte, addr,offset: int) :ref Sys->Dir 2095{ 2096 dp := ref Sys->Dir; 2097 2098 if(arr == nil || addr == 0) { 2099 dp.name = ""; 2100 dp.qid.path = big 0; 2101 dp.qid.qtype = Sys->QTDIR; 2102 dp.length = big 0; 2103 dp.mode = Sys->DMDIR|8r777; 2104 } 2105 else { 2106 dp.name = getname(arr); 2107 for(i:=0; i < len dp.name; i++) 2108 if(dp.name[i]>='A' && dp.name[i]<='Z') 2109 dp.name[i] = dp.name[i]-'A'+'a'; 2110 2111 # dp.qid.path = bytes2short(d.start); 2112 dp.qid.path = big (addr*(Sectorsize/DOSDIRSIZE) + offset/DOSDIRSIZE); 2113 dattr := int arr[11]; 2114 2115 if(dattr & DRONLY) 2116 dp.mode = 8r444; 2117 else 2118 dp.mode = 8r666; 2119 2120 dp.atime = gtime(arr); 2121 dp.mtime = dp.atime; 2122 if(dattr & DDIR) { 2123 dp.length = big 0; 2124 dp.qid.qtype |= Styx->QTDIR; 2125 dp.mode |= Sys->DMDIR|8r111; 2126 } 2127 else 2128 dp.length = big bytes2int(arr[28:32]); 2129 2130 if(dattr & DSYSTEM){ 2131 dp.mode |= Styx->DMEXCL; 2132 dp.qid.qtype |= Styx->QTEXCL; 2133 } 2134 } 2135 2136 dp.qid.vers = 0; 2137 dp.dtype = 0; 2138 dp.dev = 0; 2139 dp.uid = "dos"; 2140 dp.gid = "srv"; 2141 2142 return dp; 2143} 2144 2145putdir(d: ref Dosdir, dp: ref Sys->Dir) 2146{ 2147 if(dp.mode & 2) 2148 d.attr &= byte ~DRONLY; 2149 else 2150 d.attr |= byte DRONLY; 2151 2152 if(dp.mode & Styx->DMEXCL) 2153 d.attr |= byte DSYSTEM; 2154 else 2155 d.attr &= byte ~DSYSTEM; 2156 xputtime(d, dp.mtime); 2157} 2158 2159getname(arr: array of byte): string 2160{ 2161 p: string; 2162 for(i:=0; i<8; i++) { 2163 c := int arr[i]; 2164 if(c == 0 || c == ' ') 2165 break; 2166 if(i == 0 && c == 16r05) 2167 c = 16re5; 2168 p[len p] = c; 2169 } 2170 for(i=8; i<11; i++) { 2171 c := int arr[i]; 2172 if(c == 0 || c == ' ') 2173 break; 2174 if(i == 8) 2175 p[len p] = '.'; 2176 p[len p] = c; 2177 } 2178 2179 return p; 2180} 2181 2182dosname(p: string): (string, string) 2183{ 2184 name := " "; 2185 for(i := 0; i < len p && i < 8; i++) { 2186 c := p[i]; 2187 if(c >= 'a' && c <= 'z') 2188 c += 'A'-'a'; 2189 else if(c == '.') 2190 break; 2191 name[i] = c; 2192 } 2193 ext := " "; 2194 for(j := len p - 1; j >= i; j--) { 2195 if(p[j] == '.') { 2196 q := 0; 2197 for(j++; j < len p && q < 3; j++) { 2198 c := p[j]; 2199 if(c >= 'a' && c <= 'z') 2200 c += 'A'-'a'; 2201 ext[q++] = c; 2202 } 2203 break; 2204 } 2205 } 2206 return (name, ext); 2207} 2208 2209putname(p: string, d: ref Dosdir) 2210{ 2211 if ((int d.attr & DLONG) == DLONG) 2212 panic("putname of long name"); 2213 (d.name, d.ext) = dosname(p); 2214} 2215 2216mystrcmp(s1, s2: string): int 2217{ 2218 n := len s1; 2219 if(n != len s2) 2220 return 1; 2221 2222 for(i := 0; i < n; i++) { 2223 c := s1[i]; 2224 if(c >= 'A' && c <= 'Z') 2225 c -= 'A'-'a'; 2226 d := s2[i]; 2227 if(d >= 'A' && d <= 'Z') 2228 d -= 'A'-'a'; 2229 if(c != d) 2230 return 1; 2231 } 2232 return 0; 2233} 2234 2235# 2236# return the length of a long name in directory 2237# entries or zero if it's normal dos 2238# 2239name2de(p: string): int 2240{ 2241 ext := 0; 2242 name := 0; 2243 2244 for(end := len p; --end >= 0 && p[end] != '.';) 2245 ext++; 2246 2247 if(end > 0) { 2248 name = end; 2249 for(i := 0; i < end; i++) { 2250 if(p[i] == '.') 2251 return (len p+DOSRUNES-1)/DOSRUNES; 2252 } 2253 } 2254 else { 2255 name = ext; 2256 ext = 0; 2257 } 2258 2259 if(name <= 8 && ext <= 3 && isvalidname(p)) 2260 return 0; 2261 2262 return (len p+DOSRUNES-1)/DOSRUNES; 2263} 2264 2265isvalidname(s: string): int 2266{ 2267 dot := 0; 2268 for(i := 0; i < len s; i++) 2269 if(s[i] == '.') { 2270 if(++dot > 1 || i == len s-1) 2271 return 0; 2272 } else if(s[i] > len isdos || isdos[s[i]] == 0) 2273 return 0; 2274 return 1; 2275} 2276 2277getnamesect(arr: array of byte): string 2278{ 2279 s: string; 2280 c: int; 2281 2282 for(i := 1; i < 11; i += 2) { 2283 c = int arr[i] | (int arr[i+1] << 8); 2284 if(c == 0) 2285 return s; 2286 s[len s] = c; 2287 } 2288 for(i = 14; i < 26; i += 2) { 2289 c = int arr[i] | (int arr[i+1] << 8); 2290 if(c == 0) 2291 return s; 2292 s[len s] = c; 2293 } 2294 for(i = 28; i < 32; i += 2) { 2295 c = int arr[i] | (int arr[i+1] << 8); 2296 if(c == 0) 2297 return s; 2298 s[len s] = c; 2299 } 2300 return s; 2301} 2302 2303# takes a long filename and converts to a short dos name, with a tag number. 2304long2short(src: string,val: int): string 2305{ 2306 dst :=" "; 2307 skip:=0; 2308 xskip:=0; 2309 ext:=len src-1; 2310 while(ext>=0 && src[ext]!='.') 2311 ext--; 2312 2313 if (ext < 0) 2314 ext=len src -1; 2315 2316 # convert name eliding periods 2317 j:=0; 2318 for(name := 0; name < ext && j<8; name++){ 2319 c := src[name]; 2320 if(c!='.' && c!=' ' && c!='\t') { 2321 if(c>='a' && c<='z') 2322 dst[j++] = c-'a'+'A'; 2323 else 2324 dst[j++] = c; 2325 } 2326 else 2327 skip++; 2328 } 2329 2330 # convert extension 2331 j=8; 2332 for(xname := ext+1; xname < len src && j<11; xname++) { 2333 c := src[xname]; 2334 if(c!=' ' && c!='\t'){ 2335 if (c>='a' && c<='z') 2336 dst[j++] = c-'a'+'A'; 2337 else 2338 dst[j++] = c; 2339 }else 2340 xskip++; 2341 } 2342 2343 # add tag number 2344 j =1; 2345 for(i:=val; i > 0; i/=10) 2346 j++; 2347 2348 if (8-j<name) 2349 name = 8-j; 2350 else 2351 name -= skip; 2352 2353 dst[name]='~'; 2354 for(; val > 0; val /= 10) 2355 dst[name+ --j] = (val%10)+'0'; 2356 2357 if(debug) 2358 chat(sys->sprint("returning dst [%s] src [%s]\n",dst,src)); 2359 2360 return dst; 2361} 2362 2363getfat(xf: ref Xfs, n: int): int 2364{ 2365 bp := xf.ptr; 2366 k := 0; 2367 2368 if(n < 2 || n >= bp.fatclusters) 2369 return -1; 2370 fb := bp.fatbits; 2371 k = (fb*n) >> 3; 2372 if(k < 0 || k >= bp.fatsize*bp.sectsize) 2373 panic("getfat"); 2374 2375 sect := k/bp.sectsize + bp.fataddr; 2376 o := k%bp.sectsize; 2377 p := getsect(xf, sect); 2378 if(p == nil) 2379 return -1; 2380 k = int p.iobuf[o++]; 2381 if(o >= bp.sectsize) { 2382 putsect(p); 2383 p = getsect(xf, sect+1); 2384 if(p == nil) 2385 return -1; 2386 o = 0; 2387 } 2388 k |= int p.iobuf[o++]<<8; 2389 if(fb == 32){ 2390 # fat32 is really fat28 2391 k |= int p.iobuf[o++] << 16; 2392 k |= (int p.iobuf[o] & 16r0F) << 24; 2393 fb = 28; 2394 } 2395 putsect(p); 2396 if(fb == 12) { 2397 if(n&1) 2398 k >>= 4; 2399 else 2400 k &= 16rfff; 2401 } 2402 2403 if(debug & FAT_INFO) 2404 chat(sys->sprint("fat(0x%x)=0x%x...", n, k)); 2405 2406 # 2407 # check for out of range 2408 # 2409 if(k >= (1<<fb) - 8) 2410 return -1; 2411 return k; 2412} 2413 2414putfat(xf: ref Xfs, n, val: int) 2415{ 2416 bp := xf.ptr; 2417 if(n < 2 || n >= bp.fatclusters) 2418 panic(sys->sprint("putfat n=%d", n)); 2419 k := (bp.fatbits*n) >> 3; 2420 if(k >= bp.fatsize*bp.sectsize) 2421 panic("putfat"); 2422 sect := k/bp.sectsize + bp.fataddr; 2423 for(; sect<bp.rootaddr; sect+=bp.fatsize) { 2424 o := k%bp.sectsize; 2425 p := getsect(xf, sect); 2426 if(p == nil) 2427 continue; 2428 case bp.fatbits { 2429 12 => 2430 if(n&1) { 2431 p.iobuf[o] &= byte 16r0f; 2432 p.iobuf[o++] |= byte (val<<4); 2433 if(o >= bp.sectsize) { 2434 p.flags |= BMOD; 2435 putsect(p); 2436 p = getsect(xf, sect+1); 2437 if(p == nil) 2438 continue; 2439 o = 0; 2440 } 2441 p.iobuf[o] = byte (val>>4); 2442 } 2443 else { 2444 p.iobuf[o++] = byte val; 2445 if(o >= bp.sectsize) { 2446 p.flags |= BMOD; 2447 putsect(p); 2448 p = getsect(xf, sect+1); 2449 if(p == nil) 2450 continue; 2451 o = 0; 2452 } 2453 p.iobuf[o] &= byte 16rf0; 2454 p.iobuf[o] |= byte ((val>>8)&16r0f); 2455 } 2456 16 => 2457 p.iobuf[o++] = byte val; 2458 p.iobuf[o] = byte (val>>8); 2459 32 => # fat32 is really fat28 2460 p.iobuf[o++] = byte val; 2461 p.iobuf[o++] = byte (val>>8); 2462 p.iobuf[o++] = byte (val>>16); 2463 p.iobuf[o] = byte ((int p.iobuf[o] & 16rF0) | ((val>>24) & 16r0F)); 2464 * => 2465 panic("putfat fatbits"); 2466 } 2467 2468 p.flags |= BMOD; 2469 putsect(p); 2470 } 2471} 2472 2473falloc(xf: ref Xfs): int 2474{ 2475 bp := xf.ptr; 2476 n := bp.freeptr; 2477 for(;;) { 2478 if(getfat(xf, n) == 0) 2479 break; 2480 if(++n >= bp.fatclusters) 2481 n = FATRESRV; 2482 if(n == bp.freeptr) 2483 return 0; 2484 } 2485 bp.freeptr = n+1; 2486 if(bp.freeptr >= bp.fatclusters) 2487 bp.freeptr = FATRESRV; 2488 putfat(xf, n, int 16rffffffff); 2489 k := clust2sect(bp, n); 2490 for(i:=0; i<bp.clustsize; i++) { 2491 p := getosect(xf, k+i); 2492 if(p == nil) 2493 return -1; 2494 for(j:=0; j<len p.iobuf; j++) 2495 p.iobuf[j] = byte 0; 2496 p.flags = BMOD; 2497 putsect(p); 2498 } 2499 return n; 2500} 2501 2502clust2sect(bp: ref Dosbpb, clust: int): int 2503{ 2504 return bp.dataaddr + (clust - FATRESRV)*bp.clustsize; 2505} 2506 2507sect2clust(bp: ref Dosbpb, sect: int): int 2508{ 2509 c := (sect - bp.dataaddr) / bp.clustsize + FATRESRV; 2510 # assert(sect == clust2sect(bp, c)); 2511 return c; 2512} 2513 2514bootdump(b: ref Dosboot) 2515{ 2516 chat(sys->sprint("magic: 0x%2.2x 0x%2.2x 0x%2.2x\n", 2517 int b.magic[0], int b.magic[1], int b.magic[2])); 2518 chat(sys->sprint("version: \"%8.8s\"\n", string b.version)); 2519 chat(sys->sprint("sectsize: %d\n", bytes2short(b.sectsize))); 2520 chat(sys->sprint("allocsize: %d\n", int b.clustsize)); 2521 chat(sys->sprint("nresrv: %d\n", bytes2short(b.nresrv))); 2522 chat(sys->sprint("nfats: %d\n", int b.nfats)); 2523 chat(sys->sprint("rootsize: %d\n", bytes2short(b.rootsize))); 2524 chat(sys->sprint("volsize: %d\n", bytes2short(b.volsize))); 2525 chat(sys->sprint("mediadesc: 0x%2.2x\n", int b.mediadesc)); 2526 chat(sys->sprint("fatsize: %d\n", bytes2short(b.fatsize))); 2527 chat(sys->sprint("trksize: %d\n", bytes2short(b.trksize))); 2528 chat(sys->sprint("nheads: %d\n", bytes2short(b.nheads))); 2529 chat(sys->sprint("nhidden: %d\n", bytes2int(b.nhidden))); 2530 chat(sys->sprint("bigvolsize: %d\n", bytes2int(b.bigvolsize))); 2531 chat(sys->sprint("driveno: %d\n", int b.driveno)); 2532 chat(sys->sprint("bootsig: 0x%2.2x\n", int b.bootsig)); 2533 chat(sys->sprint("volid: 0x%8.8x\n", bytes2int(b.volid))); 2534 chat(sys->sprint("label: \"%11.11s\"\n", string b.label)); 2535} 2536 2537xputtime(d: ref Dosdir, s: int) 2538{ 2539 if(s == 0) 2540 t := daytime->local((sys->millisec() - nowt1)/1000 + nowt); 2541 else 2542 t = daytime->local(s); 2543 x := (t.hour<<11) | (t.min<<5) | (t.sec>>1); 2544 d.time[0] = byte x; 2545 d.time[1] = byte (x>>8); 2546 x = ((t.year-80)<<9) | ((t.mon+1)<<5) | t.mday; 2547 d.date[0] = byte x; 2548 d.date[1] = byte (x>>8); 2549} 2550 2551puttime(d: ref Dosdir) 2552{ 2553 xputtime(d, 0); 2554} 2555 2556gtime(a: array of byte): int 2557{ 2558 tm := ref Daytime->Tm; 2559 i := bytes2short(a[22:24]); # dos time 2560 tm.hour = i >> 11; 2561 tm.min = (i>>5) & 63; 2562 tm.sec = (i & 31) << 1; 2563 i = bytes2short(a[24:26]); # dos date 2564 tm.year = 80 + (i>>9); 2565 tm.mon = ((i>>5) & 15) - 1; 2566 tm.mday = i & 31; 2567 tm.tzoff = tzoff; # DOS time is local time 2568 return daytime->tm2epoch(tm); 2569} 2570 2571dirdump(arr: array of byte, addr, offset: int) 2572{ 2573 if(!debug) 2574 return; 2575 attrchar:= "rhsvda67"; 2576 d := Dosdir.arr2Dd(arr); 2577 buf := sys->sprint("\"%.8s.%.3s\" ", d.name, d.ext); 2578 p_i:=7; 2579 2580 for(i := 16r80; i != 0; i >>= 1) { 2581 if((d.attr & byte i) == byte i) 2582 ch := attrchar[p_i]; 2583 else 2584 ch = '-'; 2585 buf += sys->sprint("%c", ch); 2586 p_i--; 2587 } 2588 2589 i = bytes2short(d.time); 2590 buf += sys->sprint(" %2.2d:%2.2d:%2.2d", i>>11, (i>>5)&63, (i&31)<<1); 2591 i = bytes2short(d.date); 2592 buf += sys->sprint(" %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31); 2593 buf += sys->sprint(" %d %d", bytes2short(d.start), bytes2short(d.length)); 2594 buf += sys->sprint(" %d %d\n",addr,offset); 2595 chat(buf); 2596} 2597 2598putnamesect(longname: string, curslot: int, first: int, sum: int, a: array of byte) 2599{ 2600 for(i := 0; i < DOSDIRSIZE; i++) 2601 a[i] = byte 16rFF; 2602 if(first) 2603 a[0] = byte (16r40 | curslot); 2604 else 2605 a[0] = byte curslot; 2606 a[11] = byte DLONG; 2607 a[12] = byte 0; 2608 a[13] = byte sum; 2609 a[26] = byte 0; 2610 a[27] = byte 0; 2611 # a[1:1+10] = characters 1 to 5 2612 n := len longname; 2613 j := (curslot-1)*DOSRUNES; 2614 for(i = 1; i < 1+10; i += 2){ 2615 c := 0; 2616 if(j < n) 2617 c = longname[j++]; 2618 a[i] = byte c; 2619 a[i+1] = byte (c >> 8); 2620 if(c == 0) 2621 return; 2622 } 2623 # a[14:14+12] = characters 6 to 11 2624 for(i = 14; i < 14+12; i += 2){ 2625 c := 0; 2626 if(j < n) 2627 c = longname[j++]; 2628 a[i] = byte c; 2629 a[i+1] = byte (c >> 8); 2630 if(c == 0) 2631 return; 2632 } 2633 # a[28:28+4] characters 12 to 13 2634 for(i = 28; i < 28+4; i += 2){ 2635 c := 0; 2636 if(j < n) 2637 c = longname[j++]; 2638 a[i] = byte c; 2639 a[i+1] = byte (c>>8); 2640 if(c == 0) 2641 return; 2642 } 2643} 2644 2645putlongname(xf: ref Xfs, ndp: ref Dosptr, name: string, sname: string): int 2646{ 2647 bp := xf.ptr; 2648 first := 1; 2649 sum := aliassum(sname); 2650 for(nds := (len name+DOSRUNES-1)/DOSRUNES; nds > 0; nds--) { 2651 putnamesect(name, nds, first, sum, ndp.p.iobuf[ndp.offset:]); 2652 first = 0; 2653 ndp.offset += DOSDIRSIZE; 2654 if(ndp.offset == bp.sectsize) { 2655 if(debug) 2656 chat(sys->sprint("long name %s entry %d/%d crossing sector, addr=%d, naddr=%d", name, nds, (len name+DOSRUNES-1)/DOSRUNES, ndp.addr, ndp.naddr)); 2657 ndp.p.flags |= BMOD; 2658 putsect(ndp.p); 2659 ndp.p = nil; 2660 ndp.d = nil; 2661 2662 # switch to the next cluster for the next long entry or the subsequent normal dir. entry 2663 # naddr must be set up correctly by searchdir because we'll need one or the other 2664 2665 ndp.prevaddr = ndp.addr; 2666 ndp.addr = ndp.naddr; 2667 ndp.naddr = -1; 2668 if(ndp.addr < 0) 2669 return -1; 2670 ndp.p = getsect(xf, ndp.addr); 2671 if(ndp.p == nil) 2672 return -1; 2673 ndp.offset = 0; 2674 } 2675 } 2676 return 0; 2677} 2678 2679bytes2int(a: array of byte): int 2680{ 2681 return (((((int a[3] << 8) | int a[2]) << 8) | int a[1]) << 8) | int a[0]; 2682} 2683 2684bytes2short(a: array of byte): int 2685{ 2686 return (int a[1] << 8) | int a[0]; 2687} 2688 2689chat(s: string) 2690{ 2691 if(debug) 2692 sys->fprint(sys->fildes(2), "%s", s); 2693} 2694 2695panic(s: string) 2696{ 2697 sys->fprint(sys->fildes(2), "dosfs: panic: %s\n", s); 2698 if(pflag) 2699 <-chan of int; # hang here 2700 raise "fail:panic"; 2701} 2702 2703Dosboot.arr2Db(arr: array of byte): ref Dosboot 2704{ 2705 db := ref Dosboot; 2706 db.magic = arr[0:3]; 2707 db.version = arr[3:11]; 2708 db.sectsize = arr[11:13]; 2709 db.clustsize = arr[13]; 2710 db.nresrv = arr[14:16]; 2711 db.nfats = arr[16]; 2712 db.rootsize = arr[17:19]; 2713 db.volsize = arr[19:21]; 2714 db.mediadesc = arr[21]; 2715 db.fatsize = arr[22:24]; 2716 db.trksize = arr[24:26]; 2717 db.nheads = arr[26:28]; 2718 db.nhidden = arr[28:32]; 2719 db.bigvolsize = arr[32:36]; 2720 db.driveno = arr[36]; 2721 db.bootsig = arr[38]; 2722 db.volid = arr[39:43]; 2723 db.label = arr[43:54]; 2724 return db; 2725} 2726 2727Dosdir.arr2Dd(arr: array of byte): ref Dosdir 2728{ 2729 dir := ref Dosdir; 2730 for(i := 0; i < 8; i++) 2731 dir.name[len dir.name] = int arr[i]; 2732 for(; i < 11; i++) 2733 dir.ext[len dir.ext] = int arr[i]; 2734 dir.attr = arr[11]; 2735 dir.reserved = arr[12:22]; 2736 dir.time = arr[22:24]; 2737 dir.date = arr[24:26]; 2738 dir.start = arr[26:28]; 2739 dir.length = arr[28:32]; 2740 return dir; 2741} 2742 2743Dosdir.Dd2arr(d: ref Dosdir): array of byte 2744{ 2745 a := array[32] of byte; 2746 i:=0; 2747 for(j := 0; j < len d.name; j++) 2748 a[i++] = byte d.name[j]; 2749 for(; j<8; j++) 2750 a[i++]= byte 0; 2751 for(j=0; j<len d.ext; j++) 2752 a[i++] = byte d.ext[j]; 2753 for(; j<3; j++) 2754 a[i++]= byte 0; 2755 a[i++] = d.attr; 2756 for(j=0; j<10; j++) 2757 a[i++] = d.reserved[j]; 2758 for(j=0; j<2; j++) 2759 a[i++] = d.time[j]; 2760 for(j=0; j<2; j++) 2761 a[i++] = d.date[j]; 2762 for(j=0; j<2; j++) 2763 a[i++] = d.start[j]; 2764 for(j=0; j<4; j++) 2765 a[i++] = d.length[j]; 2766 return a; 2767} 2768 2769# 2770# checksum of short name for use in long name directory entries 2771# assumes sname is already padded correctly to 8+3 2772# 2773aliassum(sname: string): int 2774{ 2775 i := 0; 2776 for(sum:=0; i<11; i++) 2777 sum = (((sum&1)<<7)|((sum&16rfe)>>1))+sname[i]; 2778 return sum; 2779} 2780 2781# 2782# track i/o 2783# 2784 2785# An Xfs represents the root of an external file system, anchored 2786# to the server and the client 2787Xfs: adt { 2788 next:cyclic ref Xfs; 2789 name: string; # of file containing external f.s. 2790 qid: Sys->Qid; # of file containing external f.s. 2791 refn: int; # attach count 2792 rootqid: Sys->Qid; # of inferno constructed root directory 2793 dev: ref Sys->FD; # FD of the file containing external f.s. 2794 fmt: int; # successfully read format 2795 offset: int; # offset in sectors to file system 2796 ptr: ref Dosbpb; 2797}; 2798 2799# An Xfile represents the mapping of fid's & qid's to the server. 2800Xfile: adt { 2801 next: cyclic ref Xfile; # in hash bucket 2802 client: int; 2803 fid: int; 2804 flags: int; 2805 qid: Sys->Qid; 2806 xf: ref Xfs; 2807 ptr: ref Dosptr; 2808}; 2809 2810Iosect: adt 2811{ 2812 next: cyclic ref Iosect; 2813 flags: int; 2814 t: cyclic ref Iotrack; 2815 iobuf: array of byte; 2816}; 2817 2818Iotrack: adt 2819{ 2820 flags: int; 2821 xf: ref Xfs; 2822 addr: int; 2823 next: cyclic ref Iotrack; # in lru list 2824 prev: cyclic ref Iotrack; 2825 hnext: cyclic ref Iotrack; # in hash list 2826 hprev: cyclic ref Iotrack; 2827 refn: int; 2828 tp: cyclic ref Track; 2829}; 2830 2831Track: adt 2832{ 2833 create: fn(): ref Track; 2834 p: cyclic array of ref Iosect; 2835 buf: array of byte; 2836}; 2837 2838BMOD: con 1<<0; 2839BIMM: con 1<<1; 2840BSTALE: con 1<<2; 2841 2842HIOB: con 31; # a prime 2843NIOBUF: con 20; 2844 2845Sectorsize: con 512; 2846Sect2trk: con 9; # default 2847 2848hiob := array[HIOB+1] of ref Iotrack; # hash buckets + lru list 2849iobuf := array[NIOBUF] of ref Iotrack; # the real ones 2850freelist: ref Iosect; 2851sect2trk := Sect2trk; 2852trksize := Sect2trk*Sectorsize; 2853 2854FIDMOD: con 127; # prime 2855xhead: ref Xfs; 2856client: int; 2857 2858xfiles := array[FIDMOD] of ref Xfile; 2859iodebug := 0; 2860 2861iotrackinit(sectors: int) 2862{ 2863 if(sectors <= 0) 2864 sectors = 9; 2865 sect2trk = sectors; 2866 trksize = sect2trk*Sectorsize; 2867 2868 freelist = nil; 2869 2870 for(i := 0;i < FIDMOD; i++) 2871 xfiles[i] = ref Xfile(nil,0,0,0,Sys->Qid(big 0,0,0),nil,nil); 2872 2873 for(i = 0; i <= HIOB; i++) 2874 hiob[i] = ref Iotrack; 2875 2876 for(i = 0; i < HIOB; i++) { 2877 hiob[i].hprev = hiob[i]; 2878 hiob[i].hnext = hiob[i]; 2879 hiob[i].refn = 0; 2880 hiob[i].addr = 0; 2881 } 2882 hiob[i].prev = hiob[i]; 2883 hiob[i].next = hiob[i]; 2884 hiob[i].refn = 0; 2885 hiob[i].addr = 0; 2886 2887 for(i=0;i<NIOBUF;i++) 2888 iobuf[i] = ref Iotrack; 2889 2890 for(i=0; i<NIOBUF; i++) { 2891 iobuf[i].hprev = iobuf[i].hnext = iobuf[i]; 2892 iobuf[i].prev = iobuf[i].next = iobuf[i]; 2893 iobuf[i].refn=iobuf[i].addr=0; 2894 iobuf[i].flags = 0; 2895 if(hiob[HIOB].next != iobuf[i]) { 2896 iobuf[i].prev.next = iobuf[i].next; 2897 iobuf[i].next.prev = iobuf[i].prev; 2898 iobuf[i].next = hiob[HIOB].next; 2899 iobuf[i].prev = hiob[HIOB]; 2900 hiob[HIOB].next.prev = iobuf[i]; 2901 hiob[HIOB].next = iobuf[i]; 2902 } 2903 iobuf[i].tp = Track.create(); 2904 } 2905} 2906 2907Track.create(): ref Track 2908{ 2909 t := ref Track; 2910 t.p = array[sect2trk] of ref Iosect; 2911 t.buf = array[trksize] of byte; 2912 return t; 2913} 2914 2915getsect(xf: ref Xfs, addr: int): ref Iosect 2916{ 2917 return getiosect(xf, addr, 1); 2918} 2919 2920getosect(xf: ref Xfs, addr: int): ref Iosect 2921{ 2922 return getiosect(xf, addr, 0); 2923} 2924 2925# get the sector corresponding to the address addr. 2926getiosect(xf: ref Xfs, addr , rflag: int): ref Iosect 2927{ 2928 # offset from beginning of track. 2929 toff := addr % sect2trk; 2930 2931 # address of beginning of track. 2932 taddr := addr - toff; 2933 t := getiotrack(xf, taddr); 2934 2935 if(rflag && t.flags&BSTALE) { 2936 if(tread(t) < 0) 2937 return nil; 2938 2939 t.flags &= ~BSTALE; 2940 } 2941 2942 t.refn++; 2943 if(t.tp.p[toff] == nil) { 2944 p := newsect(); 2945 t.tp.p[toff] = p; 2946 p.flags = t.flags&BSTALE; 2947 p.t = t; 2948 p.iobuf = t.tp.buf[toff*Sectorsize:(toff+1)*Sectorsize]; 2949 } 2950 return t.tp.p[toff]; 2951} 2952 2953putsect(p: ref Iosect) 2954{ 2955 t: ref Iotrack; 2956 2957 t = p.t; 2958 t.flags |= p.flags; 2959 p.flags = 0; 2960 t.refn--; 2961 if(t.refn < 0) 2962 panic("putsect: refcount"); 2963 2964 if(t.flags & BIMM) { 2965 if(t.flags & BMOD) 2966 twrite(t); 2967 t.flags &= ~(BMOD|BIMM); 2968 } 2969} 2970 2971# get the track corresponding to addr 2972# (which is the address of the beginning of a track 2973getiotrack(xf: ref Xfs, addr: int): ref Iotrack 2974{ 2975 p: ref Iotrack; 2976 mp := hiob[HIOB]; 2977 2978 if(iodebug) 2979 chat(sys->sprint("iotrack %d,%d...", xf.dev.fd, addr)); 2980 2981 # find bucket in hash table. 2982 h := (xf.dev.fd<<24) ^ addr; 2983 if(h < 0) 2984 h = ~h; 2985 h %= HIOB; 2986 hp := hiob[h]; 2987 2988 out: for(;;){ 2989 loop: for(;;) { 2990 # look for it in the active list 2991 for(p = hp.hnext; p != hp; p=p.hnext) { 2992 if(p.addr != addr || p.xf != xf) 2993 continue; 2994 if(p.addr == addr && p.xf == xf) { 2995 break out; 2996 } 2997 continue loop; 2998 } 2999 3000 # not found 3001 # take oldest unref'd entry 3002 for(p = mp.prev; p != mp; p=p.prev) 3003 if(p.refn == 0 ) 3004 break; 3005 if(p == mp) { 3006 if(iodebug) 3007 chat("iotrack all ref'd\n"); 3008 continue loop; 3009 } 3010 3011 if((p.flags & BMOD)!= 0) { 3012 twrite(p); 3013 p.flags &= ~(BMOD|BIMM); 3014 continue loop; 3015 } 3016 purgetrack(p); 3017 p.addr = addr; 3018 p.xf = xf; 3019 p.flags = BSTALE; 3020 break out; 3021 } 3022 } 3023 3024 if(hp.hnext != p) { 3025 p.hprev.hnext = p.hnext; 3026 p.hnext.hprev = p.hprev; 3027 p.hnext = hp.hnext; 3028 p.hprev = hp; 3029 hp.hnext.hprev = p; 3030 hp.hnext = p; 3031 } 3032 if(mp.next != p) { 3033 p.prev.next = p.next; 3034 p.next.prev = p.prev; 3035 p.next = mp.next; 3036 p.prev = mp; 3037 mp.next.prev = p; 3038 mp.next = p; 3039 } 3040 return p; 3041} 3042 3043purgetrack(t: ref Iotrack) 3044{ 3045 refn := sect2trk; 3046 for(i := 0; i < sect2trk; i++) { 3047 if(t.tp.p[i] == nil) { 3048 --refn; 3049 continue; 3050 } 3051 freesect(t.tp.p[i]); 3052 --refn; 3053 t.tp.p[i]=nil; 3054 } 3055 if(t.refn != refn) 3056 panic("purgetrack"); 3057 if(refn!=0) 3058 panic("refn not 0"); 3059} 3060 3061twrite(t: ref Iotrack): int 3062{ 3063 if(iodebug) 3064 chat(sys->sprint("[twrite %d...", t.addr)); 3065 3066 if((t.flags & BSTALE)!= 0) { 3067 refn:=0; 3068 for(i:=0; i<sect2trk; i++) 3069 if(t.tp.p[i]!=nil) 3070 ++refn; 3071 3072 if(refn < sect2trk) { 3073 if(tread(t) < 0) { 3074 if (iodebug) 3075 chat("error]"); 3076 return -1; 3077 } 3078 } 3079 else 3080 t.flags &= ~BSTALE; 3081 } 3082 3083 if(devwrite(t.xf, t.addr, t.tp.buf) < 0) { 3084 if(iodebug) 3085 chat("error]"); 3086 return -1; 3087 } 3088 3089 if(iodebug) 3090 chat(" done]"); 3091 3092 return 0; 3093} 3094 3095tread(t: ref Iotrack): int 3096{ 3097 refn := 0; 3098 rval: int; 3099 3100 for(i := 0; i < sect2trk; i++) 3101 if(t.tp.p[i] != nil) 3102 ++refn; 3103 3104 if(iodebug) 3105 chat(sys->sprint("[tread %d...", t.addr)); 3106 3107 tbuf := t.tp.buf; 3108 if(refn != 0) 3109 tbuf = array[trksize] of byte; 3110 3111 rval = devread(t.xf, t.addr, tbuf); 3112 if(rval < 0) { 3113 if(iodebug) 3114 chat("error]"); 3115 return -1; 3116 } 3117 3118 if(refn != 0) { 3119 for(i=0; i < sect2trk; i++) { 3120 if(t.tp.p[i] == nil) { 3121 t.tp.buf[i*Sectorsize:]=tbuf[i*Sectorsize:(i+1)*Sectorsize]; 3122 if(iodebug) 3123 chat(sys->sprint("%d ", i)); 3124 } 3125 } 3126 } 3127 3128 if(iodebug) 3129 chat("done]"); 3130 3131 t.flags &= ~BSTALE; 3132 return 0; 3133} 3134 3135purgebuf(xf: ref Xfs) 3136{ 3137 for(p := 0; p < NIOBUF; p++) { 3138 if(iobuf[p].xf != xf) 3139 continue; 3140 if(iobuf[p].xf == xf) { 3141 if((iobuf[p].flags & BMOD) != 0) 3142 twrite(iobuf[p]); 3143 3144 iobuf[p].flags = BSTALE; 3145 purgetrack(iobuf[p]); 3146 } 3147 } 3148} 3149 3150sync() 3151{ 3152 for(p := 0; p < NIOBUF; p++) { 3153 if(!(iobuf[p].flags & BMOD)) 3154 continue; 3155 3156 if(iobuf[p].flags & BMOD){ 3157 twrite(iobuf[p]); 3158 iobuf[p].flags &= ~(BMOD|BIMM); 3159 } 3160 } 3161} 3162 3163 3164newsect(): ref Iosect 3165{ 3166 if((p := freelist)!=nil) { 3167 freelist = p.next; 3168 p.next = nil; 3169 } else 3170 p = ref Iosect(nil, 0, nil,nil); 3171 3172 return p; 3173} 3174 3175freesect(p: ref Iosect) 3176{ 3177 p.next = freelist; 3178 freelist = p; 3179} 3180 3181 3182# devio from here 3183deverror(name: string, xf: ref Xfs, addr,n,nret: int): int 3184{ 3185 if(nret < 0) { 3186 if(iodebug) 3187 chat(sys->sprint("%s errstr=\"%r\"...", name)); 3188 xf.dev = nil; 3189 return -1; 3190 } 3191 if(iodebug) 3192 chat(sys->sprint("dev %d sector %d, %s: %d, should be %d\n", 3193 xf.dev.fd, addr, name, nret, n)); 3194 3195 panic(name); 3196 return -1; 3197} 3198 3199devread(xf: ref Xfs, addr: int, buf: array of byte): int 3200{ 3201 if(xf.dev==nil) 3202 return -1; 3203 3204 sys->seek(xf.dev, big (xf.offset+addr*Sectorsize), sys->SEEKSTART); 3205 nread := sys->read(xf.dev, buf, trksize); 3206 if(nread != trksize) 3207 return deverror("read", xf, addr, trksize, nread); 3208 3209 return 0; 3210} 3211 3212devwrite(xf: ref Xfs, addr: int, buf: array of byte): int 3213{ 3214 if(xf.dev == nil) 3215 return -1; 3216 3217 sys->seek(xf.dev, big (xf.offset+addr*Sectorsize), 0); 3218 nwrite := sys->write(xf.dev, buf, trksize); 3219 if(nwrite != trksize) 3220 return deverror("write", xf, addr, trksize , nwrite); 3221 3222 return 0; 3223} 3224 3225devcheck(xf: ref Xfs): int 3226{ 3227 buf := array[Sectorsize] of byte; 3228 3229 if(xf.dev == nil) 3230 return -1; 3231 3232 sys->seek(xf.dev, big 0, sys->SEEKSTART); 3233 if(sys->read(xf.dev, buf, Sectorsize) != Sectorsize){ 3234 xf.dev = nil; 3235 return -1; 3236 } 3237 3238 return 0; 3239} 3240 3241# setup and return the Xfs associated with "name" 3242 3243getxfs(name: string): (ref Xfs, string) 3244{ 3245 if(name == nil) 3246 return (nil, "no file system device specified"); 3247 3248 3249 # If the name passed is of the form 'name:offset' then 3250 # offset is used to prime xf->offset. This allows accessing 3251 # a FAT-based filesystem anywhere within a partition. 3252 # Typical use would be to mount a filesystem in the presence 3253 # of a boot manager programm at the beginning of the disc. 3254 3255 offset := 0; 3256 for(i := 0;i < len name; i++) 3257 if(name[i]==':') 3258 break; 3259 3260 if(i < len name) { 3261 offset = int name[i+1:]; 3262 if(offset < 0) 3263 return (nil, "invalid device offset to file system"); 3264 offset *= Sectorsize; 3265 name = name[0:i]; 3266 } 3267 3268 fd := sys->open(name, Sys->ORDWR); 3269 if(fd == nil) { 3270 if(iodebug) 3271 chat(sys->sprint("getxfs: open(%s) failed: %r\n", name)); 3272 return (nil, sys->sprint("can't open %s: %r", name)); 3273 } 3274 3275 (rval,dir) := sys->fstat(fd); 3276 if(rval < 0) 3277 return (nil, sys->sprint("can't stat %s: %r", name)); 3278 3279 # lock down the list of xf's. 3280 fxf: ref Xfs; 3281 for(xf := xhead; xf != nil; xf = xf.next) { 3282 if(xf.refn == 0) { 3283 if(fxf == nil) 3284 fxf = xf; 3285 continue; 3286 } 3287 if(xf.qid.path != dir.qid.path || xf.qid.vers != dir.qid.vers) 3288 continue; 3289 3290 if(xf.name!= name || xf.dev == nil) 3291 continue; 3292 3293 if(devcheck(xf) < 0) # look for media change 3294 continue; 3295 3296 if(offset && xf.offset != offset) 3297 continue; 3298 3299 if(iodebug) 3300 chat(sys->sprint("incref \"%s\", dev=%d...", 3301 xf.name, xf.dev.fd)); 3302 3303 ++xf.refn; 3304 return (xf, nil); 3305 } 3306 3307 # this xf doesn't exist, make a new one and stick it on the list. 3308 if(fxf == nil){ 3309 fxf = ref Xfs; 3310 fxf.next = xhead; 3311 xhead = fxf; 3312 } 3313 3314 if(iodebug) 3315 chat(sys->sprint("alloc \"%s\", dev=%d...", name, fd.fd)); 3316 3317 fxf.name = name; 3318 fxf.refn = 1; 3319 fxf.qid = dir.qid; 3320 fxf.dev = fd; 3321 fxf.fmt = 0; 3322 fxf.offset = offset; 3323 return (fxf, nil); 3324} 3325 3326refxfs(xf: ref Xfs, delta: int) 3327{ 3328 xf.refn += delta; 3329 if(xf.refn == 0) { 3330 if (iodebug) 3331 chat(sys->sprint("free \"%s\", dev=%d...", 3332 xf.name, xf.dev.fd)); 3333 3334 purgebuf(xf); 3335 if(xf.dev !=nil) 3336 xf.dev = nil; 3337 } 3338} 3339 3340xfile(fid, flag: int): ref Xfile 3341{ 3342 pf: ref Xfile; 3343 3344 # find hashed file list in LRU? table. 3345 k := (fid^client)%FIDMOD; 3346 3347 # find if this fid is in the hashed file list. 3348 f:=xfiles[k]; 3349 for(pf = nil; f != nil; f = f.next) { 3350 if(f.fid == fid && f.client == client) 3351 break; 3352 pf=f; 3353 } 3354 3355 # move this fid to the front of the list if it was further down. 3356 if(f != nil && pf != nil){ 3357 pf.next = f.next; 3358 f.next = xfiles[k]; 3359 xfiles[k] = f; 3360 } 3361 3362 case flag { 3363 * => 3364 panic("xfile"); 3365 Asis => 3366 if(f != nil && f.xf != nil && f.xf.dev == nil) 3367 return nil; 3368 return f; 3369 Clean => 3370 break; 3371 Clunk => 3372 if(f != nil) { 3373 xfiles[k] = f.next; 3374 clean(f); 3375 } 3376 return nil; 3377 } 3378 3379 # clean it up .. 3380 if(f != nil) 3381 return clean(f); 3382 3383 # f wasn't found in the hashtable, make a new one and add it 3384 f = ref Xfile; 3385 f.next = xfiles[k]; 3386 xfiles[k] = f; 3387 # sort out the fid, etc. 3388 f.fid = fid; 3389 f.client = client; 3390 f.flags = 0; 3391 f.qid = Sys->Qid(big 0, 0, Styx->QTFILE); 3392 f.xf = nil; 3393 f.ptr = ref Dosptr(0,0,0,0,0,0,-1,-1,nil,nil); 3394 return f; 3395} 3396 3397clean(f: ref Xfile): ref Xfile 3398{ 3399 f.ptr = nil; 3400 if(f.xf != nil) { 3401 refxfs(f.xf, -1); 3402 f.xf = nil; 3403 } 3404 f.flags = 0; 3405 f.qid = Sys->Qid(big 0, 0, 0); 3406 return f; 3407} 3408 3409# 3410# the file at <addr, offset> has moved 3411# relocate the dos entries of all fids in the same file 3412# 3413dosptrreloc(f: ref Xfile, dp: ref Dosptr, addr: int, offset: int) 3414{ 3415 i: int; 3416 p: ref Xfile; 3417 xdp: ref Dosptr; 3418 3419 for(i=0; i < FIDMOD; i++){ 3420 for(p = xfiles[i]; p != nil; p = p.next){ 3421 xdp = p.ptr; 3422 if(p != f && p.xf == f.xf 3423 && xdp != nil && xdp.addr == addr && xdp.offset == offset){ 3424 *xdp = *dp; 3425 xdp.p = nil; 3426 # xdp.d = nil; 3427 p.qid.path = big QIDPATH(xdp); 3428 } 3429 } 3430 } 3431} 3432