1 /* 2 * Copyright (c) 1996, 1998 Robert Nordier 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in 12 * the documentation and/or other materials provided with the 13 * distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS 16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY 19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 21 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 23 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 25 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 /* 32 * Readonly filesystem for Microsoft FAT12/FAT16/FAT32 filesystems, 33 * also supports VFAT. 34 */ 35 36 #include <sys/types.h> 37 #include <string.h> 38 #include <stddef.h> 39 40 #include "stand.h" 41 42 #include "dosfs.h" 43 44 45 static int dos_open(const char *path, struct open_file *fd); 46 static int dos_close(struct open_file *fd); 47 static int dos_read(struct open_file *fd, void *buf, size_t size, size_t *resid); 48 static off_t dos_seek(struct open_file *fd, off_t offset, int whence); 49 static int dos_stat(struct open_file *fd, struct stat *sb); 50 static int dos_readdir(struct open_file *fd, struct dirent *d); 51 52 struct fs_ops dosfs_fsops = { 53 "dosfs", 54 dos_open, 55 dos_close, 56 dos_read, 57 null_write, 58 dos_seek, 59 dos_stat, 60 dos_readdir 61 }; 62 63 #define SECSIZ 512 /* sector size */ 64 #define SSHIFT 9 /* SECSIZ shift */ 65 #define DEPSEC 16 /* directory entries per sector */ 66 #define DSHIFT 4 /* DEPSEC shift */ 67 #define LOCLUS 2 /* lowest cluster number */ 68 69 /* DOS "BIOS Parameter Block" */ 70 typedef struct { 71 u_char secsiz[2]; /* sector size */ 72 u_char spc; /* sectors per cluster */ 73 u_char ressec[2]; /* reserved sectors */ 74 u_char fats; /* FATs */ 75 u_char dirents[2]; /* root directory entries */ 76 u_char secs[2]; /* total sectors */ 77 u_char media; /* media descriptor */ 78 u_char spf[2]; /* sectors per FAT */ 79 u_char spt[2]; /* sectors per track */ 80 u_char heads[2]; /* drive heads */ 81 u_char hidsec[4]; /* hidden sectors */ 82 u_char lsecs[4]; /* huge sectors */ 83 u_char lspf[4]; /* huge sectors per FAT */ 84 u_char xflg[2]; /* flags */ 85 u_char vers[2]; /* filesystem version */ 86 u_char rdcl[4]; /* root directory start cluster */ 87 u_char infs[2]; /* filesystem info sector */ 88 u_char bkbs[2]; /* backup boot sector */ 89 } DOS_BPB; 90 91 /* Initial portion of DOS boot sector */ 92 typedef struct { 93 u_char jmp[3]; /* usually 80x86 'jmp' opcode */ 94 u_char oem[8]; /* OEM name and version */ 95 DOS_BPB bpb; /* BPB */ 96 } DOS_BS; 97 98 /* Supply missing "." and ".." root directory entries */ 99 static const char *const dotstr[2] = {".", ".."}; 100 static DOS_DE dot[2] = { 101 {". ", " ", FA_DIR, {0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 102 {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}}, 103 {".. ", " ", FA_DIR, {0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 104 {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}} 105 }; 106 107 /* The usual conversion macros to avoid multiplication and division */ 108 #define bytsec(n) ((n) >> SSHIFT) 109 #define secbyt(s) ((s) << SSHIFT) 110 #define entsec(e) ((e) >> DSHIFT) 111 #define bytblk(fs, n) ((n) >> (fs)->bshift) 112 #define blkbyt(fs, b) ((b) << (fs)->bshift) 113 #define secblk(fs, s) ((s) >> ((fs)->bshift - SSHIFT)) 114 #define blksec(fs, b) ((b) << ((fs)->bshift - SSHIFT)) 115 116 /* Convert cluster number to offset within filesystem */ 117 #define blkoff(fs, b) (secbyt((fs)->lsndta) + blkbyt(fs, (b) - LOCLUS)) 118 119 /* Convert cluster number to logical sector number */ 120 #define blklsn(fs, b) ((fs)->lsndta + blksec(fs, (b) - LOCLUS)) 121 122 /* Convert cluster number to offset within FAT */ 123 #define fatoff(sz, c) ((sz) == 12 ? (c) + ((c) >> 1) : \ 124 (sz) == 16 ? (c) << 1 : \ 125 (c) << 2) 126 127 /* Does cluster number reference a valid data cluster? */ 128 #define okclus(fs, c) ((c) >= LOCLUS && (c) <= (fs)->xclus) 129 130 /* Get start cluster from directory entry */ 131 #define stclus(sz, de) ((sz) != 32 ? cv2((de)->clus) : \ 132 ((u_int)cv2((de)->dex.h_clus) << 16) | \ 133 cv2((de)->clus)) 134 135 /* 136 * fat cache metadata 137 */ 138 struct fatcache { 139 int unit; /* disk unit number */ 140 int size; /* buffer (and fat) size in sectors */ 141 u_char *buf; 142 }; 143 144 static struct fatcache fat; 145 146 static int dosunmount(DOS_FS *); 147 static int parsebs(DOS_FS *, DOS_BS *); 148 static int namede(DOS_FS *, const char *, DOS_DE **); 149 static int lookup(DOS_FS *, u_int, const char *, DOS_DE **); 150 static void cp_xdnm(u_char *, DOS_XDE *); 151 static void cp_sfn(u_char *, DOS_DE *); 152 static off_t fsize(DOS_FS *, DOS_DE *); 153 static int fatcnt(DOS_FS *, u_int); 154 static int fatget(DOS_FS *, u_int *); 155 static int fatend(u_int, u_int); 156 static int ioread(DOS_FS *, u_int, void *, u_int); 157 static int ioget(struct open_file *, daddr_t, size_t, void *, u_int); 158 159 static void 160 dos_read_fat(DOS_FS *fs, struct open_file *fd) 161 { 162 struct devdesc *dd = fd->f_devdata; 163 164 if (fat.buf != NULL) { /* can we reuse old buffer? */ 165 if (fat.size != fs->spf) { 166 free(fat.buf); /* no, free old buffer */ 167 fat.buf = NULL; 168 } 169 } 170 171 if (fat.buf == NULL) 172 fat.buf = malloc(secbyt(fs->spf)); 173 174 if (fat.buf != NULL) { 175 if (ioget(fd, fs->lsnfat, 0, fat.buf, secbyt(fs->spf)) == 0) { 176 fat.size = fs->spf; 177 fat.unit = dd->d_unit; 178 return; 179 } 180 } 181 if (fat.buf != NULL) /* got IO error */ 182 free(fat.buf); 183 fat.buf = NULL; 184 fat.unit = -1; /* impossible unit */ 185 fat.size = 0; 186 } 187 188 /* 189 * Mount DOS filesystem 190 */ 191 static int 192 dos_mount(DOS_FS *fs, struct open_file *fd) 193 { 194 int err; 195 struct devdesc *dd = fd->f_devdata; 196 u_char *buf; 197 198 bzero(fs, sizeof(DOS_FS)); 199 fs->fd = fd; 200 201 if ((err = !(buf = malloc(secbyt(1))) ? errno : 0) || 202 (err = ioget(fs->fd, 0, 0, buf, secbyt(1))) || 203 (err = parsebs(fs, (DOS_BS *)buf))) { 204 if (buf != NULL) 205 free(buf); 206 (void)dosunmount(fs); 207 return(err); 208 } 209 free(buf); 210 211 if (fat.buf == NULL || fat.unit != dd->d_unit) 212 dos_read_fat(fs, fd); 213 214 fs->root = dot[0]; 215 fs->root.name[0] = ' '; 216 if (fs->fatsz == 32) { 217 fs->root.clus[0] = fs->rdcl & 0xff; 218 fs->root.clus[1] = (fs->rdcl >> 8) & 0xff; 219 fs->root.dex.h_clus[0] = (fs->rdcl >> 16) & 0xff; 220 fs->root.dex.h_clus[1] = (fs->rdcl >> 24) & 0xff; 221 } 222 return 0; 223 } 224 225 /* 226 * Unmount mounted filesystem 227 */ 228 static int 229 dos_unmount(DOS_FS *fs) 230 { 231 int err; 232 233 if (fs->links) 234 return(EBUSY); 235 if ((err = dosunmount(fs))) 236 return(err); 237 return 0; 238 } 239 240 /* 241 * Common code shared by dos_mount() and dos_unmount() 242 */ 243 static int 244 dosunmount(DOS_FS *fs) 245 { 246 free(fs); 247 return(0); 248 } 249 250 /* 251 * Open DOS file 252 */ 253 static int 254 dos_open(const char *path, struct open_file *fd) 255 { 256 DOS_DE *de; 257 DOS_FILE *f; 258 DOS_FS *fs; 259 u_int size, clus; 260 int err = 0; 261 262 /* Allocate mount structure, associate with open */ 263 fs = malloc(sizeof(DOS_FS)); 264 265 if ((err = dos_mount(fs, fd))) 266 goto out; 267 268 if ((err = namede(fs, path, &de))) 269 goto out; 270 271 clus = stclus(fs->fatsz, de); 272 size = cv4(de->size); 273 274 if ((!(de->attr & FA_DIR) && (!clus != !size)) || 275 ((de->attr & FA_DIR) && size) || 276 (clus && !okclus(fs, clus))) { 277 err = EINVAL; 278 goto out; 279 } 280 f = malloc(sizeof(DOS_FILE)); 281 bzero(f, sizeof(DOS_FILE)); 282 f->fs = fs; 283 fs->links++; 284 f->de = *de; 285 fd->f_fsdata = (void *)f; 286 287 out: 288 return(err); 289 } 290 291 /* 292 * Read from file 293 */ 294 static int 295 dos_read(struct open_file *fd, void *buf, size_t nbyte, size_t *resid) 296 { 297 off_t size; 298 u_int nb, off, clus, c, cnt, n; 299 DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 300 int err = 0; 301 302 /* 303 * as ioget() can be called *a lot*, use twiddle here. 304 * also 4 seems to be good value not to slow loading down too much: 305 * with 270MB file (~540k ioget() calls, twiddle can easily waste 4-5sec. 306 */ 307 twiddle(4); 308 nb = (u_int)nbyte; 309 if ((size = fsize(f->fs, &f->de)) == -1) 310 return EINVAL; 311 if (nb > (n = size - f->offset)) 312 nb = n; 313 off = f->offset; 314 if ((clus = stclus(f->fs->fatsz, &f->de))) 315 off &= f->fs->bsize - 1; 316 c = f->c; 317 cnt = nb; 318 while (cnt) { 319 n = 0; 320 if (!c) { 321 if ((c = clus)) 322 n = bytblk(f->fs, f->offset); 323 } else if (!off) 324 n++; 325 while (n--) { 326 if ((err = fatget(f->fs, &c))) 327 goto out; 328 if (!okclus(f->fs, c)) { 329 err = EINVAL; 330 goto out; 331 } 332 } 333 if (!clus || (n = f->fs->bsize - off) > cnt) 334 n = cnt; 335 if ((err = ioread(f->fs, (c ? blkoff(f->fs, c) : 336 secbyt(f->fs->lsndir)) + off, buf, n))) 337 goto out; 338 f->offset += n; 339 f->c = c; 340 off = 0; 341 buf = (char *)buf + n; 342 cnt -= n; 343 } 344 out: 345 if (resid) 346 *resid = nbyte - nb + cnt; 347 return(err); 348 } 349 350 /* 351 * Reposition within file 352 */ 353 static off_t 354 dos_seek(struct open_file *fd, off_t offset, int whence) 355 { 356 off_t off; 357 u_int size; 358 DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 359 360 size = cv4(f->de.size); 361 switch (whence) { 362 case SEEK_SET: 363 off = 0; 364 break; 365 case SEEK_CUR: 366 off = f->offset; 367 break; 368 case SEEK_END: 369 off = size; 370 break; 371 default: 372 errno = EINVAL; 373 return(-1); 374 } 375 off += offset; 376 if (off < 0 || off > size) { 377 errno = EINVAL; 378 return(-1); 379 } 380 f->offset = (u_int)off; 381 f->c = 0; 382 return(off); 383 } 384 385 /* 386 * Close open file 387 */ 388 static int 389 dos_close(struct open_file *fd) 390 { 391 DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 392 DOS_FS *fs = f->fs; 393 394 f->fs->links--; 395 free(f); 396 dos_unmount(fs); 397 return 0; 398 } 399 400 /* 401 * Return some stat information on a file. 402 */ 403 static int 404 dos_stat(struct open_file *fd, struct stat *sb) 405 { 406 DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 407 408 /* only important stuff */ 409 sb->st_mode = f->de.attr & FA_DIR ? S_IFDIR | 0555 : S_IFREG | 0444; 410 sb->st_nlink = 1; 411 sb->st_uid = 0; 412 sb->st_gid = 0; 413 if ((sb->st_size = fsize(f->fs, &f->de)) == -1) 414 return EINVAL; 415 return (0); 416 } 417 418 static int 419 dos_checksum(char *name, char *ext) 420 { 421 int x, i; 422 char buf[11]; 423 424 bcopy(name, buf, 8); 425 bcopy(ext, buf+8, 3); 426 x = 0; 427 for (i = 0; i < 11; i++) { 428 x = ((x & 1) << 7) | (x >> 1); 429 x += buf[i]; 430 x &= 0xff; 431 } 432 return (x); 433 } 434 435 static int 436 dos_readdir(struct open_file *fd, struct dirent *d) 437 { 438 /* DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; */ 439 u_char fn[261]; 440 DOS_DIR dd; 441 size_t res; 442 u_int chk, x, xdn; 443 int err; 444 445 x = chk = 0; 446 while (1) { 447 xdn = x; 448 x = 0; 449 err = dos_read(fd, &dd, sizeof(dd), &res); 450 if (err) 451 return (err); 452 if (res == sizeof(dd)) 453 return (ENOENT); 454 if (dd.de.name[0] == 0) 455 return (ENOENT); 456 457 /* Skip deleted entries */ 458 if (dd.de.name[0] == 0xe5) 459 continue; 460 461 /* Check if directory entry is volume label */ 462 if (dd.de.attr & FA_LABEL) { 463 /* 464 * If volume label set, check if the current entry is 465 * extended entry (FA_XDE) for long file names. 466 */ 467 if ((dd.de.attr & FA_MASK) == FA_XDE) { 468 /* 469 * Read through all following extended entries 470 * to get the long file name. 0x40 marks the 471 * last entry containing part of long file name. 472 */ 473 if (dd.xde.seq & 0x40) 474 chk = dd.xde.chk; 475 else if (dd.xde.seq != xdn - 1 || dd.xde.chk != chk) 476 continue; 477 x = dd.xde.seq & ~0x40; 478 if (x < 1 || x > 20) { 479 x = 0; 480 continue; 481 } 482 cp_xdnm(fn, &dd.xde); 483 } else { 484 /* skip only volume label entries */ 485 continue; 486 } 487 } else { 488 if (xdn == 1) { 489 x = dos_checksum(dd.de.name, dd.de.ext); 490 if (x == chk) 491 break; 492 } else { 493 cp_sfn(fn, &dd.de); 494 break; 495 } 496 x = 0; 497 } 498 } 499 500 d->d_fileno = (dd.de.clus[1] << 8) + dd.de.clus[0]; 501 d->d_reclen = sizeof(*d); 502 d->d_type = (dd.de.attr & FA_DIR) ? DT_DIR : DT_REG; 503 memcpy(d->d_name, fn, sizeof(d->d_name)); 504 return(0); 505 } 506 507 /* 508 * Parse DOS boot sector 509 */ 510 static int 511 parsebs(DOS_FS *fs, DOS_BS *bs) 512 { 513 u_int sc; 514 515 if ((bs->jmp[0] != 0x69 && 516 bs->jmp[0] != 0xe9 && 517 (bs->jmp[0] != 0xeb || bs->jmp[2] != 0x90)) || 518 bs->bpb.media < 0xf0) 519 return EINVAL; 520 if (cv2(bs->bpb.secsiz) != SECSIZ) 521 return EINVAL; 522 if (!(fs->spc = bs->bpb.spc) || fs->spc & (fs->spc - 1)) 523 return EINVAL; 524 fs->bsize = secbyt(fs->spc); 525 fs->bshift = ffs(fs->bsize) - 1; 526 if ((fs->spf = cv2(bs->bpb.spf))) { 527 if (bs->bpb.fats != 2) 528 return EINVAL; 529 if (!(fs->dirents = cv2(bs->bpb.dirents))) 530 return EINVAL; 531 } else { 532 if (!(fs->spf = cv4(bs->bpb.lspf))) 533 return EINVAL; 534 if (!bs->bpb.fats || bs->bpb.fats > 16) 535 return EINVAL; 536 if ((fs->rdcl = cv4(bs->bpb.rdcl)) < LOCLUS) 537 return EINVAL; 538 } 539 if (!(fs->lsnfat = cv2(bs->bpb.ressec))) 540 return EINVAL; 541 fs->lsndir = fs->lsnfat + fs->spf * bs->bpb.fats; 542 fs->lsndta = fs->lsndir + entsec(fs->dirents); 543 if (!(sc = cv2(bs->bpb.secs)) && !(sc = cv4(bs->bpb.lsecs))) 544 return EINVAL; 545 if (fs->lsndta > sc) 546 return EINVAL; 547 if ((fs->xclus = secblk(fs, sc - fs->lsndta) + 1) < LOCLUS) 548 return EINVAL; 549 fs->fatsz = fs->dirents ? fs->xclus < 0xff6 ? 12 : 16 : 32; 550 sc = (secbyt(fs->spf) << 1) / (fs->fatsz >> 2) - 1; 551 if (fs->xclus > sc) 552 fs->xclus = sc; 553 return 0; 554 } 555 556 /* 557 * Return directory entry from path 558 */ 559 static int 560 namede(DOS_FS *fs, const char *path, DOS_DE **dep) 561 { 562 char name[256]; 563 DOS_DE *de; 564 char *s; 565 size_t n; 566 int err; 567 568 err = 0; 569 de = &fs->root; 570 while (*path) { 571 while (*path == '/') 572 path++; 573 if (*path == '\0') 574 break; 575 if (!(s = strchr(path, '/'))) 576 s = strchr(path, 0); 577 if ((n = s - path) > 255) 578 return ENAMETOOLONG; 579 memcpy(name, path, n); 580 name[n] = 0; 581 path = s; 582 if (!(de->attr & FA_DIR)) 583 return ENOTDIR; 584 if ((err = lookup(fs, stclus(fs->fatsz, de), name, &de))) 585 return err; 586 } 587 *dep = de; 588 return 0; 589 } 590 591 /* 592 * Lookup path segment 593 */ 594 static int 595 lookup(DOS_FS *fs, u_int clus, const char *name, DOS_DE **dep) 596 { 597 static DOS_DIR dir[DEPSEC]; 598 u_char lfn[261]; 599 u_char sfn[13]; 600 u_int nsec, lsec, xdn, chk, sec, ent, x; 601 int err, ok; 602 603 if (!clus) 604 for (ent = 0; ent < 2; ent++) 605 if (!strcasecmp(name, dotstr[ent])) { 606 *dep = dot + ent; 607 return 0; 608 } 609 if (!clus && fs->fatsz == 32) 610 clus = fs->rdcl; 611 nsec = !clus ? entsec(fs->dirents) : fs->spc; 612 lsec = 0; 613 xdn = chk = 0; 614 for (;;) { 615 if (!clus && !lsec) 616 lsec = fs->lsndir; 617 else if (okclus(fs, clus)) 618 lsec = blklsn(fs, clus); 619 else 620 return EINVAL; 621 for (sec = 0; sec < nsec; sec++) { 622 if ((err = ioget(fs->fd, lsec + sec, 0, dir, secbyt(1)))) 623 return err; 624 for (ent = 0; ent < DEPSEC; ent++) { 625 if (!*dir[ent].de.name) 626 return ENOENT; 627 if (*dir[ent].de.name != 0xe5) { 628 if ((dir[ent].de.attr & FA_MASK) == FA_XDE) { 629 x = dir[ent].xde.seq; 630 if (x & 0x40 || (x + 1 == xdn && 631 dir[ent].xde.chk == chk)) { 632 if (x & 0x40) { 633 chk = dir[ent].xde.chk; 634 x &= ~0x40; 635 } 636 if (x >= 1 && x <= 20) { 637 cp_xdnm(lfn, &dir[ent].xde); 638 xdn = x; 639 continue; 640 } 641 } 642 } else if (!(dir[ent].de.attr & FA_LABEL)) { 643 if ((ok = xdn == 1)) { 644 x = dos_checksum(dir[ent].de.name, dir[ent].de.ext); 645 ok = chk == x && 646 !strcasecmp(name, (const char *)lfn); 647 } 648 if (!ok) { 649 cp_sfn(sfn, &dir[ent].de); 650 ok = !strcasecmp(name, (const char *)sfn); 651 } 652 if (ok) { 653 *dep = &dir[ent].de; 654 return 0; 655 } 656 } 657 } 658 xdn = 0; 659 } 660 } 661 if (!clus) 662 break; 663 if ((err = fatget(fs, &clus))) 664 return err; 665 if (fatend(fs->fatsz, clus)) 666 break; 667 } 668 return ENOENT; 669 } 670 671 /* 672 * Copy name from extended directory entry 673 */ 674 static void 675 cp_xdnm(u_char *lfn, DOS_XDE *xde) 676 { 677 static struct { 678 u_int off; 679 u_int dim; 680 } ix[3] = { 681 {offsetof(DOS_XDE, name1), sizeof(xde->name1) / 2}, 682 {offsetof(DOS_XDE, name2), sizeof(xde->name2) / 2}, 683 {offsetof(DOS_XDE, name3), sizeof(xde->name3) / 2} 684 }; 685 u_char *p; 686 u_int n, x, c; 687 688 lfn += 13 * ((xde->seq & ~0x40) - 1); 689 for (n = 0; n < 3; n++) 690 for (p = (u_char *)xde + ix[n].off, x = ix[n].dim; x; 691 p += 2, x--) { 692 if ((c = cv2(p)) && (c < 32 || c > 127)) 693 c = '?'; 694 if (!(*lfn++ = c)) 695 return; 696 } 697 if (xde->seq & 0x40) 698 *lfn = 0; 699 } 700 701 /* 702 * Copy short filename 703 */ 704 static void 705 cp_sfn(u_char *sfn, DOS_DE *de) 706 { 707 u_char *p; 708 int j, i; 709 710 p = sfn; 711 if (*de->name != ' ') { 712 for (j = 7; de->name[j] == ' '; j--); 713 for (i = 0; i <= j; i++) 714 *p++ = de->name[i]; 715 if (*de->ext != ' ') { 716 *p++ = '.'; 717 for (j = 2; de->ext[j] == ' '; j--); 718 for (i = 0; i <= j; i++) 719 *p++ = de->ext[i]; 720 } 721 } 722 *p = 0; 723 if (*sfn == 5) 724 *sfn = 0xe5; 725 } 726 727 /* 728 * Return size of file in bytes 729 */ 730 static off_t 731 fsize(DOS_FS *fs, DOS_DE *de) 732 { 733 u_long size; 734 u_int c; 735 int n; 736 737 if (!(size = cv4(de->size)) && de->attr & FA_DIR) { 738 if (!(c = cv2(de->clus))) 739 size = fs->dirents * sizeof(DOS_DE); 740 else { 741 if ((n = fatcnt(fs, c)) == -1) 742 return n; 743 size = blkbyt(fs, n); 744 } 745 } 746 return size; 747 } 748 749 /* 750 * Count number of clusters in chain 751 */ 752 static int 753 fatcnt(DOS_FS *fs, u_int c) 754 { 755 int n; 756 757 for (n = 0; okclus(fs, c); n++) 758 if (fatget(fs, &c)) 759 return -1; 760 return fatend(fs->fatsz, c) ? n : -1; 761 } 762 763 /* 764 * Get next cluster in cluster chain. Use in core fat cache unless another 765 * device replaced it. 766 */ 767 static int 768 fatget(DOS_FS *fs, u_int *c) 769 { 770 u_char buf[4]; 771 u_char *s; 772 u_int x, offset, off, n, nbyte, lsec; 773 struct devdesc *dd = fs->fd->f_devdata; 774 int err = 0; 775 776 if (fat.unit != dd->d_unit) { 777 /* fat cache was changed to another device, don't use it */ 778 err = ioread(fs, secbyt(fs->lsnfat) + fatoff(fs->fatsz, *c), buf, 779 fs->fatsz != 32 ? 2 : 4); 780 if (err) 781 return (err); 782 } else { 783 offset = fatoff(fs->fatsz, *c); 784 nbyte = fs->fatsz != 32 ? 2 : 4; 785 786 s = buf; 787 if ((off = offset & (SECSIZ - 1))) { 788 offset -= off; 789 lsec = bytsec(offset); 790 offset += SECSIZ; 791 if ((n = SECSIZ - off) > nbyte) 792 n = nbyte; 793 memcpy(s, fat.buf + secbyt(lsec) + off, n); 794 s += n; 795 nbyte -= n; 796 } 797 n = nbyte & (SECSIZ - 1); 798 if (nbyte -= n) { 799 memcpy(s, fat.buf + secbyt(bytsec(offset)), nbyte); 800 offset += nbyte; 801 s += nbyte; 802 } 803 if (n) 804 memcpy(s, fat.buf + secbyt(bytsec(offset)), n); 805 } 806 807 x = fs->fatsz != 32 ? cv2(buf) : cv4(buf); 808 *c = fs->fatsz == 12 ? *c & 1 ? x >> 4 : x & 0xfff : x; 809 return (0); 810 } 811 812 /* 813 * Is cluster an end-of-chain marker? 814 */ 815 static int 816 fatend(u_int sz, u_int c) 817 { 818 return c > (sz == 12 ? 0xff7U : sz == 16 ? 0xfff7U : 0xffffff7); 819 } 820 821 /* 822 * Offset-based I/O primitive 823 */ 824 static int 825 ioread(DOS_FS *fs, u_int offset, void *buf, u_int nbyte) 826 { 827 char *s; 828 u_int off, n; 829 int err; 830 831 s = buf; 832 if ((off = offset & (SECSIZ - 1))) { 833 offset -= off; 834 if ((n = SECSIZ - off) > nbyte) 835 n = nbyte; 836 if ((err = ioget(fs->fd, bytsec(offset), off, s, n))) 837 return err; 838 offset += SECSIZ; 839 s += n; 840 nbyte -= n; 841 } 842 n = nbyte & (SECSIZ - 1); 843 if (nbyte -= n) { 844 if ((err = ioget(fs->fd, bytsec(offset), 0, s, nbyte))) 845 return err; 846 offset += nbyte; 847 s += nbyte; 848 } 849 if (n) { 850 if ((err = ioget(fs->fd, bytsec(offset), 0, s, n))) 851 return err; 852 } 853 return 0; 854 } 855 856 /* 857 * Sector-based I/O primitive 858 */ 859 static int 860 ioget(struct open_file *fd, daddr_t lsec, size_t offset, void *buf, u_int size) 861 { 862 return ((fd->f_dev->dv_strategy)(fd->f_devdata, F_READ, lsec, offset, 863 size, buf, NULL)); 864 } 865