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 30 /* 31 * Readonly filesystem for Microsoft FAT12/FAT16/FAT32 filesystems, 32 * also supports VFAT. 33 */ 34 35 #include <sys/types.h> 36 #include <string.h> 37 #include <stddef.h> 38 39 #include "stand.h" 40 41 #include "dosfs.h" 42 43 44 static int dos_open(const char *path, struct open_file *fd); 45 static int dos_close(struct open_file *fd); 46 static int dos_read(struct open_file *fd, void *buf, size_t size, size_t *resid); 47 static off_t dos_seek(struct open_file *fd, off_t offset, int whence); 48 static int dos_stat(struct open_file *fd, struct stat *sb); 49 static int dos_readdir(struct open_file *fd, struct dirent *d); 50 51 struct fs_ops dosfs_fsops = { 52 "dosfs", 53 dos_open, 54 dos_close, 55 dos_read, 56 null_write, 57 dos_seek, 58 dos_stat, 59 dos_readdir 60 }; 61 62 #define SECSIZ 512 /* sector size */ 63 #define SSHIFT 9 /* SECSIZ shift */ 64 #define DEPSEC 16 /* directory entries per sector */ 65 #define DSHIFT 4 /* DEPSEC shift */ 66 #define LOCLUS 2 /* lowest cluster number */ 67 #define FATBLKSZ 0x20000 /* size of block in the FAT cache buffer */ 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 static int parsebs(DOS_FS *, DOS_BS *); 136 static int namede(DOS_FS *, const char *, DOS_DE **); 137 static int lookup(DOS_FS *, u_int, const char *, DOS_DE **); 138 static void cp_xdnm(u_char *, DOS_XDE *); 139 static void cp_sfn(u_char *, DOS_DE *); 140 static off_t fsize(DOS_FS *, DOS_DE *); 141 static int fatcnt(DOS_FS *, u_int); 142 static int fatget(DOS_FS *, u_int *); 143 static int fatend(u_int, u_int); 144 static int ioread(DOS_FS *, u_int, void *, size_t); 145 static int ioget(struct open_file *, daddr_t, void *, size_t); 146 147 static int 148 dos_read_fatblk(DOS_FS *fs, struct open_file *fd, u_int blknum) 149 { 150 int err; 151 size_t io_size; 152 daddr_t offset_in_fat, max_offset_in_fat; 153 154 offset_in_fat = ((daddr_t)blknum) * FATBLKSZ; 155 max_offset_in_fat = secbyt(fs->spf); 156 io_size = FATBLKSZ; 157 if (offset_in_fat > max_offset_in_fat) 158 offset_in_fat = max_offset_in_fat; 159 if (offset_in_fat + io_size > max_offset_in_fat) 160 io_size = ((size_t)(max_offset_in_fat - offset_in_fat)); 161 162 if (io_size != 0) { 163 err = ioget(fd, fs->lsnfat + bytsec(offset_in_fat), 164 fs->fatbuf, io_size); 165 if (err != 0) { 166 fs->fatbuf_blknum = ((u_int)(-1)); 167 return (err); 168 } 169 } 170 171 if (io_size < FATBLKSZ) 172 memset(fs->fatbuf + io_size, 0, FATBLKSZ - io_size); 173 174 fs->fatbuf_blknum = blknum; 175 return (0); 176 } 177 178 /* 179 * Mount DOS filesystem 180 */ 181 static int 182 dos_mount(DOS_FS *fs, struct open_file *fd) 183 { 184 int err; 185 u_char *buf; 186 187 bzero(fs, sizeof(DOS_FS)); 188 fs->fd = fd; 189 190 if ((buf = malloc(secbyt(1))) == NULL) 191 return (errno); 192 if ((err = ioget(fs->fd, 0, buf, secbyt(1))) || 193 (err = parsebs(fs, (DOS_BS *)buf))) { 194 free(buf); 195 return (err); 196 } 197 free(buf); 198 199 if ((fs->fatbuf = malloc(FATBLKSZ)) == NULL) 200 return (errno); 201 err = dos_read_fatblk(fs, fd, 0); 202 if (err != 0) { 203 free(fs->fatbuf); 204 return (err); 205 } 206 207 fs->root = dot[0]; 208 fs->root.name[0] = ' '; 209 if (fs->fatsz == 32) { 210 fs->root.clus[0] = fs->rdcl & 0xff; 211 fs->root.clus[1] = (fs->rdcl >> 8) & 0xff; 212 fs->root.dex.h_clus[0] = (fs->rdcl >> 16) & 0xff; 213 fs->root.dex.h_clus[1] = (fs->rdcl >> 24) & 0xff; 214 } 215 return (0); 216 } 217 218 /* 219 * Unmount mounted filesystem 220 */ 221 static int 222 dos_unmount(DOS_FS *fs) 223 { 224 if (fs->links) 225 return (EBUSY); 226 free(fs->fatbuf); 227 free(fs); 228 return (0); 229 } 230 231 /* 232 * Open DOS file 233 */ 234 static int 235 dos_open(const char *path, struct open_file *fd) 236 { 237 DOS_DE *de; 238 DOS_FILE *f; 239 DOS_FS *fs; 240 u_int size, clus; 241 int err; 242 243 /* Allocate mount structure, associate with open */ 244 if ((fs = malloc(sizeof(DOS_FS))) == NULL) 245 return (errno); 246 if ((err = dos_mount(fs, fd))) { 247 free(fs); 248 return (err); 249 } 250 251 if ((err = namede(fs, path, &de))) { 252 dos_unmount(fs); 253 return (err); 254 } 255 256 clus = stclus(fs->fatsz, de); 257 size = cv4(de->size); 258 259 if ((!(de->attr & FA_DIR) && (!clus != !size)) || 260 ((de->attr & FA_DIR) && size) || 261 (clus && !okclus(fs, clus))) { 262 dos_unmount(fs); 263 return (EINVAL); 264 } 265 if ((f = malloc(sizeof(DOS_FILE))) == NULL) { 266 err = errno; 267 dos_unmount(fs); 268 return (err); 269 } 270 bzero(f, sizeof(DOS_FILE)); 271 f->fs = fs; 272 fs->links++; 273 f->de = *de; 274 fd->f_fsdata = (void *)f; 275 return (0); 276 } 277 278 /* 279 * Read from file 280 */ 281 static int 282 dos_read(struct open_file *fd, void *buf, size_t nbyte, size_t *resid) 283 { 284 off_t size; 285 u_int nb, off, clus, c, cnt, n; 286 DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 287 int err = 0; 288 289 /* 290 * as ioget() can be called *a lot*, use twiddle here. 291 * also 4 seems to be good value not to slow loading down too much: 292 * with 270MB file (~540k ioget() calls, twiddle can easily waste 4-5sec. 293 */ 294 twiddle(4); 295 nb = (u_int)nbyte; 296 if ((size = fsize(f->fs, &f->de)) == -1) 297 return (EINVAL); 298 if (nb > (n = size - f->offset)) 299 nb = n; 300 off = f->offset; 301 if ((clus = stclus(f->fs->fatsz, &f->de))) 302 off &= f->fs->bsize - 1; 303 c = f->c; 304 cnt = nb; 305 while (cnt) { 306 n = 0; 307 if (!c) { 308 if ((c = clus)) 309 n = bytblk(f->fs, f->offset); 310 } else if (!off) 311 n++; 312 while (n--) { 313 if ((err = fatget(f->fs, &c))) 314 goto out; 315 if (!okclus(f->fs, c)) { 316 err = EINVAL; 317 goto out; 318 } 319 } 320 if (!clus || (n = f->fs->bsize - off) > cnt) 321 n = cnt; 322 if ((err = ioread(f->fs, (c ? blkoff(f->fs, c) : 323 secbyt(f->fs->lsndir)) + off, buf, n))) 324 goto out; 325 f->offset += n; 326 f->c = c; 327 off = 0; 328 buf = (char *)buf + n; 329 cnt -= n; 330 } 331 out: 332 if (resid) 333 *resid = nbyte - nb + cnt; 334 return (err); 335 } 336 337 /* 338 * Reposition within file 339 */ 340 static off_t 341 dos_seek(struct open_file *fd, off_t offset, int whence) 342 { 343 off_t off; 344 u_int size; 345 DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 346 347 size = cv4(f->de.size); 348 switch (whence) { 349 case SEEK_SET: 350 off = 0; 351 break; 352 case SEEK_CUR: 353 off = f->offset; 354 break; 355 case SEEK_END: 356 off = size; 357 break; 358 default: 359 errno = EINVAL; 360 return (-1); 361 } 362 off += offset; 363 if (off < 0 || off > size) { 364 errno = EINVAL; 365 return (-1); 366 } 367 f->offset = (u_int)off; 368 f->c = 0; 369 return (off); 370 } 371 372 /* 373 * Close open file 374 */ 375 static int 376 dos_close(struct open_file *fd) 377 { 378 DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 379 DOS_FS *fs = f->fs; 380 381 f->fs->links--; 382 free(f); 383 dos_unmount(fs); 384 return (0); 385 } 386 387 /* 388 * Return some stat information on a file. 389 */ 390 static int 391 dos_stat(struct open_file *fd, struct stat *sb) 392 { 393 DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 394 395 /* only important stuff */ 396 sb->st_mode = f->de.attr & FA_DIR ? S_IFDIR | 0555 : S_IFREG | 0444; 397 sb->st_nlink = 1; 398 sb->st_uid = 0; 399 sb->st_gid = 0; 400 if ((sb->st_size = fsize(f->fs, &f->de)) == -1) 401 return (EINVAL); 402 return (0); 403 } 404 405 static int 406 dos_checksum(char *name, char *ext) 407 { 408 int x, i; 409 char buf[11]; 410 411 bcopy(name, buf, 8); 412 bcopy(ext, buf+8, 3); 413 x = 0; 414 for (i = 0; i < 11; i++) { 415 x = ((x & 1) << 7) | (x >> 1); 416 x += buf[i]; 417 x &= 0xff; 418 } 419 return (x); 420 } 421 422 static int 423 dos_readdir(struct open_file *fd, struct dirent *d) 424 { 425 /* DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; */ 426 u_char fn[261]; 427 DOS_DIR dd; 428 size_t res; 429 u_int chk, x, xdn; 430 int err; 431 432 x = chk = 0; 433 while (1) { 434 xdn = x; 435 x = 0; 436 err = dos_read(fd, &dd, sizeof(dd), &res); 437 if (err) 438 return (err); 439 if (res == sizeof(dd)) 440 return (ENOENT); 441 if (dd.de.name[0] == 0) 442 return (ENOENT); 443 444 /* Skip deleted entries */ 445 if (dd.de.name[0] == 0xe5) 446 continue; 447 448 /* Check if directory entry is volume label */ 449 if (dd.de.attr & FA_LABEL) { 450 /* 451 * If volume label set, check if the current entry is 452 * extended entry (FA_XDE) for long file names. 453 */ 454 if ((dd.de.attr & FA_MASK) == FA_XDE) { 455 /* 456 * Read through all following extended entries 457 * to get the long file name. 0x40 marks the 458 * last entry containing part of long file name. 459 */ 460 if (dd.xde.seq & 0x40) 461 chk = dd.xde.chk; 462 else if (dd.xde.seq != xdn - 1 || dd.xde.chk != chk) 463 continue; 464 x = dd.xde.seq & ~0x40; 465 if (x < 1 || x > 20) { 466 x = 0; 467 continue; 468 } 469 cp_xdnm(fn, &dd.xde); 470 } else { 471 /* skip only volume label entries */ 472 continue; 473 } 474 } else { 475 if (xdn == 1) { 476 x = dos_checksum(dd.de.name, dd.de.ext); 477 if (x == chk) 478 break; 479 } else { 480 cp_sfn(fn, &dd.de); 481 break; 482 } 483 x = 0; 484 } 485 } 486 487 d->d_fileno = (dd.de.clus[1] << 8) + dd.de.clus[0]; 488 d->d_reclen = sizeof(*d); 489 d->d_type = (dd.de.attr & FA_DIR) ? DT_DIR : DT_REG; 490 memcpy(d->d_name, fn, sizeof(d->d_name)); 491 return (0); 492 } 493 494 /* 495 * Parse DOS boot sector 496 */ 497 static int 498 parsebs(DOS_FS *fs, DOS_BS *bs) 499 { 500 u_int sc; 501 502 if ((bs->jmp[0] != 0x69 && 503 bs->jmp[0] != 0xe9 && 504 (bs->jmp[0] != 0xeb || bs->jmp[2] != 0x90)) || 505 bs->bpb.media < 0xf0) 506 return (EINVAL); 507 if (cv2(bs->bpb.secsiz) != SECSIZ) 508 return (EINVAL); 509 if (!(fs->spc = bs->bpb.spc) || fs->spc & (fs->spc - 1)) 510 return (EINVAL); 511 fs->bsize = secbyt(fs->spc); 512 fs->bshift = ffs(fs->bsize) - 1; 513 if ((fs->spf = cv2(bs->bpb.spf))) { 514 if (bs->bpb.fats != 2) 515 return (EINVAL); 516 if (!(fs->dirents = cv2(bs->bpb.dirents))) 517 return (EINVAL); 518 } else { 519 if (!(fs->spf = cv4(bs->bpb.lspf))) 520 return (EINVAL); 521 if (!bs->bpb.fats || bs->bpb.fats > 16) 522 return (EINVAL); 523 if ((fs->rdcl = cv4(bs->bpb.rdcl)) < LOCLUS) 524 return (EINVAL); 525 } 526 if (!(fs->lsnfat = cv2(bs->bpb.ressec))) 527 return (EINVAL); 528 fs->lsndir = fs->lsnfat + fs->spf * bs->bpb.fats; 529 fs->lsndta = fs->lsndir + entsec(fs->dirents); 530 if (!(sc = cv2(bs->bpb.secs)) && !(sc = cv4(bs->bpb.lsecs))) 531 return (EINVAL); 532 if (fs->lsndta > sc) 533 return (EINVAL); 534 if ((fs->xclus = secblk(fs, sc - fs->lsndta) + 1) < LOCLUS) 535 return (EINVAL); 536 fs->fatsz = fs->dirents ? fs->xclus < 0xff6 ? 12 : 16 : 32; 537 sc = (secbyt(fs->spf) << 1) / (fs->fatsz >> 2) - 1; 538 if (fs->xclus > sc) 539 fs->xclus = sc; 540 return (0); 541 } 542 543 /* 544 * Return directory entry from path 545 */ 546 static int 547 namede(DOS_FS *fs, const char *path, DOS_DE **dep) 548 { 549 char name[256]; 550 DOS_DE *de; 551 char *s; 552 size_t n; 553 int err; 554 555 err = 0; 556 de = &fs->root; 557 while (*path) { 558 while (*path == '/') 559 path++; 560 if (*path == '\0') 561 break; 562 if (!(s = strchr(path, '/'))) 563 s = strchr(path, 0); 564 if ((n = s - path) > 255) 565 return (ENAMETOOLONG); 566 memcpy(name, path, n); 567 name[n] = 0; 568 path = s; 569 if (!(de->attr & FA_DIR)) 570 return (ENOTDIR); 571 if ((err = lookup(fs, stclus(fs->fatsz, de), name, &de))) 572 return (err); 573 } 574 *dep = de; 575 return (0); 576 } 577 578 /* 579 * Lookup path segment 580 */ 581 static int 582 lookup(DOS_FS *fs, u_int clus, const char *name, DOS_DE **dep) 583 { 584 static DOS_DIR dir[DEPSEC]; 585 u_char lfn[261]; 586 u_char sfn[13]; 587 u_int nsec, lsec, xdn, chk, sec, ent, x; 588 int err, ok; 589 590 if (!clus) 591 for (ent = 0; ent < 2; ent++) 592 if (!strcasecmp(name, dotstr[ent])) { 593 *dep = dot + ent; 594 return (0); 595 } 596 if (!clus && fs->fatsz == 32) 597 clus = fs->rdcl; 598 nsec = !clus ? entsec(fs->dirents) : fs->spc; 599 lsec = 0; 600 xdn = chk = 0; 601 for (;;) { 602 if (!clus && !lsec) 603 lsec = fs->lsndir; 604 else if (okclus(fs, clus)) 605 lsec = blklsn(fs, clus); 606 else 607 return (EINVAL); 608 for (sec = 0; sec < nsec; sec++) { 609 if ((err = ioget(fs->fd, lsec + sec, dir, secbyt(1)))) 610 return (err); 611 for (ent = 0; ent < DEPSEC; ent++) { 612 if (!*dir[ent].de.name) 613 return (ENOENT); 614 if (*dir[ent].de.name != 0xe5) { 615 if ((dir[ent].de.attr & FA_MASK) == FA_XDE) { 616 x = dir[ent].xde.seq; 617 if (x & 0x40 || (x + 1 == xdn && 618 dir[ent].xde.chk == chk)) { 619 if (x & 0x40) { 620 chk = dir[ent].xde.chk; 621 x &= ~0x40; 622 } 623 if (x >= 1 && x <= 20) { 624 cp_xdnm(lfn, &dir[ent].xde); 625 xdn = x; 626 continue; 627 } 628 } 629 } else if (!(dir[ent].de.attr & FA_LABEL)) { 630 if ((ok = xdn == 1)) { 631 x = dos_checksum(dir[ent].de.name, dir[ent].de.ext); 632 ok = chk == x && 633 !strcasecmp(name, (const char *)lfn); 634 } 635 if (!ok) { 636 cp_sfn(sfn, &dir[ent].de); 637 ok = !strcasecmp(name, (const char *)sfn); 638 } 639 if (ok) { 640 *dep = &dir[ent].de; 641 return (0); 642 } 643 } 644 } 645 xdn = 0; 646 } 647 } 648 if (!clus) 649 break; 650 if ((err = fatget(fs, &clus))) 651 return (err); 652 if (fatend(fs->fatsz, clus)) 653 break; 654 } 655 return (ENOENT); 656 } 657 658 /* 659 * Copy name from extended directory entry 660 */ 661 static void 662 cp_xdnm(u_char *lfn, DOS_XDE *xde) 663 { 664 static struct { 665 u_int off; 666 u_int dim; 667 } ix[3] = { 668 {offsetof(DOS_XDE, name1), sizeof(xde->name1) / 2}, 669 {offsetof(DOS_XDE, name2), sizeof(xde->name2) / 2}, 670 {offsetof(DOS_XDE, name3), sizeof(xde->name3) / 2} 671 }; 672 u_char *p; 673 u_int n, x, c; 674 675 lfn += 13 * ((xde->seq & ~0x40) - 1); 676 for (n = 0; n < 3; n++) 677 for (p = (u_char *)xde + ix[n].off, x = ix[n].dim; x; 678 p += 2, x--) { 679 if ((c = cv2(p)) && (c < 32 || c > 127)) 680 c = '?'; 681 if (!(*lfn++ = c)) 682 return; 683 } 684 if (xde->seq & 0x40) 685 *lfn = 0; 686 } 687 688 /* 689 * Copy short filename 690 */ 691 static void 692 cp_sfn(u_char *sfn, DOS_DE *de) 693 { 694 u_char *p; 695 int j, i; 696 697 p = sfn; 698 if (*de->name != ' ') { 699 for (j = 7; de->name[j] == ' '; j--); 700 for (i = 0; i <= j; i++) 701 *p++ = de->name[i]; 702 if (*de->ext != ' ') { 703 *p++ = '.'; 704 for (j = 2; de->ext[j] == ' '; j--); 705 for (i = 0; i <= j; i++) 706 *p++ = de->ext[i]; 707 } 708 } 709 *p = 0; 710 if (*sfn == 5) 711 *sfn = 0xe5; 712 } 713 714 /* 715 * Return size of file in bytes 716 */ 717 static off_t 718 fsize(DOS_FS *fs, DOS_DE *de) 719 { 720 u_long size; 721 u_int c; 722 int n; 723 724 if (!(size = cv4(de->size)) && de->attr & FA_DIR) { 725 if (!(c = cv2(de->clus))) 726 size = fs->dirents * sizeof(DOS_DE); 727 else { 728 if ((n = fatcnt(fs, c)) == -1) 729 return (n); 730 size = blkbyt(fs, n); 731 } 732 } 733 return (size); 734 } 735 736 /* 737 * Count number of clusters in chain 738 */ 739 static int 740 fatcnt(DOS_FS *fs, u_int c) 741 { 742 int n; 743 744 for (n = 0; okclus(fs, c); n++) 745 if (fatget(fs, &c)) 746 return (-1); 747 return (fatend(fs->fatsz, c) ? n : -1); 748 } 749 750 /* 751 * Get next cluster in cluster chain. Use in core fat cache unless 752 * the number of current 128K block in FAT has changed. 753 */ 754 static int 755 fatget(DOS_FS *fs, u_int *c) 756 { 757 u_int val_in, val_out, offset, blknum, nbyte; 758 const u_char *p_entry; 759 int err; 760 761 /* check input value to prevent overflow in fatoff() */ 762 val_in = *c; 763 if (val_in & 0xf0000000) 764 return (EINVAL); 765 766 /* ensure that current 128K FAT block is cached */ 767 offset = fatoff(fs->fatsz, val_in); 768 nbyte = fs->fatsz != 32 ? 2 : 4; 769 if (offset + nbyte > secbyt(fs->spf)) 770 return (EINVAL); 771 blknum = offset / FATBLKSZ; 772 offset %= FATBLKSZ; 773 if (offset + nbyte > FATBLKSZ) 774 return (EINVAL); 775 if (blknum != fs->fatbuf_blknum) { 776 err = dos_read_fatblk(fs, fs->fd, blknum); 777 if (err != 0) 778 return (err); 779 } 780 p_entry = fs->fatbuf + offset; 781 782 /* extract cluster number from FAT entry */ 783 switch (fs->fatsz) { 784 case 32: 785 val_out = cv4(p_entry); 786 val_out &= 0x0fffffff; 787 break; 788 case 16: 789 val_out = cv2(p_entry); 790 break; 791 case 12: 792 val_out = cv2(p_entry); 793 if (val_in & 1) 794 val_out >>= 4; 795 else 796 val_out &= 0xfff; 797 break; 798 default: 799 return (EINVAL); 800 } 801 *c = val_out; 802 return (0); 803 } 804 805 /* 806 * Is cluster an end-of-chain marker? 807 */ 808 static int 809 fatend(u_int sz, u_int c) 810 { 811 return (c > (sz == 12 ? 0xff7U : sz == 16 ? 0xfff7U : 0xffffff7)); 812 } 813 814 /* 815 * Offset-based I/O primitive 816 */ 817 static int 818 ioread(DOS_FS *fs, u_int offset, void *buf, size_t nbyte) 819 { 820 char *s; 821 u_int off, n; 822 int err; 823 u_char local_buf[SECSIZ]; 824 825 s = buf; 826 if ((off = offset & (SECSIZ - 1))) { 827 offset -= off; 828 if ((n = SECSIZ - off) > nbyte) 829 n = nbyte; 830 if ((err = ioget(fs->fd, bytsec(offset), local_buf, sizeof(local_buf)))) 831 return (err); 832 memcpy(s, local_buf + off, n); 833 offset += SECSIZ; 834 s += n; 835 nbyte -= n; 836 } 837 n = nbyte & (SECSIZ - 1); 838 if (nbyte -= n) { 839 if ((err = ioget(fs->fd, bytsec(offset), s, nbyte))) 840 return (err); 841 offset += nbyte; 842 s += nbyte; 843 } 844 if (n) { 845 if ((err = ioget(fs->fd, bytsec(offset), local_buf, sizeof(local_buf)))) 846 return (err); 847 memcpy(s, local_buf, n); 848 } 849 return (0); 850 } 851 852 /* 853 * Sector-based I/O primitive 854 */ 855 static int 856 ioget(struct open_file *fd, daddr_t lsec, void *buf, size_t size) 857 { 858 size_t rsize; 859 int rv; 860 861 /* Make sure we get full read or error. */ 862 rsize = 0; 863 rv = (fd->f_dev->dv_strategy)(fd->f_devdata, F_READ, lsec, 864 size, buf, &rsize); 865 if ((rv == 0) && (size != rsize)) 866 rv = EIO; 867 return (rv); 868 } 869