1 #include "u.h" 2 #include "lib.h" 3 #include "mem.h" 4 #include "dat.h" 5 #include "fns.h" 6 #include "fs.h" 7 8 struct Dosboot{ 9 uchar magic[3]; 10 uchar version[8]; 11 uchar sectsize[2]; 12 uchar clustsize; 13 uchar nresrv[2]; 14 uchar nfats; 15 uchar rootsize[2]; 16 uchar volsize[2]; 17 uchar mediadesc; 18 uchar fatsize[2]; 19 uchar trksize[2]; 20 uchar nheads[2]; 21 uchar nhidden[4]; 22 uchar bigvolsize[4]; 23 /* fat 32 */ 24 uchar bigfatsize[4]; 25 uchar extflags[2]; 26 uchar fsversion[2]; 27 uchar rootdirstartclust[4]; 28 uchar fsinfosect[2]; 29 uchar backupbootsect[2]; 30 /* ??? 31 uchar driveno; 32 uchar reserved0; 33 uchar bootsig; 34 uchar volid[4]; 35 uchar label[11]; 36 uchar reserved1[8]; 37 */ 38 }; 39 40 struct Dosdir{ 41 uchar name[8]; 42 uchar ext[3]; 43 uchar attr; 44 uchar lowercase; 45 uchar hundredth; 46 uchar ctime[2]; 47 uchar cdate[2]; 48 uchar adate[2]; 49 uchar highstart[2]; 50 uchar mtime[2]; 51 uchar mdate[2]; 52 uchar start[2]; 53 uchar length[4]; 54 }; 55 56 #define DOSRONLY 0x01 57 #define DOSHIDDEN 0x02 58 #define DOSSYSTEM 0x04 59 #define DOSVLABEL 0x08 60 #define DOSDIR 0x10 61 #define DOSARCH 0x20 62 63 /* 64 * predeclared 65 */ 66 static void bootdump(Dosboot*); 67 static void setname(Dosfile*, char*); 68 69 /* 70 * debugging 71 */ 72 #define chatty 0 73 #define chat if(chatty)print 74 75 /* 76 * block io buffers 77 */ 78 enum 79 { 80 Nbio= 16, 81 }; 82 typedef struct Clustbuf Clustbuf; 83 struct Clustbuf 84 { 85 int age; 86 long sector; 87 uchar *iobuf; 88 Dos *dos; 89 int size; 90 }; 91 Clustbuf bio[Nbio]; 92 93 /* 94 * get an io block from an io buffer 95 */ 96 Clustbuf* 97 getclust(Dos *dos, long sector) 98 { 99 Fs *fs; 100 Clustbuf *p, *oldest; 101 int size; 102 103 chat("getclust @ %ld\n", sector); 104 105 /* 106 * if we have it, just return it 107 */ 108 for(p = bio; p < &bio[Nbio]; p++){ 109 if(sector == p->sector && dos == p->dos){ 110 p->age = m->ticks; 111 chat("getclust %ld in cache\n", sector); 112 return p; 113 } 114 } 115 116 /* 117 * otherwise, reuse the oldest entry 118 */ 119 oldest = bio; 120 for(p = &bio[1]; p < &bio[Nbio]; p++){ 121 if(p->age <= oldest->age) 122 oldest = p; 123 } 124 p = oldest; 125 126 /* 127 * make sure the buffer is big enough 128 */ 129 size = dos->clustsize*dos->sectsize; 130 if(p->iobuf==0 || p->size < size) 131 p->iobuf = ialloc(size, 0); 132 p->size = size; 133 134 /* 135 * read in the cluster 136 */ 137 fs = (Fs*)dos; 138 chat("getclust addr %lud %p %p %p\n", (ulong)((sector+dos->start)*(vlong)dos->sectsize), 139 fs, fs->diskseek, fs->diskread); 140 if(fs->diskseek(fs, (sector+dos->start)*(vlong)dos->sectsize) < 0){ 141 chat("can't seek block\n"); 142 return 0; 143 } 144 if(fs->diskread(fs, p->iobuf, size) != size){ 145 chat("can't read block\n"); 146 return 0; 147 } 148 149 p->age = m->ticks; 150 p->dos = dos; 151 p->sector = sector; 152 chat("getclust %ld read\n", sector); 153 return p; 154 } 155 156 /* 157 * walk the fat one level ( n is a current cluster number ). 158 * return the new cluster number or -1 if no more. 159 */ 160 static long 161 fatwalk(Dos *dos, int n) 162 { 163 ulong k, sect; 164 Clustbuf *p; 165 int o; 166 167 chat("fatwalk %d\n", n); 168 169 if(n < 2 || n >= dos->fatclusters) 170 return -1; 171 172 switch(dos->fatbits){ 173 case 12: 174 k = (3*n)/2; break; 175 case 16: 176 k = 2*n; break; 177 case 32: 178 k = 4*n; break; 179 default: 180 return -1; 181 } 182 if(k >= dos->fatsize*dos->sectsize) 183 panic("getfat"); 184 185 sect = (k/(dos->sectsize*dos->clustsize))*dos->clustsize + dos->fataddr; 186 o = k%(dos->sectsize*dos->clustsize); 187 p = getclust(dos, sect); 188 k = p->iobuf[o++]; 189 if(o >= dos->sectsize*dos->clustsize){ 190 p = getclust(dos, sect+dos->clustsize); 191 o = 0; 192 } 193 k |= p->iobuf[o++]<<8; 194 if(dos->fatbits == 12){ 195 if(n&1) 196 k >>= 4; 197 else 198 k &= 0xfff; 199 if(k >= 0xff8) 200 k = -1; 201 } 202 else if (dos->fatbits == 32){ 203 if(o >= dos->sectsize*dos->clustsize){ 204 p = getclust(dos, sect+dos->clustsize); 205 o = 0; 206 } 207 k |= p->iobuf[o++]<<16; 208 k |= p->iobuf[o]<<24; 209 if (k >= 0xfffffff8) 210 k = -1; 211 } 212 else 213 k = k < 0xfff8 ? k : -1; 214 chat("fatwalk %d -> %lud\n", n, k); 215 return k; 216 } 217 218 /* 219 * map a file's logical cluster address to a physical sector address 220 */ 221 static long 222 fileaddr(Dosfile *fp, long ltarget) 223 { 224 Dos *dos = fp->dos; 225 long l; 226 long p; 227 228 chat("fileaddr %8.8s %ld\n", fp->name, ltarget); 229 /* 230 * root directory is contiguous and easy (unless FAT32) 231 */ 232 if(fp->pstart == 0 && dos->rootsize != 0) { 233 if(ltarget*dos->sectsize*dos->clustsize >= dos->rootsize*sizeof(Dosdir)) 234 return -1; 235 l = dos->rootaddr + ltarget*dos->clustsize; 236 chat("fileaddr %ld -> %ld\n", ltarget, l); 237 return l; 238 } 239 240 /* 241 * anything else requires a walk through the fat 242 */ 243 if(ltarget >= fp->lcurrent && fp->pcurrent){ 244 /* start at the currrent point */ 245 l = fp->lcurrent; 246 p = fp->pcurrent; 247 } else { 248 /* go back to the beginning */ 249 l = 0; 250 p = fp->pstart; 251 } 252 while(l != ltarget){ 253 /* walk the fat */ 254 p = fatwalk(dos, p); 255 if(p < 0) 256 return -1; 257 l++; 258 } 259 fp->lcurrent = l; 260 fp->pcurrent = p; 261 262 /* 263 * clusters start at 2 instead of 0 (why? - presotto) 264 */ 265 l = dos->dataaddr + (p-2)*dos->clustsize; 266 chat("fileaddr %ld -> %ld\n", ltarget, l); 267 return l; 268 } 269 270 /* 271 * read from a dos file 272 */ 273 long 274 dosread(Dosfile *fp, void *a, long n) 275 { 276 long addr; 277 long rv; 278 int i; 279 int off; 280 Clustbuf *p; 281 uchar *from, *to; 282 283 if((fp->attr & DOSDIR) == 0){ 284 if(fp->offset >= fp->length) 285 return 0; 286 if(fp->offset+n > fp->length) 287 n = fp->length - fp->offset; 288 } 289 290 to = a; 291 for(rv = 0; rv < n; rv+=i){ 292 /* 293 * read the cluster 294 */ 295 addr = fileaddr(fp, fp->offset/fp->dos->clustbytes); 296 if(addr < 0) 297 return -1; 298 p = getclust(fp->dos, addr); 299 if(p == 0) 300 return -1; 301 302 /* 303 * copy the bytes we need 304 */ 305 off = fp->offset % fp->dos->clustbytes; 306 from = &p->iobuf[off]; 307 i = n - rv; 308 if(i > fp->dos->clustbytes - off) 309 i = fp->dos->clustbytes - off; 310 memmove(to, from, i); 311 to += i; 312 fp->offset += i; 313 } 314 315 return rv; 316 } 317 318 /* 319 * walk a directory returns 320 * -1 if something went wrong 321 * 0 if not found 322 * 1 if found 323 */ 324 int 325 doswalk(File *f, char *name) 326 { 327 Dosdir d; 328 long n; 329 Dosfile *file; 330 331 chat("doswalk %s\n", name); 332 333 file = &f->dos; 334 335 if((file->attr & DOSDIR) == 0){ 336 chat("walking non-directory!\n"); 337 return -1; 338 } 339 340 setname(file, name); 341 342 file->offset = 0; /* start at the beginning */ 343 while((n = dosread(file, &d, sizeof(d))) == sizeof(d)){ 344 chat("comparing to %8.8s.%3.3s\n", (char*)d.name, (char*)d.ext); 345 if(memcmp(file->name, d.name, sizeof(d.name)) != 0) 346 continue; 347 if(memcmp(file->ext, d.ext, sizeof(d.ext)) != 0) 348 continue; 349 if(d.attr & DOSVLABEL){ 350 chat("%8.8s.%3.3s is a LABEL\n", (char*)d.name, (char*)d.ext); 351 continue; 352 } 353 file->attr = d.attr; 354 file->pstart = GSHORT(d.start); 355 if (file->dos->fatbits == 32) 356 file->pstart |= GSHORT(d.highstart) << 16; 357 file->length = GLONG(d.length); 358 file->pcurrent = 0; 359 file->lcurrent = 0; 360 file->offset = 0; 361 return 1; 362 } 363 return n >= 0 ? 0 : -1; 364 } 365 366 /* 367 * instructions that boot blocks can start with 368 */ 369 #define JMPSHORT 0xeb 370 #define JMPNEAR 0xe9 371 372 /* 373 * read in a segment 374 */ 375 long 376 dosreadseg(File *f, void *va, long len) 377 { 378 char *a; 379 long n, sofar; 380 Dosfile *fp; 381 382 fp = &f->dos; 383 a = va; 384 for(sofar = 0; sofar < len; sofar += n){ 385 n = 8*1024; 386 if(len - sofar < n) 387 n = len - sofar; 388 n = dosread(fp, a + sofar, n); 389 if(n <= 0) 390 break; 391 print("."); 392 } 393 return sofar; 394 } 395 396 int 397 dosinit(Fs *fs) 398 { 399 Clustbuf *p; 400 Dosboot *b; 401 int i; 402 Dos *dos; 403 Dosfile *root; 404 405 chat("dosinit0 %p %p %p\n", fs, fs->diskseek, fs->diskread); 406 407 dos = &fs->dos; 408 /* defaults till we know better */ 409 dos->sectsize = 512; 410 dos->clustsize = 1; 411 412 /* get first sector */ 413 p = getclust(dos, 0); 414 if(p == 0){ 415 chat("can't read boot block\n"); 416 return -1; 417 } 418 419 chat("dosinit0a\n"); 420 421 p->dos = 0; 422 b = (Dosboot *)p->iobuf; 423 if(b->magic[0] != JMPNEAR && (b->magic[0] != JMPSHORT || b->magic[2] != 0x90)){ 424 chat("no dos file system %x %x %x %x\n", 425 b->magic[0], b->magic[1], b->magic[2], b->magic[3]); 426 return -1; 427 } 428 429 if(chatty) 430 bootdump(b); 431 432 if(b->clustsize == 0) { 433 unreasonable: 434 if(chatty){ 435 print("unreasonable FAT BPB: "); 436 for(i=0; i<3+8+2+1; i++) 437 print(" %.2ux", p->iobuf[i]); 438 print("\n"); 439 } 440 return -1; 441 } 442 443 chat("dosinit1\n"); 444 445 /* 446 * Determine the systems' wondrous properties. 447 * There are heuristics here, but there's no real way 448 * of knowing if this is a reasonable FAT. 449 */ 450 dos->fatbits = 0; 451 dos->sectsize = GSHORT(b->sectsize); 452 if(dos->sectsize & 0xFF) 453 goto unreasonable; 454 dos->clustsize = b->clustsize; 455 dos->clustbytes = dos->sectsize*dos->clustsize; 456 dos->nresrv = GSHORT(b->nresrv); 457 dos->nfats = b->nfats; 458 dos->fatsize = GSHORT(b->fatsize); 459 dos->rootsize = GSHORT(b->rootsize); 460 dos->volsize = GSHORT(b->volsize); 461 if(dos->volsize == 0) 462 dos->volsize = GLONG(b->bigvolsize); 463 dos->mediadesc = b->mediadesc; 464 if(dos->fatsize == 0) { 465 chat("fat32\n"); 466 dos->rootsize = 0; 467 dos->fatsize = GLONG(b->bigfatsize); 468 dos->fatbits = 32; 469 } 470 dos->fataddr = dos->nresrv; 471 if (dos->rootsize == 0) { 472 dos->rootaddr = 0; 473 dos->rootclust = GLONG(b->rootdirstartclust); 474 dos->dataaddr = dos->fataddr + dos->nfats*dos->fatsize; 475 } else { 476 dos->rootaddr = dos->fataddr + dos->nfats*dos->fatsize; 477 i = dos->rootsize*sizeof(Dosdir) + dos->sectsize - 1; 478 i = i/dos->sectsize; 479 dos->dataaddr = dos->rootaddr + i; 480 } 481 dos->fatclusters = 2+(dos->volsize - dos->dataaddr)/dos->clustsize; 482 if(dos->fatbits != 32) { 483 if(dos->fatclusters < 4087) 484 dos->fatbits = 12; 485 else 486 dos->fatbits = 16; 487 } 488 dos->freeptr = 2; 489 490 if(dos->clustbytes < 512 || dos->clustbytes > 64*1024) 491 goto unreasonable; 492 493 chat("dosinit2\n"); 494 495 /* 496 * set up the root 497 */ 498 499 fs->root.fs = fs; 500 root = &fs->root.dos; 501 root->dos = dos; 502 root->pstart = dos->rootsize == 0 ? dos->rootclust : 0; 503 root->pcurrent = root->lcurrent = 0; 504 root->offset = 0; 505 root->attr = DOSDIR; 506 root->length = dos->rootsize*sizeof(Dosdir); 507 508 chat("dosinit3\n"); 509 510 fs->read = dosreadseg; 511 fs->walk = doswalk; 512 return 0; 513 } 514 515 static void 516 bootdump(Dosboot *b) 517 { 518 if(chatty == 0) 519 return; 520 print("magic: 0x%2.2x 0x%2.2x 0x%2.2x ", 521 b->magic[0], b->magic[1], b->magic[2]); 522 print("version: \"%8.8s\"\n", (char*)b->version); 523 print("sectsize: %d ", GSHORT(b->sectsize)); 524 print("allocsize: %d ", b->clustsize); 525 print("nresrv: %d ", GSHORT(b->nresrv)); 526 print("nfats: %d\n", b->nfats); 527 print("rootsize: %d ", GSHORT(b->rootsize)); 528 print("volsize: %d ", GSHORT(b->volsize)); 529 print("mediadesc: 0x%2.2x\n", b->mediadesc); 530 print("fatsize: %d ", GSHORT(b->fatsize)); 531 print("trksize: %d ", GSHORT(b->trksize)); 532 print("nheads: %d ", GSHORT(b->nheads)); 533 print("nhidden: %d ", GLONG(b->nhidden)); 534 print("bigvolsize: %d\n", GLONG(b->bigvolsize)); 535 /* 536 print("driveno: %d\n", b->driveno); 537 print("reserved0: 0x%2.2x\n", b->reserved0); 538 print("bootsig: 0x%2.2x\n", b->bootsig); 539 print("volid: 0x%8.8x\n", GLONG(b->volid)); 540 print("label: \"%11.11s\"\n", b->label); 541 */ 542 } 543 544 545 /* 546 * set up a dos file name 547 */ 548 static void 549 setname(Dosfile *fp, char *from) 550 { 551 char *to; 552 553 to = fp->name; 554 for(; *from && to-fp->name < 8; from++, to++){ 555 if(*from == '.'){ 556 from++; 557 break; 558 } 559 if(*from >= 'a' && *from <= 'z') 560 *to = *from + 'A' - 'a'; 561 else 562 *to = *from; 563 } 564 while(to - fp->name < 8) 565 *to++ = ' '; 566 567 /* from might be 12345678.123: don't save the '.' in ext */ 568 if(*from == '.') 569 from++; 570 571 to = fp->ext; 572 for(; *from && to-fp->ext < 3; from++, to++){ 573 if(*from >= 'a' && *from <= 'z') 574 *to = *from + 'A' - 'a'; 575 else 576 *to = *from; 577 } 578 while(to-fp->ext < 3) 579 *to++ = ' '; 580 581 chat("name is %8.8s.%3.3s\n", fp->name, fp->ext); 582 } 583