1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <auth.h> 5 #include <fcall.h> 6 #include "iotrack.h" 7 #include "dat.h" 8 #include "fns.h" 9 10 static uchar isdos[256]; 11 12 int 13 isdosfs(uchar *buf) 14 { 15 /* 16 * When dynamic disc managers move the disc partition, 17 * they make it start with 0xE9. 18 */ 19 if(buf[0] == 0xE9) 20 return 1; 21 22 /* 23 * Check if the jump displacement (magic[1]) is too short for a FAT. 24 * 25 * check now omitted due to digital cameras that use a 0 jump. 26 * the ecma-107 standard says this is okay and that interoperable fat 27 * implementations shouldn't assume this: 28 * http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-107.pdf, 29 * page 11. 30 */ 31 if(buf[0] == 0xEB && buf[2] == 0x90 /* && buf[1] >= 0x30 */) 32 return 1; 33 if(chatty) 34 fprint(2, "bad sig %.2ux %.2ux %.2uxn", buf[0], buf[1], buf[2]); 35 36 return 0; 37 } 38 39 int 40 dosfs(Xfs *xf) 41 { 42 Iosect *p, *p1; 43 Dosboot *b; 44 Fatinfo *fi; 45 Dosboot32 *b32; 46 Dosbpb *bp; 47 long fisec, extflags; 48 int i; 49 50 if(!isdos['a']){ 51 for(i = 'a'; i <= 'z'; i++) 52 isdos[i] = 1; 53 for(i = 'A'; i <= 'Z'; i++) 54 isdos[i] = 1; 55 for(i = '0'; i <= '9'; i++) 56 isdos[i] = 1; 57 isdos['$'] = 1; 58 isdos['%'] = 1; 59 isdos['''] = 1; 60 isdos['-'] = 1; 61 isdos['_'] = 1; 62 isdos['@'] = 1; 63 isdos['~'] = 1; 64 isdos['`'] = 1; 65 isdos['!'] = 1; 66 isdos['('] = 1; 67 isdos[')'] = 1; 68 isdos['{'] = 1; 69 isdos['}'] = 1; 70 isdos['^'] = 1; 71 isdos['#'] = 1; 72 isdos['&'] = 1; 73 } 74 75 p = getsect(xf, 0); 76 if(p == 0) 77 return -1; 78 79 b = (Dosboot*)p->iobuf; 80 if(b->clustsize == 0 || isdosfs(p->iobuf) == 0){ 81 putsect(p); 82 return -1; 83 } 84 85 bp = malloc(sizeof(Dosbpb)); 86 memset(bp, 0, sizeof(Dosbpb)); /* clear lock */ 87 xf->ptr = bp; 88 xf->fmt = 1; 89 90 bp->sectsize = GSHORT(b->sectsize); 91 bp->clustsize = b->clustsize; 92 bp->nresrv = GSHORT(b->nresrv); 93 bp->nfats = b->nfats; 94 bp->rootsize = GSHORT(b->rootsize); 95 bp->volsize = GSHORT(b->volsize); 96 if(bp->volsize == 0) 97 bp->volsize = GLONG(b->bigvolsize); 98 bp->mediadesc = b->mediadesc; 99 bp->fatsize = GSHORT(b->fatsize); 100 bp->fataddr = GSHORT(b->nresrv); 101 102 bp->fatinfo = 0; 103 104 if(bp->fatsize == 0){ /* is FAT32 */ 105 if(chatty) 106 bootsecdump32(2, xf, (Dosboot32*)b); 107 xf->isfat32 = 1; 108 b32 = (Dosboot32*)b; 109 bp->fatsize = GLONG(b32->fatsize32); 110 if(bp->fatsize == 0){ 111 putsect(p); 112 if(chatty) 113 fprint(2, "fatsize 0\n"); 114 return -1; 115 } 116 bp->dataaddr = bp->fataddr + bp->nfats*bp->fatsize; 117 bp->rootaddr = 0; 118 bp->rootstart = GLONG(b32->rootstart); 119 120 /* 121 * disable fat mirroring? 122 */ 123 extflags = GSHORT(b32->extflags); 124 if(extflags & 0x0080){ 125 for(i = 0; i < 4; i++){ 126 if(extflags & (1 << i)){ 127 bp->fataddr += i * bp->fatsize; 128 bp->nfats = 1; 129 break; 130 } 131 } 132 } 133 134 /* 135 * fat free list info 136 */ 137 bp->freeptr = FATRESRV; 138 fisec = GSHORT(b32->infospec); 139 if(fisec != 0 && fisec < GSHORT(b32->nresrv)){ 140 p1 = getsect(xf, fisec); 141 if(p1 != nil){ 142 fi = (Fatinfo*)p1->iobuf; 143 if(GLONG(fi->sig1) == FATINFOSIG1 && GLONG(fi->sig) == FATINFOSIG){ 144 bp->fatinfo = fisec; 145 bp->freeptr = GLONG(fi->nextfree); 146 bp->freeclusters = GLONG(fi->freeclust); 147 chat("fat info: %ld free clusters, next free %ld\n", bp->freeclusters, bp->freeptr); 148 } 149 putsect(p1); 150 } 151 } 152 }else{ 153 if(chatty) 154 bootdump(2, b); 155 bp->rootaddr = bp->fataddr + bp->nfats*bp->fatsize; 156 bp->rootstart = 0; 157 i = bp->rootsize*DOSDIRSIZE + bp->sectsize-1; 158 i /= bp->sectsize; 159 bp->dataaddr = bp->rootaddr + i; 160 bp->freeptr = FATRESRV; 161 } 162 bp->fatclusters = FATRESRV+(bp->volsize - bp->dataaddr)/bp->clustsize; 163 164 if(xf->isfat32) 165 bp->fatbits = 32; 166 else if(bp->fatclusters < 4087) 167 bp->fatbits = 12; 168 else 169 bp->fatbits = 16; 170 171 chat("fatbits=%d (%d clusters)...", bp->fatbits, bp->fatclusters); 172 for(i=0; i<b->nfats; i++) 173 chat("fat %d: %ld...", i, bp->fataddr+i*bp->fatsize); 174 chat("root: %ld...", bp->rootaddr); 175 chat("data: %ld...", bp->dataaddr); 176 putsect(p); 177 return 0; 178 } 179 180 /* 181 * initialize f to the root directory 182 * this file has no Dosdir entry, 183 * so we special case it all over. 184 */ 185 void 186 rootfile(Xfile *f) 187 { 188 Dosptr *dp; 189 190 dp = f->ptr; 191 memset(dp, 0, sizeof(Dosptr)); 192 dp->prevaddr = -1; 193 } 194 195 int 196 isroot(ulong addr) 197 { 198 return addr == 0; 199 } 200 201 int 202 getfile(Xfile *f) 203 { 204 Dosptr *dp; 205 Iosect *p; 206 207 dp = f->ptr; 208 if(dp->p) 209 panic("getfile"); 210 p = getsect(f->xf, dp->addr); 211 if(p == nil) 212 return -1; 213 214 /* 215 * we could also make up a Dosdir for the root 216 */ 217 dp->d = nil; 218 if(!isroot(dp->addr)){ 219 if(f->qid.path != QIDPATH(dp)){ 220 chat("qid mismatch f=%#llux d=%#lux...", f->qid.path, QIDPATH(dp)); 221 putsect(p); 222 errno = Enonexist; 223 return -1; 224 } 225 dp->d = (Dosdir *)&p->iobuf[dp->offset]; 226 } 227 dp->p = p; 228 return 0; 229 } 230 231 void 232 putfile(Xfile *f) 233 { 234 Dosptr *dp; 235 236 dp = f->ptr; 237 if(!dp->p) 238 panic("putfile"); 239 putsect(dp->p); 240 dp->p = nil; 241 dp->d = nil; 242 } 243 244 long 245 getstart(Xfs *xf, Dosdir *d) 246 { 247 long start; 248 249 start = GSHORT(d->start); 250 if(xf->isfat32) 251 start |= GSHORT(d->hstart)<<16; 252 return start; 253 } 254 255 void 256 putstart(Xfs *xf, Dosdir *d, long start) 257 { 258 PSHORT(d->start, start); 259 if(xf->isfat32) 260 PSHORT(d->hstart, start>>16); 261 } 262 263 /* 264 * return the disk cluster for the iclust cluster in f 265 */ 266 long 267 fileclust(Xfile *f, long iclust, int cflag) 268 { 269 Dosbpb *bp; 270 Dosptr *dp; 271 Dosdir *d; 272 long start, clust, nskip, next; 273 274 bp = f->xf->ptr; 275 dp = f->ptr; 276 d = dp->d; 277 next = 0; 278 279 /* 280 * asking for the cluster of the root directory 281 * is not a well-formed question, since the root directory 282 * does not begin on a cluster boundary. 283 */ 284 if(!f->xf->isfat32 && isroot(dp->addr)) 285 return -1; 286 287 if(f->xf->isfat32 && isroot(dp->addr)){ 288 start = bp->rootstart; 289 }else{ 290 start = getstart(f->xf, d); 291 if(start == 0){ 292 if(!cflag) 293 return -1; 294 mlock(bp); 295 start = falloc(f->xf); 296 unmlock(bp); 297 if(start <= 0) 298 return -1; 299 puttime(d, 0); 300 putstart(f->xf, d, start); 301 dp->p->flags |= BMOD; 302 dp->clust = 0; 303 } 304 } 305 if(dp->clust == 0 || iclust < dp->iclust){ 306 clust = start; 307 nskip = iclust; 308 }else{ 309 clust = dp->clust; 310 nskip = iclust - dp->iclust; 311 } 312 if(chatty > 1 && nskip > 0) 313 chat("clust %#lx, skip %ld...", clust, nskip); 314 if(clust <= 0) 315 return -1; 316 if(nskip > 0){ 317 mlock(bp); 318 while(--nskip >= 0){ 319 next = getfat(f->xf, clust); 320 if(chatty > 1) 321 chat("->%#lx", next); 322 if(next > 0){ 323 clust = next; 324 continue; 325 }else if(!cflag) 326 break; 327 if(d && (d->attr&DSYSTEM)){ 328 next = cfalloc(f); 329 if(next < 0) 330 break; 331 /* cfalloc will call putfat for us, since clust may change */ 332 } else { 333 next = falloc(f->xf); 334 if(next < 0) 335 break; 336 putfat(f->xf, clust, next); 337 } 338 clust = next; 339 } 340 unmlock(bp); 341 if(next <= 0) 342 return -1; 343 dp->clust = clust; 344 dp->iclust = iclust; 345 } 346 if(chatty > 1) 347 chat(" clust(%#lx)=%#lx...", iclust, clust); 348 return clust; 349 } 350 351 /* 352 * return the disk sector for the isect disk sector in f 353 */ 354 long 355 fileaddr(Xfile *f, long isect, int cflag) 356 { 357 Dosbpb *bp; 358 Dosptr *dp; 359 long clust; 360 361 bp = f->xf->ptr; 362 dp = f->ptr; 363 if(!f->xf->isfat32 && isroot(dp->addr)){ 364 if(isect*bp->sectsize >= bp->rootsize*DOSDIRSIZE) 365 return -1; 366 return bp->rootaddr + isect; 367 } 368 clust = fileclust(f, isect/bp->clustsize, cflag); 369 if(clust < 0) 370 return -1; 371 372 return clust2sect(bp, clust) + isect%bp->clustsize; 373 } 374 375 /* 376 * translate names 377 */ 378 void 379 fixname(char *buf) 380 { 381 int c; 382 char *p; 383 384 p = buf; 385 while(c = *p){ 386 if(c == ':' && trspaces) 387 *p = ' '; 388 p++; 389 } 390 } 391 392 /* 393 * classify the file name as one of 394 * Invalid - contains a bad character 395 * Short - short valid 8.3 name, no lowercase letters 396 * ShortLower - short valid 8.3 name except for lowercase letters 397 * Long - long name 398 */ 399 int 400 classifyname(char *buf) 401 { 402 char *p, *dot; 403 int c, isextended, is8dot3, islower, ndot; 404 405 p = buf; 406 isextended = 0; 407 islower = 0; 408 dot = nil; 409 ndot = 0; 410 while(c = (uchar)*p){ 411 if(c&0x80) /* UTF8 */ 412 isextended = 1; 413 else if(c == '.'){ 414 dot = p; 415 ndot++; 416 }else if(strchr("+,:;=[] ", c)) 417 isextended = 1; 418 else if(!isdos[c]) 419 return Invalid; 420 if('a' <= c && c <= 'z') 421 islower = 1; 422 p++; 423 } 424 425 is8dot3 = (ndot==0 && p-buf <= 8) || (ndot==1 && dot-buf <= 8 && p-(dot+1) <= 3); 426 427 if(!isextended && is8dot3){ 428 if(islower) 429 return ShortLower; 430 return Short; 431 } 432 return Long; 433 } 434 435 /* 436 * make an alias for a valid long file name 437 */ 438 void 439 mkalias(char *name, char *sname, int id) 440 { 441 Rune r; 442 char *s, *e, sid[10]; 443 int i, esuf, v; 444 445 e = strrchr(name, '.'); 446 if(e == nil) 447 e = strchr(name, '\0'); 448 449 s = name; 450 i = 0; 451 while(s < e && i < 6){ 452 if(isdos[(uchar)*s]) 453 sname[i++] = *s++; 454 else 455 s += chartorune(&r, s); 456 } 457 458 v = snprint(sid, 10, "%d", id); 459 if(i + 1 + v > 8) 460 i = 8 - 1 - v; 461 sname[i++] = '~'; 462 strcpy(&sname[i], sid); 463 i += v; 464 465 sname[i++] = '.'; 466 esuf = i + 3; 467 if(esuf > 12) 468 panic("bad mkalias"); 469 while(*e && i < esuf){ 470 if(isdos[(uchar)*e]) 471 sname[i++] = *e++; 472 else 473 e += chartorune(&r, e); 474 } 475 if(sname[i-1] == '.') 476 i--; 477 sname[i] = '\0'; 478 } 479 480 /* 481 * check for valid plan 9 names, 482 * rewrite ' ' to ':' 483 */ 484 char isfrog[256]={ 485 /*NUL*/ 1, 1, 1, 1, 1, 1, 1, 1, 486 /*BKS*/ 1, 1, 1, 1, 1, 1, 1, 1, 487 /*DLE*/ 1, 1, 1, 1, 1, 1, 1, 1, 488 /*CAN*/ 1, 1, 1, 1, 1, 1, 1, 1, 489 /* [' '] 1, let's try this -rsc */ 490 ['/'] 1, 491 [0x7f] 1, 492 }; 493 494 int 495 nameok(char *elem) 496 { 497 while(*elem) { 498 if(*elem == ' ' && trspaces) 499 *elem = ':'; 500 if(isfrog[*(uchar*)elem]) 501 return 0; 502 elem++; 503 } 504 return 1; 505 } 506 507 /* 508 * look for a directory entry matching name 509 * always searches for long names which match a short name 510 */ 511 int 512 searchdir(Xfile *f, char *name, Dosptr *dp, int cflag, int longtype) 513 { 514 Xfs *xf; 515 Iosect *p; 516 Dosbpb *bp; 517 Dosdir *d; 518 char buf[261], *bname; 519 int isect, addr, o, addr1, addr2, prevaddr, prevaddr1, o1, islong, have, need, sum; 520 521 xf = f->xf; 522 bp = xf->ptr; 523 addr1 = -1; 524 addr2 = -1; 525 prevaddr1 = -1; 526 o1 = 0; 527 islong = 0; 528 sum = -1; 529 530 need = 1; 531 if(longtype!=Short && cflag) 532 need += (utflen(name) + DOSRUNE-1) / DOSRUNE; 533 534 memset(dp, 0, sizeof(Dosptr)); 535 dp->prevaddr = -1; 536 dp->naddr = -1; 537 dp->paddr = ((Dosptr *)f->ptr)->addr; 538 dp->poffset = ((Dosptr *)f->ptr)->offset; 539 540 have = 0; 541 addr = -1; 542 bname = nil; 543 for(isect=0;; isect++){ 544 prevaddr = addr; 545 addr = fileaddr(f, isect, cflag); 546 if(addr < 0) 547 break; 548 p = getsect(xf, addr); 549 if(p == 0) 550 break; 551 for(o=0; o<bp->sectsize; o+=DOSDIRSIZE){ 552 d = (Dosdir *)&p->iobuf[o]; 553 if(d->name[0] == 0x00){ 554 chat("end dir(0)..."); 555 putsect(p); 556 if(!cflag) 557 return -1; 558 559 /* 560 * addr1 & o1 are the start of the dirs 561 * addr2 is the optional second cluster used if the long name 562 * entry does not fit within the addr1 cluster 563 * 564 * have tells us the number of contiguous free dirs 565 * starting at addr1.o1; need are necessary to hold the long name. 566 */ 567 if(addr1 < 0){ 568 addr1 = addr; 569 prevaddr1 = prevaddr; 570 o1 = o; 571 } 572 if(addr2 < 0 && (bp->sectsize-o)/DOSDIRSIZE + have < need){ 573 addr2 = fileaddr(f, isect+1, cflag); 574 if(addr2 < 0) 575 goto breakout; 576 }else if(addr2 < 0) 577 addr2 = addr; 578 if(addr2 == addr1) 579 addr2 = -1; 580 dp->addr = addr1; 581 dp->offset = o1; 582 dp->prevaddr = prevaddr1; 583 dp->naddr = addr2; 584 return 0; 585 } 586 if(d->name[0] == DOSEMPTY){ 587 if(chatty) 588 fprint(2, "empty dir\n"); 589 590 have++; 591 if(addr1 == -1){ 592 addr1 = addr; 593 o1 = o; 594 prevaddr1 = prevaddr; 595 } 596 if(addr2 == -1 && have >= need) 597 addr2 = addr; 598 continue; 599 } 600 have = 0; 601 if(addr2 == -1) 602 addr1 = -1; 603 604 dirdump(d); 605 if((d->attr & 0xf) == 0xf){ 606 bname = getnamesect(buf, bname, p->iobuf + o, &islong, &sum, 1); 607 continue; 608 } 609 if(d->attr & DVLABEL){ 610 islong = 0; 611 continue; 612 } 613 if(islong != 1 || sum != aliassum(d) || cistrcmp(bname, name) != 0){ 614 bname = buf; 615 getname(buf, d); 616 } 617 islong = 0; 618 if(cistrcmp(bname, name) != 0) 619 continue; 620 if(chatty) 621 fprint(2, "found\n"); 622 if(cflag){ 623 putsect(p); 624 return -1; 625 } 626 dp->addr = addr; 627 dp->prevaddr = prevaddr; 628 dp->offset = o; 629 dp->p = p; 630 dp->d = d; 631 return 0; 632 } 633 putsect(p); 634 } 635 breakout: 636 chat("end dir(1)..."); 637 return -1; 638 } 639 640 int 641 emptydir(Xfile *f) 642 { 643 Xfs *xf = f->xf; 644 Dosbpb *bp = xf->ptr; 645 int isect, addr, o; 646 Iosect *p; 647 Dosdir *d; 648 649 for(isect=0;; isect++){ 650 addr = fileaddr(f, isect, 0); 651 if(addr < 0) 652 break; 653 p = getsect(xf, addr); 654 if(p == 0) 655 return -1; 656 for(o=0; o<bp->sectsize; o+=DOSDIRSIZE){ 657 d = (Dosdir *)&p->iobuf[o]; 658 if(d->name[0] == 0x00){ 659 putsect(p); 660 return 0; 661 } 662 if(d->name[0] == DOSEMPTY) 663 continue; 664 if(d->name[0] == '.') 665 continue; 666 if(d->attr&DVLABEL) 667 continue; 668 putsect(p); 669 return -1; 670 } 671 putsect(p); 672 } 673 return 0; 674 } 675 676 long 677 readdir(Xfile *f, void *vbuf, long offset, long count) 678 { 679 Xfs *xf; 680 Dosbpb *bp; 681 Dir dir; 682 int isect, addr, o, islong, sum; 683 Iosect *p; 684 Dosdir *d; 685 long rcnt, n; 686 char *name, snamebuf[8+1+3+1], namebuf[DOSNAMELEN]; 687 uchar *buf; 688 689 buf = vbuf; 690 rcnt = 0; 691 xf = f->xf; 692 bp = xf->ptr; 693 if(count <= 0) 694 return 0; 695 islong = 0; 696 sum = -1; 697 name = nil; 698 for(isect=0;; isect++){ 699 addr = fileaddr(f, isect, 0); 700 if(addr < 0) 701 break; 702 p = getsect(xf, addr); 703 if(p == 0) 704 return -1; 705 for(o=0; o<bp->sectsize; o+=DOSDIRSIZE){ 706 d = (Dosdir *)&p->iobuf[o]; 707 if(d->name[0] == 0x00){ 708 putsect(p); 709 return rcnt; 710 } 711 if(d->name[0] == DOSEMPTY) 712 continue; 713 dirdump(d); 714 if(d->name[0] == '.'){ 715 if(d->name[1] == ' ' || d->name[1] == 0) 716 continue; 717 if(d->name[1] == '.' && 718 (d->name[2] == ' ' || d->name[2] == 0)) 719 continue; 720 } 721 if((d->attr & 0xf) == 0xf){ 722 name = getnamesect(namebuf, name, p->iobuf+o, &islong, &sum, 1); 723 continue; 724 } 725 if(d->attr & DVLABEL){ 726 islong = 0; 727 continue; 728 } 729 dir.name = snamebuf; 730 getdir(xf, &dir, d, addr, o); 731 if(islong == 1 && nameok(name) && sum == aliassum(d)) 732 dir.name = name; 733 islong = 0; 734 n = convD2M(&dir, &buf[rcnt], count - rcnt); 735 name = nil; 736 if(n <= BIT16SZ){ /* no room for next entry */ 737 putsect(p); 738 return rcnt; 739 } 740 rcnt += n; 741 if(offset > 0){ 742 offset -= rcnt; 743 rcnt = 0; 744 islong = 0; 745 continue; 746 } 747 if(rcnt == count){ 748 putsect(p); 749 return rcnt; 750 } 751 } 752 putsect(p); 753 } 754 return rcnt; 755 } 756 757 /* 758 * set up ndp for a directory's parent 759 * the hardest part is setting up paddr 760 */ 761 int 762 walkup(Xfile *f, Dosptr *ndp) 763 { 764 Dosbpb *bp; 765 Dosptr *dp; 766 Dosdir *xd; 767 Iosect *p; 768 long k, o, so, start, pstart, ppstart, st, ppclust; 769 770 bp = f->xf->ptr; 771 dp = f->ptr; 772 memset(ndp, 0, sizeof(Dosptr)); 773 ndp->prevaddr = -1; 774 ndp->naddr = -1; 775 ndp->addr = dp->paddr; 776 ndp->offset = dp->poffset; 777 778 chat("walkup: paddr=%#lx...", dp->paddr); 779 780 /* 781 * root's paddr is always itself 782 */ 783 if(isroot(dp->paddr)) 784 return 0; 785 786 /* 787 * find the start of our parent's directory 788 */ 789 p = getsect(f->xf, dp->paddr); 790 if(p == nil) 791 goto error; 792 xd = (Dosdir *)&p->iobuf[dp->poffset]; 793 dirdump(xd); 794 start = getstart(f->xf, xd); 795 chat("start=%#lx...", start); 796 if(start == 0) 797 goto error; 798 putsect(p); 799 800 /* 801 * verify that parent's . points to itself 802 */ 803 p = getsect(f->xf, clust2sect(bp, start)); 804 if(p == nil) 805 goto error; 806 xd = (Dosdir *)p->iobuf; 807 dirdump(xd); 808 st = getstart(f->xf, xd); 809 if(xd->name[0]!='.' || xd->name[1]!=' ' || start!=st) 810 goto error; 811 812 /* 813 * parent's .. is the next entry, and has start of parent's parent 814 */ 815 xd++; 816 dirdump(xd); 817 if(xd->name[0] != '.' || xd->name[1] != '.') 818 goto error; 819 pstart = getstart(f->xf, xd); 820 putsect(p); 821 822 /* 823 * we're done if parent is root 824 */ 825 if(pstart == 0 || f->xf->isfat32 && pstart == bp->rootstart) 826 return 0; 827 828 /* 829 * verify that parent's . points to itself 830 */ 831 p = getsect(f->xf, clust2sect(bp, pstart)); 832 if(p == 0){ 833 chat("getsect %ld failed\n", pstart); 834 goto error; 835 } 836 xd = (Dosdir *)p->iobuf; 837 dirdump(xd); 838 st = getstart(f->xf, xd); 839 if(xd->name[0]!='.' || xd->name[1]!=' ' || pstart!=st) 840 goto error; 841 842 /* 843 * parent's parent's .. is the next entry, and has start of parent's parent's parent 844 */ 845 xd++; 846 dirdump(xd); 847 if(xd->name[0] != '.' || xd->name[1] != '.') 848 goto error; 849 ppstart = getstart(f->xf, xd); 850 putsect(p); 851 852 /* 853 * open parent's parent's parent, and walk through it until parent's parent is found 854 * need this to find parent's parent's addr and offset 855 */ 856 ppclust = ppstart; 857 if(f->xf->isfat32 && ppclust == 0){ 858 ppclust = bp->rootstart; 859 chat("ppclust 0, resetting to rootstart\n"); 860 } 861 k = ppclust ? clust2sect(bp, ppclust) : bp->rootaddr; 862 p = getsect(f->xf, k); 863 if(p == nil){ 864 chat("getsect %ld failed\n", k); 865 goto error; 866 } 867 xd = (Dosdir *)p->iobuf; 868 dirdump(xd); 869 if(ppstart){ 870 st = getstart(f->xf, xd); 871 if(xd->name[0]!='.' || xd->name[1]!=' ' || ppstart!=st) 872 goto error; 873 } 874 for(so=1;; so++){ 875 for(o=0; o<bp->sectsize; o+=DOSDIRSIZE){ 876 xd = (Dosdir *)&p->iobuf[o]; 877 if(xd->name[0] == 0x00){ 878 chat("end dir\n"); 879 goto error; 880 } 881 if(xd->name[0] == DOSEMPTY) 882 continue; 883 st = getstart(f->xf, xd); 884 if(st == pstart) 885 goto out; 886 } 887 if(ppclust){ 888 if(so%bp->clustsize == 0){ 889 mlock(bp); 890 ppclust = getfat(f->xf, ppclust); 891 unmlock(bp); 892 if(ppclust < 0){ 893 chat("getfat %ld failed\n", ppclust); 894 goto error; 895 } 896 } 897 k = clust2sect(bp, ppclust) + 898 so%bp->clustsize; 899 }else{ 900 if(so*bp->sectsize >= bp->rootsize*DOSDIRSIZE) 901 goto error; 902 k = bp->rootaddr + so; 903 } 904 putsect(p); 905 p = getsect(f->xf, k); 906 if(p == 0){ 907 chat("getsect %ld failed\n", k); 908 goto error; 909 } 910 } 911 out: 912 putsect(p); 913 ndp->paddr = k; 914 ndp->poffset = o; 915 return 0; 916 917 error: 918 if(p) 919 putsect(p); 920 return -1; 921 } 922 923 long 924 readfile(Xfile *f, void *vbuf, long offset, long count) 925 { 926 Xfs *xf = f->xf; 927 Dosbpb *bp = xf->ptr; 928 Dosptr *dp = f->ptr; 929 Dosdir *d = dp->d; 930 int isect, addr, o, c; 931 Iosect *p; 932 uchar *buf; 933 long length, rcnt; 934 935 rcnt = 0; 936 length = GLONG(d->length); 937 buf = vbuf; 938 if(offset >= length) 939 return 0; 940 if(offset+count >= length) 941 count = length - offset; 942 isect = offset/bp->sectsize; 943 o = offset%bp->sectsize; 944 while(count > 0){ 945 addr = fileaddr(f, isect++, 0); 946 if(addr < 0) 947 break; 948 c = bp->sectsize - o; 949 if(c > count) 950 c = count; 951 p = getsect(xf, addr); 952 if(p == 0) 953 return -1; 954 memmove(&buf[rcnt], &p->iobuf[o], c); 955 putsect(p); 956 count -= c; 957 rcnt += c; 958 o = 0; 959 } 960 return rcnt; 961 } 962 963 long 964 writefile(Xfile *f, void *vbuf, long offset, long count) 965 { 966 Xfs *xf = f->xf; 967 Dosbpb *bp = xf->ptr; 968 Dosptr *dp = f->ptr; 969 Dosdir *d = dp->d; 970 int isect, addr = 0, o, c; 971 Iosect *p; 972 uchar *buf; 973 long length, rcnt = 0, dlen; 974 975 buf = vbuf; 976 isect = offset/bp->sectsize; 977 o = offset%bp->sectsize; 978 while(count > 0){ 979 addr = fileaddr(f, isect++, 1); 980 if(addr < 0) 981 break; 982 c = bp->sectsize - o; 983 if(c > count) 984 c = count; 985 if(c == bp->sectsize){ 986 p = getosect(xf, addr); 987 p->flags = 0; 988 }else{ 989 p = getsect(xf, addr); 990 if(p == nil) 991 return -1; 992 } 993 memmove(&p->iobuf[o], &buf[rcnt], c); 994 p->flags |= BMOD; 995 putsect(p); 996 count -= c; 997 rcnt += c; 998 o = 0; 999 } 1000 if(rcnt <= 0 && addr < 0) 1001 return -1; 1002 length = 0; 1003 dlen = GLONG(d->length); 1004 if(rcnt > 0) 1005 length = offset+rcnt; 1006 else if(dp->addr && dp->clust){ 1007 c = bp->clustsize*bp->sectsize; 1008 if(dp->iclust > (dlen+c-1)/c) 1009 length = c*dp->iclust; 1010 } 1011 if(length > dlen) 1012 PLONG(d->length, length); 1013 puttime(d, 0); 1014 dp->p->flags |= BMOD; 1015 return rcnt; 1016 } 1017 1018 int 1019 truncfile(Xfile *f, long length) 1020 { 1021 Xfs *xf = f->xf; 1022 Dosbpb *bp = xf->ptr; 1023 Dosptr *dp = f->ptr; 1024 Dosdir *d = dp->d; 1025 long clust, next, n; 1026 1027 mlock(bp); 1028 clust = getstart(f->xf, d); 1029 n = length; 1030 if(n <= 0) 1031 putstart(f->xf, d, 0); 1032 else 1033 n -= bp->sectsize; 1034 while(clust > 0){ 1035 next = getfat(xf, clust); 1036 if(n <= 0) 1037 putfat(xf, clust, 0); 1038 else 1039 n -= bp->clustsize*bp->sectsize; 1040 clust = next; 1041 } 1042 unmlock(bp); 1043 PLONG(d->length, length); 1044 dp->iclust = 0; 1045 dp->clust = 0; 1046 dp->p->flags |= BMOD; 1047 return 0; 1048 } 1049 1050 void 1051 putdir(Dosdir *d, Dir *dp) 1052 { 1053 if(dp->mode != ~0){ 1054 if(dp->mode & 2) 1055 d->attr &= ~DRONLY; 1056 else 1057 d->attr |= DRONLY; 1058 if(dp->mode & DMEXCL) 1059 d->attr |= DSYSTEM; 1060 else 1061 d->attr &= ~DSYSTEM; 1062 } 1063 if(dp->mtime != ~0) 1064 puttime(d, dp->mtime); 1065 } 1066 1067 /* 1068 * should extend this to deal with 1069 * creation and access dates 1070 */ 1071 void 1072 getdir(Xfs *xfs, Dir *dp, Dosdir *d, int addr, int offset) 1073 { 1074 if(d == nil || addr == 0) 1075 panic("getdir on root"); 1076 dp->type = 0; 1077 dp->dev = 0; 1078 getname(dp->name, d); 1079 1080 dp->qid.path = addr*(Sectorsize/DOSDIRSIZE) + 1081 offset/DOSDIRSIZE; 1082 dp->qid.vers = 0; 1083 1084 if(d->attr & DRONLY) 1085 dp->mode = 0444; 1086 else 1087 dp->mode = 0666; 1088 dp->atime = gtime(d); 1089 dp->mtime = dp->atime; 1090 dp->qid.type = QTFILE; 1091 if(d->attr & DDIR){ 1092 dp->qid.type = QTDIR; 1093 dp->mode |= DMDIR|0111; 1094 dp->length = 0; 1095 }else 1096 dp->length = GLONG(d->length); 1097 if(d->attr & DSYSTEM){ 1098 dp->mode |= DMEXCL; 1099 if(iscontig(xfs, d)) 1100 dp->mode |= DMAPPEND; 1101 } 1102 1103 dp->uid = "bill"; 1104 dp->muid = "bill"; 1105 dp->gid = "trog"; 1106 } 1107 1108 void 1109 getname(char *p, Dosdir *d) 1110 { 1111 int c, i; 1112 1113 for(i=0; i<8; i++){ 1114 c = d->name[i]; 1115 if(c == '\0' || c == ' ') 1116 break; 1117 if(i == 0 && c == 0x05) 1118 c = 0xe5; 1119 *p++ = c; 1120 } 1121 for(i=0; i<3; i++){ 1122 c = d->ext[i]; 1123 if(c == '\0' || c == ' ') 1124 break; 1125 if(i == 0) 1126 *p++ = '.'; 1127 *p++ = c; 1128 } 1129 *p = 0; 1130 } 1131 1132 static char* 1133 getnamerunes(char *dst, uchar *buf, int step) 1134 { 1135 int i; 1136 Rune r; 1137 char dbuf[DOSRUNE * UTFmax + 1], *d; 1138 1139 d = dbuf; 1140 r = 1; 1141 for(i = 1; r && i < 11; i += 2){ 1142 r = buf[i] | (buf[i+1] << 8); 1143 d += runetochar(d, &r); 1144 } 1145 for(i = 14; r && i < 26; i += 2){ 1146 r = buf[i] | (buf[i+1] << 8); 1147 d += runetochar(d, &r); 1148 } 1149 for(i = 28; r && i < 32; i += 2){ 1150 r = buf[i] | (buf[i+1] << 8); 1151 d += runetochar(d, &r); 1152 } 1153 1154 if(step == 1) 1155 dst -= d - dbuf; 1156 1157 memmove(dst, dbuf, d - dbuf); 1158 1159 if(step == -1){ 1160 dst += d - dbuf; 1161 *dst = '\0'; 1162 } 1163 1164 return dst; 1165 } 1166 1167 char* 1168 getnamesect(char *dbuf, char *d, uchar *buf, int *islong, int *sum, int step) 1169 { 1170 /* 1171 * validation checks to make sure we're 1172 * making up a consistent name 1173 */ 1174 if(buf[11] != 0xf || buf[12] != 0){ 1175 *islong = 0; 1176 return nil; 1177 } 1178 if(step == 1){ 1179 if((buf[0] & 0xc0) == 0x40){ 1180 *islong = buf[0] & 0x3f; 1181 *sum = buf[13]; 1182 d = dbuf + DOSNAMELEN; 1183 *--d = '\0'; 1184 }else if(*islong && *islong == buf[0] + 1 && *sum == buf[13]){ 1185 *islong = buf[0]; 1186 }else{ 1187 *islong = 0; 1188 return nil; 1189 } 1190 }else{ 1191 if(*islong + 1 == (buf[0] & 0xbf) && *sum == buf[13]){ 1192 *islong = buf[0] & 0x3f; 1193 if(buf[0] & 0x40) 1194 *sum = -1; 1195 }else{ 1196 *islong = 0; 1197 *sum = -1; 1198 return nil; 1199 } 1200 } 1201 if(*islong > 20){ 1202 *islong = 0; 1203 *sum = -1; 1204 return nil; 1205 } 1206 1207 return getnamerunes(d, buf, step); 1208 } 1209 1210 void 1211 putname(char *p, Dosdir *d) 1212 { 1213 int i; 1214 1215 memset(d->name, ' ', sizeof d->name+sizeof d->ext); 1216 for(i=0; i<sizeof d->name; i++){ 1217 if(*p == 0 || *p == '.') 1218 break; 1219 d->name[i] = toupper(*p++); 1220 } 1221 p = strrchr(p, '.'); 1222 if(p){ 1223 for(i=0; i<sizeof d->ext; i++){ 1224 if(*++p == 0) 1225 break; 1226 d->ext[i] = toupper(*p); 1227 } 1228 } 1229 } 1230 1231 static void 1232 putnamesect(uchar *slot, Rune *longname, int curslot, int first, int sum) 1233 { 1234 Rune r; 1235 Dosdir ds; 1236 int i, j; 1237 1238 memset(&ds, 0xff, sizeof ds); 1239 ds.attr = 0xf; 1240 ds.reserved[0] = 0; 1241 ds.reserved[1] = sum; 1242 ds.start[0] = 0; 1243 ds.start[1] = 0; 1244 if(first) 1245 ds.name[0] = 0x40 | curslot; 1246 else 1247 ds.name[0] = curslot; 1248 memmove(slot, &ds, sizeof ds); 1249 1250 j = (curslot-1) * DOSRUNE; 1251 1252 for(i = 1; i < 11; i += 2){ 1253 r = longname[j++]; 1254 slot[i] = r; 1255 slot[i+1] = r >> 8; 1256 if(r == 0) 1257 return; 1258 } 1259 for(i = 14; i < 26; i += 2){ 1260 r = longname[j++]; 1261 slot[i] = r; 1262 slot[i+1] = r >> 8; 1263 if(r == 0) 1264 return; 1265 } 1266 for(i = 28; i < 32; i += 2){ 1267 r = longname[j++]; 1268 slot[i] = r; 1269 slot[i+1] = r >> 8; 1270 if(r == 0) 1271 return; 1272 } 1273 } 1274 1275 int 1276 aliassum(Dosdir *d) 1277 { 1278 int i, sum; 1279 1280 if(d == nil) 1281 return -1; 1282 sum = 0; 1283 for(i = 0; i < 11; i++) 1284 sum = (((sum&1)<<7) | ((sum&0xfe)>>1)) + d->name[i]; 1285 return sum & 0xff; 1286 } 1287 1288 int 1289 putlongname(Xfs *xf, Dosptr *ndp, char *name, char sname[13]) 1290 { 1291 Dosbpb *bp; 1292 Dosdir tmpd; 1293 Rune longname[DOSNAMELEN+1]; 1294 int i, first, sum, nds, len; 1295 1296 /* calculate checksum */ 1297 putname(sname, &tmpd); 1298 sum = aliassum(&tmpd); 1299 1300 bp = xf->ptr; 1301 first = 1; 1302 len = utftorunes(longname, name, DOSNAMELEN); 1303 if(chatty){ 1304 chat("utftorunes %s =", name); 1305 for(i=0; i<len; i++) 1306 chat(" %.4X", longname[i]); 1307 chat("\n"); 1308 } 1309 for(nds = (len + DOSRUNE-1) / DOSRUNE; nds > 0; nds--){ 1310 putnamesect(&ndp->p->iobuf[ndp->offset], longname, nds, first, sum); 1311 first = 0; 1312 ndp->offset += 32; 1313 if(ndp->offset == bp->sectsize){ 1314 chat("long name moving over sector boundary\n"); 1315 ndp->p->flags |= BMOD; 1316 putsect(ndp->p); 1317 ndp->p = nil; 1318 1319 /* 1320 * switch to the next cluster for a long entry 1321 * naddr should be set up correctly by searchdir 1322 */ 1323 ndp->prevaddr = ndp->addr; 1324 ndp->addr = ndp->naddr; 1325 ndp->naddr = -1; 1326 if(ndp->addr == -1) 1327 return -1; 1328 ndp->p = getsect(xf, ndp->addr); 1329 if(ndp->p == nil) 1330 return -1; 1331 ndp->offset = 0; 1332 ndp->d = (Dosdir *)&ndp->p->iobuf[ndp->offset]; 1333 } 1334 } 1335 return 0; 1336 } 1337 1338 long 1339 getfat(Xfs *xf, int n) 1340 { 1341 Dosbpb *bp = xf->ptr; 1342 Iosect *p; 1343 ulong k, sect; 1344 int o, fb; 1345 1346 if(n < FATRESRV || n >= bp->fatclusters) 1347 return -1; 1348 fb = bp->fatbits; 1349 k = (fb * n) >> 3; 1350 if(k >= bp->fatsize*bp->sectsize) 1351 panic("getfat"); 1352 sect = k/bp->sectsize + bp->fataddr; 1353 o = k%bp->sectsize; 1354 p = getsect(xf, sect); 1355 if(p == nil) 1356 return -1; 1357 k = p->iobuf[o++]; 1358 if(o >= bp->sectsize){ 1359 putsect(p); 1360 p = getsect(xf, sect+1); 1361 if(p == nil) 1362 return -1; 1363 o = 0; 1364 } 1365 k |= p->iobuf[o++]<<8; 1366 if(fb == 32){ 1367 /* fat32 is really fat28 */ 1368 k |= p->iobuf[o++] << 16; 1369 k |= (p->iobuf[o] & 0x0f) << 24; 1370 fb = 28; 1371 } 1372 putsect(p); 1373 if(fb == 12){ 1374 if(n&1) 1375 k >>= 4; 1376 else 1377 k &= 0xfff; 1378 } 1379 if(chatty > 1) 1380 chat("fat(%#x)=%#lx...", n, k); 1381 1382 /* 1383 * This is a very strange check for out of range. 1384 * As a concrete example, for a 16-bit FAT, 1385 * FFF8 through FFFF all signify ``end of cluster chain.'' 1386 * This generalizes to other-sized FATs. 1387 */ 1388 if(k >= (1 << fb) - 8) 1389 return -1; 1390 1391 return k; 1392 } 1393 1394 void 1395 putfat(Xfs *xf, int n, ulong val) 1396 { 1397 Fatinfo *fi; 1398 Dosbpb *bp; 1399 Iosect *p; 1400 ulong k, sect, esect; 1401 int o; 1402 1403 bp = xf->ptr; 1404 if(n < FATRESRV || n >= bp->fatclusters) 1405 panic("putfat n=%d", n); 1406 k = (bp->fatbits * n) >> 3; 1407 if(k >= bp->fatsize*bp->sectsize) 1408 panic("putfat"); 1409 sect = k/bp->sectsize + bp->fataddr; 1410 esect = sect + bp->nfats * bp->fatsize; 1411 for(; sect<esect; sect+=bp->fatsize){ 1412 o = k%bp->sectsize; 1413 p = getsect(xf, sect); 1414 if(p == nil) 1415 continue; 1416 switch(bp->fatbits){ 1417 case 12: 1418 if(n&1){ 1419 p->iobuf[o] &= 0x0f; 1420 p->iobuf[o++] |= val<<4; 1421 if(o >= bp->sectsize){ 1422 p->flags |= BMOD; 1423 putsect(p); 1424 p = getsect(xf, sect+1); 1425 if(p == nil) 1426 continue; 1427 o = 0; 1428 } 1429 p->iobuf[o] = val>>4; 1430 }else{ 1431 p->iobuf[o++] = val; 1432 if(o >= bp->sectsize){ 1433 p->flags |= BMOD; 1434 putsect(p); 1435 p = getsect(xf, sect+1); 1436 if(p == nil) 1437 continue; 1438 o = 0; 1439 } 1440 p->iobuf[o] &= 0xf0; 1441 p->iobuf[o] |= (val>>8) & 0x0f; 1442 } 1443 break; 1444 case 16: 1445 p->iobuf[o++] = val; 1446 p->iobuf[o] = val>>8; 1447 break; 1448 case 32: /* fat32 is really fat28 */ 1449 p->iobuf[o++] = val; 1450 p->iobuf[o++] = val>>8; 1451 p->iobuf[o++] = val>>16; 1452 p->iobuf[o] = (p->iobuf[o] & 0xf0) | ((val>>24) & 0x0f); 1453 break; 1454 default: 1455 panic("putfat fatbits"); 1456 } 1457 p->flags |= BMOD; 1458 putsect(p); 1459 } 1460 1461 if(val == 0) 1462 bp->freeclusters++; 1463 else 1464 bp->freeclusters--; 1465 1466 if(bp->fatinfo){ 1467 p = getsect(xf, bp->fatinfo); 1468 if(p != nil){ 1469 fi = (Fatinfo*)p->iobuf; 1470 PLONG(fi->nextfree, bp->freeptr); 1471 PLONG(fi->freeclust, bp->freeclusters); 1472 p->flags |= BMOD; 1473 putsect(p); 1474 } 1475 } 1476 } 1477 1478 /* 1479 * Contiguous falloc; if we can, use lastclust+1. 1480 * Otherwise, move the file to get some space. 1481 * If there just isn't enough contiguous space 1482 * anywhere on disk, fail. 1483 */ 1484 int 1485 cfalloc(Xfile *f) 1486 { 1487 int l; 1488 1489 if((l=makecontig(f, 8)) >= 0) 1490 return l; 1491 return makecontig(f, 1); 1492 } 1493 1494 /* 1495 * Check whether a file is contiguous. 1496 */ 1497 int 1498 iscontig(Xfs *xf, Dosdir *d) 1499 { 1500 long clust, next; 1501 1502 clust = getstart(xf, d); 1503 if(clust <= 0) 1504 return 1; 1505 1506 for(;;) { 1507 next = getfat(xf, clust); 1508 if(next < 0) 1509 return 1; 1510 if(next != clust+1) 1511 return 0; 1512 clust = next; 1513 } 1514 } 1515 1516 /* 1517 * Make a file contiguous, with nextra clusters of 1518 * free space after it for later expansion. 1519 * Return the number of the first new cluster. 1520 */ 1521 int 1522 makecontig(Xfile *f, int nextra) 1523 { 1524 Dosbpb *bp; 1525 Dosdir *d; 1526 Dosptr *dp; 1527 Xfs *xf; 1528 Iosect *wp, *rp; 1529 long clust, next, last, start, rclust, wclust, eclust, ostart; 1530 int isok, i, n, nclust, nrun, rs, ws; 1531 1532 xf = f->xf; 1533 bp = xf->ptr; 1534 dp = f->ptr; 1535 d = dp->d; 1536 1537 isok = 1; 1538 nclust = 0; 1539 clust = fileclust(f, 0, 0); 1540 chat("clust %#lux", clust); 1541 if(clust != -1) { 1542 for(;;) { 1543 nclust++; 1544 chat("."); 1545 next = getfat(xf, clust); 1546 if(next <= 0) 1547 break; 1548 if(next != clust+1) 1549 isok = 0; 1550 clust = next; 1551 } 1552 } 1553 chat("nclust %d\n", nclust); 1554 1555 if(isok && clust != -1) { 1556 eclust = clust+1; /* eclust = first cluster past file */ 1557 assert(eclust == fileclust(f, 0, 0)+nclust); 1558 for(i=0; i<nextra; i++) 1559 if(getfat(xf, eclust+i) != 0) 1560 break; 1561 if(i == nextra) { /* they were all free */ 1562 chat("eclust=%#lx, getfat eclust-1 = %#lux\n", eclust, getfat(xf, eclust-1)); 1563 assert(getfat(xf, eclust-1) == 0xffffffff); 1564 putfat(xf, eclust-1, eclust); 1565 putfat(xf, eclust, 0xffffffff); 1566 bp->freeptr = clust+1; /* to help keep the blocks free */ 1567 return eclust; 1568 } 1569 } 1570 1571 /* need to search for nclust+nextra contiguous free blocks */ 1572 last = -1; 1573 n = bp->freeptr; 1574 nrun = 0; 1575 for(;;){ 1576 if(getfat(xf, n) == 0) { 1577 if(last+1 == n) 1578 nrun++; 1579 else 1580 nrun = 1; 1581 if(nrun >= nclust+nextra) 1582 break; 1583 last = n; 1584 } 1585 if(++n >= bp->fatclusters) 1586 n = FATRESRV; 1587 if(n == bp->freeptr) { 1588 errno = Econtig; 1589 return -1; 1590 } 1591 } 1592 bp->freeptr = n+1; 1593 1594 /* copy old data over */ 1595 start = n+1 - nrun; 1596 1597 /* sanity check */ 1598 for(i=0; i<nclust+nextra; i++) 1599 assert(getfat(xf, start+i) == 0); 1600 1601 chat("relocate chain %lux -> 0x%lux len %d\n", fileclust(f, 0, 0), start, nclust); 1602 1603 wclust = start; 1604 for(rclust = fileclust(f, 0, 0); rclust > 0; rclust = next){ 1605 rs = clust2sect(bp, rclust); 1606 ws = clust2sect(bp, wclust); 1607 for(i=0; i<bp->clustsize; i++, rs++, ws++){ 1608 rp = getsect(xf, rs); 1609 if(rp == nil) 1610 return -1; 1611 wp = getosect(xf, ws); 1612 assert(wp != nil); 1613 memmove(wp->iobuf, rp->iobuf, bp->sectsize); 1614 wp->flags = BMOD; 1615 putsect(rp); 1616 putsect(wp); 1617 } 1618 chat("move cluster %#lx -> %#lx...", rclust, wclust); 1619 next = getfat(xf, rclust); 1620 putfat(xf, wclust, wclust+1); 1621 wclust++; 1622 } 1623 1624 /* now wclust points at the first new cluster; chain it in */ 1625 chat("wclust 0x%lux start 0x%lux (fat->0x%lux) nclust %d\n", wclust, start, getfat(xf, start), nclust); 1626 assert(wclust == start+nclust); 1627 putfat(xf, wclust, 0xffffffff); /* end of file */ 1628 1629 /* update directory entry to point at new start */ 1630 ostart = fileclust(f, 0, 0); 1631 putstart(xf, d, start); 1632 1633 /* check our work */ 1634 i = 0; 1635 clust = fileclust(f, 0, 0); 1636 if(clust != -1) { 1637 for(;;) { 1638 i++; 1639 next = getfat(xf, clust); 1640 if(next <= 0) 1641 break; 1642 assert(next == clust+1); 1643 clust = next; 1644 } 1645 } 1646 chat("chain check: len %d\n", i); 1647 assert(i == nclust+1); 1648 1649 /* succeeded; remove old chain. */ 1650 for(rclust = ostart; rclust > 0; rclust = next){ 1651 next = getfat(xf, rclust); 1652 putfat(xf, rclust, 0); /* free cluster */ 1653 } 1654 1655 return start+nclust; 1656 } 1657 1658 int 1659 falloc(Xfs *xf) 1660 { 1661 Dosbpb *bp = xf->ptr; 1662 Iosect *p; 1663 int n, i, k; 1664 1665 n = bp->freeptr; 1666 for(;;){ 1667 if(getfat(xf, n) == 0) 1668 break; 1669 if(++n >= bp->fatclusters) 1670 n = FATRESRV; 1671 if(n == bp->freeptr) 1672 return -1; 1673 } 1674 bp->freeptr = n+1; 1675 if(bp->freeptr >= bp->fatclusters) 1676 bp->freeptr = FATRESRV; 1677 putfat(xf, n, 0xffffffff); 1678 k = clust2sect(bp, n); 1679 for(i=0; i<bp->clustsize; i++){ 1680 p = getosect(xf, k+i); 1681 memset(p->iobuf, 0, bp->sectsize); 1682 p->flags = BMOD; 1683 putsect(p); 1684 } 1685 return n; 1686 } 1687 1688 void 1689 ffree(Xfs *xf, long start) 1690 { 1691 putfat(xf, start, 0); 1692 } 1693 1694 long 1695 clust2sect(Dosbpb *bp, long clust) 1696 { 1697 return bp->dataaddr + (clust - FATRESRV) * bp->clustsize; 1698 } 1699 1700 long 1701 sect2clust(Dosbpb *bp, long sect) 1702 { 1703 long c; 1704 1705 c = (sect - bp->dataaddr) / bp->clustsize + FATRESRV; 1706 assert(sect == clust2sect(bp, c)); 1707 return c; 1708 } 1709 1710 void 1711 puttime(Dosdir *d, long s) 1712 { 1713 Tm *t; 1714 ushort x; 1715 1716 if(s == 0) 1717 s = time(0); 1718 t = localtime(s); 1719 1720 x = (t->hour<<11) | (t->min<<5) | (t->sec>>1); 1721 PSHORT(d->time, x); 1722 x = ((t->year-80)<<9) | ((t->mon+1)<<5) | t->mday; 1723 PSHORT(d->date, x); 1724 } 1725 1726 long 1727 gtime(Dosdir *dp) 1728 { 1729 Tm tm; 1730 int i; 1731 1732 i = GSHORT(dp->time); 1733 tm.hour = i >> 11; 1734 tm.min = (i >> 5) & 63; 1735 tm.sec = (i & 31) << 1; 1736 i = GSHORT(dp->date); 1737 tm.year = 80 + (i >> 9); 1738 tm.mon = ((i >> 5) & 15) - 1; 1739 tm.mday = i & 31; 1740 tm.zone[0] = '\0'; 1741 tm.tzoff = 0; 1742 tm.yday = 0; 1743 1744 return tm2sec(&tm); 1745 } 1746 1747 /* 1748 * structure dumps for debugging 1749 */ 1750 void 1751 bootdump(int fd, Dosboot *b) 1752 { 1753 Biobuf bp; 1754 1755 Binit(&bp, fd, OWRITE); 1756 Bprint(&bp, "magic: 0x%2.2x 0x%2.2x 0x%2.2x\n", 1757 b->magic[0], b->magic[1], b->magic[2]); 1758 Bprint(&bp, "version: \"%8.8s\"\n", (char*)b->version); 1759 Bprint(&bp, "sectsize: %d\n", GSHORT(b->sectsize)); 1760 Bprint(&bp, "clustsize: %d\n", b->clustsize); 1761 Bprint(&bp, "nresrv: %d\n", GSHORT(b->nresrv)); 1762 Bprint(&bp, "nfats: %d\n", b->nfats); 1763 Bprint(&bp, "rootsize: %d\n", GSHORT(b->rootsize)); 1764 Bprint(&bp, "volsize: %d\n", GSHORT(b->volsize)); 1765 Bprint(&bp, "mediadesc: 0x%2.2x\n", b->mediadesc); 1766 Bprint(&bp, "fatsize: %d\n", GSHORT(b->fatsize)); 1767 Bprint(&bp, "trksize: %d\n", GSHORT(b->trksize)); 1768 Bprint(&bp, "nheads: %d\n", GSHORT(b->nheads)); 1769 Bprint(&bp, "nhidden: %ld\n", GLONG(b->nhidden)); 1770 Bprint(&bp, "bigvolsize: %ld\n", GLONG(b->bigvolsize)); 1771 Bprint(&bp, "driveno: %d\n", b->driveno); 1772 Bprint(&bp, "reserved0: 0x%2.2x\n", b->reserved0); 1773 Bprint(&bp, "bootsig: 0x%2.2x\n", b->bootsig); 1774 Bprint(&bp, "volid: 0x%8.8lux\n", GLONG(b->volid)); 1775 Bprint(&bp, "label: \"%11.11s\"\n", (char*)b->label); 1776 Bterm(&bp); 1777 } 1778 1779 void 1780 bootdump32(int fd, Dosboot32 *b) 1781 { 1782 Biobuf bp; 1783 1784 Binit(&bp, fd, OWRITE); 1785 Bprint(&bp, "magic: 0x%2.2x 0x%2.2x 0x%2.2x\n", 1786 b->magic[0], b->magic[1], b->magic[2]); 1787 Bprint(&bp, "version: \"%8.8s\"\n", (char*)b->version); 1788 Bprint(&bp, "sectsize: %d\n", GSHORT(b->sectsize)); 1789 Bprint(&bp, "clustsize: %d\n", b->clustsize); 1790 Bprint(&bp, "nresrv: %d\n", GSHORT(b->nresrv)); 1791 Bprint(&bp, "nfats: %d\n", b->nfats); 1792 Bprint(&bp, "rootsize: %d\n", GSHORT(b->rootsize)); 1793 Bprint(&bp, "volsize: %d\n", GSHORT(b->volsize)); 1794 Bprint(&bp, "mediadesc: 0x%2.2x\n", b->mediadesc); 1795 Bprint(&bp, "fatsize: %d\n", GSHORT(b->fatsize)); 1796 Bprint(&bp, "trksize: %d\n", GSHORT(b->trksize)); 1797 Bprint(&bp, "nheads: %d\n", GSHORT(b->nheads)); 1798 Bprint(&bp, "nhidden: %ld\n", GLONG(b->nhidden)); 1799 Bprint(&bp, "bigvolsize: %ld\n", GLONG(b->bigvolsize)); 1800 Bprint(&bp, "fatsize32: %ld\n", GLONG(b->fatsize32)); 1801 Bprint(&bp, "extflags: %d\n", GSHORT(b->extflags)); 1802 Bprint(&bp, "version: %d\n", GSHORT(b->version1)); 1803 Bprint(&bp, "rootstart: %ld\n", GLONG(b->rootstart)); 1804 Bprint(&bp, "infospec: %d\n", GSHORT(b->infospec)); 1805 Bprint(&bp, "backupboot: %d\n", GSHORT(b->backupboot)); 1806 Bprint(&bp, "reserved: %d %d %d %d %d %d %d %d %d %d %d %d\n", 1807 b->reserved[0], b->reserved[1], b->reserved[2], b->reserved[3], 1808 b->reserved[4], b->reserved[5], b->reserved[6], b->reserved[7], 1809 b->reserved[8], b->reserved[9], b->reserved[10], b->reserved[11]); 1810 Bterm(&bp); 1811 } 1812 1813 void 1814 bootsecdump32(int fd, Xfs *xf, Dosboot32 *b32) 1815 { 1816 Fatinfo *fi; 1817 Iosect *p1; 1818 int fisec, bsec, res; 1819 1820 fprint(fd, "\nfat32\n"); 1821 bootdump32(fd, b32); 1822 res = GSHORT(b32->nresrv); 1823 bsec = GSHORT(b32->backupboot); 1824 if(bsec < res && bsec != 0){ 1825 p1 = getsect(xf, bsec); 1826 if(p1 == nil) 1827 fprint(fd, "\ncouldn't get backup boot sector: %r\n"); 1828 else{ 1829 fprint(fd, "\nbackup boot\n"); 1830 bootdump32(fd, (Dosboot32*)p1->iobuf); 1831 putsect(p1); 1832 } 1833 }else if(bsec != 0xffff) 1834 fprint(fd, "bad backup boot sector: %d reserved %d\n", bsec, res); 1835 fisec = GSHORT(b32->infospec); 1836 if(fisec < res && fisec != 0){ 1837 p1 = getsect(xf, fisec); 1838 if(p1 == nil) 1839 fprint(fd, "\ncouldn't get fat info sector: %r\n"); 1840 else{ 1841 fprint(fd, "\nfat info %d\n", fisec); 1842 fi = (Fatinfo*)p1->iobuf; 1843 fprint(fd, "sig1: 0x%lux sb 0x%lux\n", GLONG(fi->sig1), FATINFOSIG1); 1844 fprint(fd, "sig: 0x%lux sb 0x%lux\n", GLONG(fi->sig), FATINFOSIG); 1845 fprint(fd, "freeclust: %lud\n", GLONG(fi->freeclust)); 1846 fprint(fd, "nextfree: %lud\n", GLONG(fi->nextfree)); 1847 fprint(fd, "reserved: %lud %lud %lud\n", GLONG(fi->resrv), GLONG(fi->resrv+4), GLONG(fi->resrv+8)); 1848 putsect(p1); 1849 } 1850 }else if(fisec != 0xffff) 1851 fprint(2, "bad fat info sector: %d reserved %d\n", bsec, res); 1852 } 1853 1854 void 1855 dirdump(void *vdbuf) 1856 { 1857 static char attrchar[] = "rhsvda67"; 1858 Dosdir *d; 1859 char *name, namebuf[DOSNAMELEN]; 1860 char buf[128], *s, *ebuf; 1861 uchar *dbuf; 1862 int i; 1863 1864 if(!chatty) 1865 return; 1866 1867 d = vdbuf; 1868 1869 ebuf = buf + sizeof(buf); 1870 if(d->attr == 0xf){ 1871 dbuf = vdbuf; 1872 name = namebuf + DOSNAMELEN; 1873 *--name = '\0'; 1874 name = getnamerunes(name, dbuf, 1); 1875 seprint(buf, ebuf, "\"%s\" %2.2x %2.2ux %2.2ux %d", name, dbuf[0], dbuf[12], dbuf[13], GSHORT(d->start)); 1876 }else{ 1877 s = seprint(buf, ebuf, "\"%.8s.%.3s\" ", (char*)d->name, (char*)d->ext); 1878 for(i=7; i>=0; i--) 1879 *s++ = d->attr&(1<<i) ? attrchar[i] : '-'; 1880 1881 i = GSHORT(d->time); 1882 s = seprint(s, ebuf, " %2.2d:%2.2d:%2.2d", i>>11, (i>>5)&63, (i&31)<<1); 1883 i = GSHORT(d->date); 1884 s = seprint(s, ebuf, " %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31); 1885 1886 i = GSHORT(d->ctime); 1887 s = seprint(s, ebuf, " %2.2d:%2.2d:%2.2d", i>>11, (i>>5)&63, (i&31)<<1); 1888 i = GSHORT(d->cdate); 1889 s = seprint(s, ebuf, " %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31); 1890 1891 i = GSHORT(d->adate); 1892 s = seprint(s, ebuf, " %2.2d.%2.2d.%2.2d", 80+(i>>9), (i>>5)&15, i&31); 1893 1894 seprint(s, ebuf, " %d %d", GSHORT(d->start), GSHORT(d->length)); 1895 } 1896 chat("%s\n", buf); 1897 } 1898 1899 int 1900 cistrcmp(char *s1, char *s2) 1901 { 1902 int c1, c2; 1903 1904 while(*s1){ 1905 c1 = *s1++; 1906 c2 = *s2++; 1907 1908 if(c1 >= 'A' && c1 <= 'Z') 1909 c1 -= 'A' - 'a'; 1910 1911 if(c2 >= 'A' && c2 <= 'Z') 1912 c2 -= 'A' - 'a'; 1913 1914 if(c1 != c2) 1915 return c1 - c2; 1916 } 1917 return -*s2; 1918 } 1919 1920 int 1921 utftorunes(Rune *rr, char *s, int n) 1922 { 1923 Rune *r, *re; 1924 int c; 1925 1926 r = rr; 1927 re = r + n - 1; 1928 while(c = (uchar)*s){ 1929 if(c < Runeself){ 1930 *r = c; 1931 s++; 1932 }else 1933 s += chartorune(r, s); 1934 r++; 1935 if(r >= re) 1936 break; 1937 } 1938 *r = 0; 1939 return r - rr; 1940 } 1941