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 #define FATBLKSZ 0x20000 /* size of block in the FAT cache buffer */ 69 70 /* DOS "BIOS Parameter Block" */ 71 typedef struct { 72 u_char secsiz[2]; /* sector size */ 73 u_char spc; /* sectors per cluster */ 74 u_char ressec[2]; /* reserved sectors */ 75 u_char fats; /* FATs */ 76 u_char dirents[2]; /* root directory entries */ 77 u_char secs[2]; /* total sectors */ 78 u_char media; /* media descriptor */ 79 u_char spf[2]; /* sectors per FAT */ 80 u_char spt[2]; /* sectors per track */ 81 u_char heads[2]; /* drive heads */ 82 u_char hidsec[4]; /* hidden sectors */ 83 u_char lsecs[4]; /* huge sectors */ 84 u_char lspf[4]; /* huge sectors per FAT */ 85 u_char xflg[2]; /* flags */ 86 u_char vers[2]; /* filesystem version */ 87 u_char rdcl[4]; /* root directory start cluster */ 88 u_char infs[2]; /* filesystem info sector */ 89 u_char bkbs[2]; /* backup boot sector */ 90 } DOS_BPB; 91 92 /* Initial portion of DOS boot sector */ 93 typedef struct { 94 u_char jmp[3]; /* usually 80x86 'jmp' opcode */ 95 u_char oem[8]; /* OEM name and version */ 96 DOS_BPB bpb; /* BPB */ 97 } DOS_BS; 98 99 /* Supply missing "." and ".." root directory entries */ 100 static const char *const dotstr[2] = {".", ".."}; 101 static DOS_DE dot[2] = { 102 {". ", " ", FA_DIR, {0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 103 {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}}, 104 {".. ", " ", FA_DIR, {0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 105 {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}} 106 }; 107 108 /* The usual conversion macros to avoid multiplication and division */ 109 #define bytsec(n) ((n) >> SSHIFT) 110 #define secbyt(s) ((s) << SSHIFT) 111 #define entsec(e) ((e) >> DSHIFT) 112 #define bytblk(fs, n) ((n) >> (fs)->bshift) 113 #define blkbyt(fs, b) ((b) << (fs)->bshift) 114 #define secblk(fs, s) ((s) >> ((fs)->bshift - SSHIFT)) 115 #define blksec(fs, b) ((b) << ((fs)->bshift - SSHIFT)) 116 117 /* Convert cluster number to offset within filesystem */ 118 #define blkoff(fs, b) (secbyt((fs)->lsndta) + blkbyt(fs, (b) - LOCLUS)) 119 120 /* Convert cluster number to logical sector number */ 121 #define blklsn(fs, b) ((fs)->lsndta + blksec(fs, (b) - LOCLUS)) 122 123 /* Convert cluster number to offset within FAT */ 124 #define fatoff(sz, c) ((sz) == 12 ? (c) + ((c) >> 1) : \ 125 (sz) == 16 ? (c) << 1 : \ 126 (c) << 2) 127 128 /* Does cluster number reference a valid data cluster? */ 129 #define okclus(fs, c) ((c) >= LOCLUS && (c) <= (fs)->xclus) 130 131 /* Get start cluster from directory entry */ 132 #define stclus(sz, de) ((sz) != 32 ? cv2((de)->clus) : \ 133 ((u_int)cv2((de)->dex.h_clus) << 16) | \ 134 cv2((de)->clus)) 135 136 static int parsebs(DOS_FS *, DOS_BS *); 137 static int namede(DOS_FS *, const char *, DOS_DE **); 138 static int lookup(DOS_FS *, u_int, const char *, DOS_DE **); 139 static void cp_xdnm(u_char *, DOS_XDE *); 140 static void cp_sfn(u_char *, DOS_DE *); 141 static off_t fsize(DOS_FS *, DOS_DE *); 142 static int fatcnt(DOS_FS *, u_int); 143 static int fatget(DOS_FS *, u_int *); 144 static int fatend(u_int, u_int); 145 static int ioread(DOS_FS *, u_int, void *, size_t); 146 static int ioget(struct open_file *, daddr_t, void *, size_t); 147 148 static int 149 dos_read_fatblk(DOS_FS *fs, struct open_file *fd, u_int blknum) 150 { 151 int err; 152 size_t io_size; 153 daddr_t offset_in_fat, max_offset_in_fat; 154 155 offset_in_fat = ((daddr_t)blknum) * FATBLKSZ; 156 max_offset_in_fat = secbyt(fs->spf); 157 io_size = FATBLKSZ; 158 if (offset_in_fat > max_offset_in_fat) 159 offset_in_fat = max_offset_in_fat; 160 if (offset_in_fat + io_size > max_offset_in_fat) 161 io_size = ((size_t)(max_offset_in_fat - offset_in_fat)); 162 163 if (io_size != 0) { 164 err = ioget(fd, fs->lsnfat + bytsec(offset_in_fat), 165 fs->fatbuf, io_size); 166 if (err != 0) { 167 fs->fatbuf_blknum = ((u_int)(-1)); 168 return (err); 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 293 * 4-5 sec. 294 */ 295 twiddle(4); 296 nb = (u_int)nbyte; 297 if ((size = fsize(f->fs, &f->de)) == -1) 298 return (EINVAL); 299 if (nb > (n = size - f->offset)) 300 nb = n; 301 off = f->offset; 302 if ((clus = stclus(f->fs->fatsz, &f->de))) 303 off &= f->fs->bsize - 1; 304 c = f->c; 305 cnt = nb; 306 while (cnt) { 307 n = 0; 308 if (!c) { 309 if ((c = clus)) 310 n = bytblk(f->fs, f->offset); 311 } else if (!off) 312 n++; 313 while (n--) { 314 if ((err = fatget(f->fs, &c))) 315 goto out; 316 if (!okclus(f->fs, c)) { 317 err = EINVAL; 318 goto out; 319 } 320 } 321 if (!clus || (n = f->fs->bsize - off) > cnt) 322 n = cnt; 323 if ((err = ioread(f->fs, (c ? blkoff(f->fs, c) : 324 secbyt(f->fs->lsndir)) + off, buf, n))) 325 goto out; 326 f->offset += n; 327 f->c = c; 328 off = 0; 329 buf = (char *)buf + n; 330 cnt -= n; 331 } 332 out: 333 if (resid) 334 *resid = nbyte - nb + cnt; 335 return (err); 336 } 337 338 /* 339 * Reposition within file 340 */ 341 static off_t 342 dos_seek(struct open_file *fd, off_t offset, int whence) 343 { 344 off_t off; 345 u_int size; 346 DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 347 348 size = cv4(f->de.size); 349 switch (whence) { 350 case SEEK_SET: 351 off = 0; 352 break; 353 case SEEK_CUR: 354 off = f->offset; 355 break; 356 case SEEK_END: 357 off = size; 358 break; 359 default: 360 errno = EINVAL; 361 return (-1); 362 } 363 off += offset; 364 if (off < 0 || off > size) { 365 errno = EINVAL; 366 return (-1); 367 } 368 f->offset = (u_int)off; 369 f->c = 0; 370 return (off); 371 } 372 373 /* 374 * Close open file 375 */ 376 static int 377 dos_close(struct open_file *fd) 378 { 379 DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 380 DOS_FS *fs = f->fs; 381 382 f->fs->links--; 383 free(f); 384 dos_unmount(fs); 385 return (0); 386 } 387 388 /* 389 * Return some stat information on a file. 390 */ 391 static int 392 dos_stat(struct open_file *fd, struct stat *sb) 393 { 394 DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 395 396 /* only important stuff */ 397 sb->st_mode = f->de.attr & FA_DIR ? S_IFDIR | 0555 : S_IFREG | 0444; 398 sb->st_nlink = 1; 399 sb->st_uid = 0; 400 sb->st_gid = 0; 401 if ((sb->st_size = fsize(f->fs, &f->de)) == -1) 402 return (EINVAL); 403 return (0); 404 } 405 406 static int 407 dos_checksum(unsigned char *name, unsigned char *ext) 408 { 409 int x, i; 410 char buf[11]; 411 412 bcopy(name, buf, 8); 413 bcopy(ext, buf+8, 3); 414 x = 0; 415 for (i = 0; i < 11; i++) { 416 x = ((x & 1) << 7) | (x >> 1); 417 x += buf[i]; 418 x &= 0xff; 419 } 420 return (x); 421 } 422 423 static int 424 dos_readdir(struct open_file *fd, struct dirent *d) 425 { 426 /* DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; */ 427 u_char fn[261]; 428 DOS_DIR dd; 429 size_t res; 430 u_int chk, x, xdn; 431 int err; 432 433 x = chk = 0; 434 for (;;) { 435 xdn = x; 436 x = 0; 437 err = dos_read(fd, &dd, sizeof(dd), &res); 438 if (err) 439 return (err); 440 if (res == sizeof(dd)) 441 return (ENOENT); 442 if (dd.de.name[0] == 0) 443 return (ENOENT); 444 445 /* Skip deleted entries */ 446 if (dd.de.name[0] == 0xe5) 447 continue; 448 449 /* Check if directory entry is volume label */ 450 if (dd.de.attr & FA_LABEL) { 451 /* 452 * If volume label set, check if the current entry is 453 * extended entry (FA_XDE) for long file names. 454 */ 455 if ((dd.de.attr & FA_MASK) == FA_XDE) { 456 /* 457 * Read through all following extended entries 458 * to get the long file name. 0x40 marks the 459 * last entry containing part of long file name. 460 */ 461 if (dd.xde.seq & 0x40) 462 chk = dd.xde.chk; 463 else if (dd.xde.seq != xdn - 1 || 464 dd.xde.chk != chk) 465 continue; 466 x = dd.xde.seq & ~0x40; 467 if (x < 1 || x > 20) { 468 x = 0; 469 continue; 470 } 471 cp_xdnm(fn, &dd.xde); 472 } else { 473 /* skip only volume label entries */ 474 continue; 475 } 476 } else { 477 if (xdn == 1) { 478 x = dos_checksum(dd.de.name, dd.de.ext); 479 if (x == chk) 480 break; 481 } else { 482 cp_sfn(fn, &dd.de); 483 break; 484 } 485 x = 0; 486 } 487 } 488 489 d->d_fileno = (dd.de.clus[1] << 8) + dd.de.clus[0]; 490 d->d_reclen = sizeof(*d); 491 d->d_type = (dd.de.attr & FA_DIR) ? DT_DIR : DT_REG; 492 memcpy(d->d_name, fn, sizeof(d->d_name)); 493 return (0); 494 } 495 496 /* 497 * Parse DOS boot sector 498 */ 499 static int 500 parsebs(DOS_FS *fs, DOS_BS *bs) 501 { 502 u_int sc; 503 504 if ((bs->jmp[0] != 0x69 && 505 bs->jmp[0] != 0xe9 && 506 (bs->jmp[0] != 0xeb || bs->jmp[2] != 0x90)) || 507 bs->bpb.media < 0xf0) 508 return (EINVAL); 509 if (cv2(bs->bpb.secsiz) != SECSIZ) 510 return (EINVAL); 511 if (!(fs->spc = bs->bpb.spc) || fs->spc & (fs->spc - 1)) 512 return (EINVAL); 513 fs->bsize = secbyt(fs->spc); 514 fs->bshift = ffs(fs->bsize) - 1; 515 if ((fs->spf = cv2(bs->bpb.spf))) { 516 if (bs->bpb.fats != 2) 517 return (EINVAL); 518 if (!(fs->dirents = cv2(bs->bpb.dirents))) 519 return (EINVAL); 520 } else { 521 if (!(fs->spf = cv4(bs->bpb.lspf))) 522 return (EINVAL); 523 if (!bs->bpb.fats || bs->bpb.fats > 16) 524 return (EINVAL); 525 if ((fs->rdcl = cv4(bs->bpb.rdcl)) < LOCLUS) 526 return (EINVAL); 527 } 528 if (!(fs->lsnfat = cv2(bs->bpb.ressec))) 529 return (EINVAL); 530 fs->lsndir = fs->lsnfat + fs->spf * bs->bpb.fats; 531 fs->lsndta = fs->lsndir + entsec(fs->dirents); 532 if (!(sc = cv2(bs->bpb.secs)) && !(sc = cv4(bs->bpb.lsecs))) 533 return (EINVAL); 534 if (fs->lsndta > sc) 535 return (EINVAL); 536 if ((fs->xclus = secblk(fs, sc - fs->lsndta) + 1) < LOCLUS) 537 return (EINVAL); 538 fs->fatsz = fs->dirents ? fs->xclus < 0xff6 ? 12 : 16 : 32; 539 sc = (secbyt(fs->spf) << 1) / (fs->fatsz >> 2) - 1; 540 if (fs->xclus > sc) 541 fs->xclus = sc; 542 return (0); 543 } 544 545 /* 546 * Return directory entry from path 547 */ 548 static int 549 namede(DOS_FS *fs, const char *path, DOS_DE **dep) 550 { 551 char name[256]; 552 DOS_DE *de; 553 char *s; 554 size_t n; 555 int err; 556 557 err = 0; 558 de = &fs->root; 559 while (*path) { 560 while (*path == '/') 561 path++; 562 if (*path == '\0') 563 break; 564 if (!(s = strchr(path, '/'))) 565 s = strchr(path, 0); 566 if ((n = s - path) > 255) 567 return (ENAMETOOLONG); 568 memcpy(name, path, n); 569 name[n] = 0; 570 path = s; 571 if (!(de->attr & FA_DIR)) 572 return (ENOTDIR); 573 if ((err = lookup(fs, stclus(fs->fatsz, de), name, &de))) 574 return (err); 575 } 576 *dep = de; 577 return (0); 578 } 579 580 /* 581 * Lookup path segment 582 */ 583 static int 584 lookup(DOS_FS *fs, u_int clus, const char *name, DOS_DE **dep) 585 { 586 static DOS_DIR dir[DEPSEC]; 587 u_char lfn[261]; 588 u_char sfn[13]; 589 u_int nsec, lsec, xdn, chk, sec, ent, x; 590 int err, ok; 591 592 if (!clus) 593 for (ent = 0; ent < 2; ent++) 594 if (!strcasecmp(name, dotstr[ent])) { 595 *dep = dot + ent; 596 return (0); 597 } 598 if (!clus && fs->fatsz == 32) 599 clus = fs->rdcl; 600 nsec = !clus ? entsec(fs->dirents) : fs->spc; 601 lsec = 0; 602 xdn = chk = 0; 603 for (;;) { 604 if (!clus && !lsec) 605 lsec = fs->lsndir; 606 else if (okclus(fs, clus)) 607 lsec = blklsn(fs, clus); 608 else 609 return (EINVAL); 610 for (sec = 0; sec < nsec; sec++) { 611 if ((err = ioget(fs->fd, lsec + sec, dir, secbyt(1)))) 612 return (err); 613 for (ent = 0; ent < DEPSEC; ent++) { 614 if (!*dir[ent].de.name) 615 return (ENOENT); 616 if (*dir[ent].de.name != 0xe5) { 617 if ((dir[ent].de.attr & FA_MASK) == 618 FA_XDE) { 619 x = dir[ent].xde.seq; 620 if (x & 0x40 || (x + 1 == xdn && 621 dir[ent].xde.chk == chk)) { 622 if (x & 0x40) { 623 chk = dir[ent].xde.chk; 624 x &= ~0x40; 625 } 626 if (x >= 1 && x <= 20) { 627 cp_xdnm(lfn, &dir[ent].xde); 628 xdn = x; 629 continue; 630 } 631 } 632 } else if (!(dir[ent].de.attr & 633 FA_LABEL)) { 634 if ((ok = xdn == 1)) { 635 x = dos_checksum( 636 dir[ent].de.name, 637 dir[ent].de.ext); 638 ok = chk == x && 639 !strcasecmp(name, 640 (const char *)lfn); 641 } 642 if (!ok) { 643 cp_sfn(sfn, 644 &dir[ent].de); 645 ok = !strcasecmp(name, 646 (const char *)sfn); 647 } 648 if (ok) { 649 *dep = &dir[ent].de; 650 return (0); 651 } 652 } 653 } 654 xdn = 0; 655 } 656 } 657 if (!clus) 658 break; 659 if ((err = fatget(fs, &clus))) 660 return (err); 661 if (fatend(fs->fatsz, clus)) 662 break; 663 } 664 return (ENOENT); 665 } 666 667 /* 668 * Copy name from extended directory entry 669 */ 670 static void 671 cp_xdnm(u_char *lfn, DOS_XDE *xde) 672 { 673 static struct { 674 u_int off; 675 u_int dim; 676 } ix[3] = { 677 {offsetof(DOS_XDE, name1), sizeof(xde->name1) / 2}, 678 {offsetof(DOS_XDE, name2), sizeof(xde->name2) / 2}, 679 {offsetof(DOS_XDE, name3), sizeof(xde->name3) / 2} 680 }; 681 u_char *p; 682 u_int n, x, c; 683 684 lfn += 13 * ((xde->seq & ~0x40) - 1); 685 for (n = 0; n < 3; n++) 686 for (p = (u_char *)xde + ix[n].off, x = ix[n].dim; x; 687 p += 2, x--) { 688 if ((c = cv2(p)) && (c < 32 || c > 127)) 689 c = '?'; 690 if (!(*lfn++ = c)) 691 return; 692 } 693 if (xde->seq & 0x40) 694 *lfn = 0; 695 } 696 697 /* 698 * Copy short filename 699 */ 700 static void 701 cp_sfn(u_char *sfn, DOS_DE *de) 702 { 703 u_char *p; 704 int j, i; 705 706 p = sfn; 707 if (*de->name != ' ') { 708 for (j = 7; de->name[j] == ' '; j--) 709 ; 710 for (i = 0; i <= j; i++) 711 *p++ = de->name[i]; 712 if (*de->ext != ' ') { 713 *p++ = '.'; 714 for (j = 2; de->ext[j] == ' '; j--) 715 ; 716 for (i = 0; i <= j; i++) 717 *p++ = de->ext[i]; 718 } 719 } 720 *p = 0; 721 if (*sfn == 5) 722 *sfn = 0xe5; 723 } 724 725 /* 726 * Return size of file in bytes 727 */ 728 static off_t 729 fsize(DOS_FS *fs, DOS_DE *de) 730 { 731 u_long size; 732 u_int c; 733 int n; 734 735 if (!(size = cv4(de->size)) && de->attr & FA_DIR) { 736 if (!(c = cv2(de->clus))) { 737 size = fs->dirents * sizeof(DOS_DE); 738 } else { 739 if ((n = fatcnt(fs, c)) == -1) 740 return (n); 741 size = blkbyt(fs, n); 742 } 743 } 744 return (size); 745 } 746 747 /* 748 * Count number of clusters in chain 749 */ 750 static int 751 fatcnt(DOS_FS *fs, u_int c) 752 { 753 int n; 754 755 for (n = 0; okclus(fs, c); n++) 756 if (fatget(fs, &c)) 757 return (-1); 758 return (fatend(fs->fatsz, c) ? n : -1); 759 } 760 761 /* 762 * Get next cluster in cluster chain. Use in core fat cache unless 763 * the number of current 128K block in FAT has changed. 764 */ 765 static int 766 fatget(DOS_FS *fs, u_int *c) 767 { 768 u_int val_in, val_out, offset, blknum, nbyte; 769 const u_char *p_entry; 770 int err; 771 772 /* check input value to prevent overflow in fatoff() */ 773 val_in = *c; 774 if (val_in & 0xf0000000) 775 return (EINVAL); 776 777 /* ensure that current 128K FAT block is cached */ 778 offset = fatoff(fs->fatsz, val_in); 779 nbyte = fs->fatsz != 32 ? 2 : 4; 780 if (offset + nbyte > secbyt(fs->spf)) 781 return (EINVAL); 782 blknum = offset / FATBLKSZ; 783 offset %= FATBLKSZ; 784 if (offset + nbyte > FATBLKSZ) 785 return (EINVAL); 786 if (blknum != fs->fatbuf_blknum) { 787 err = dos_read_fatblk(fs, fs->fd, blknum); 788 if (err != 0) 789 return (err); 790 } 791 p_entry = fs->fatbuf + offset; 792 793 /* extract cluster number from FAT entry */ 794 switch (fs->fatsz) { 795 case 32: 796 val_out = cv4(p_entry); 797 val_out &= 0x0fffffff; 798 break; 799 case 16: 800 val_out = cv2(p_entry); 801 break; 802 case 12: 803 val_out = cv2(p_entry); 804 if (val_in & 1) 805 val_out >>= 4; 806 else 807 val_out &= 0xfff; 808 break; 809 default: 810 return (EINVAL); 811 } 812 *c = val_out; 813 return (0); 814 } 815 816 /* 817 * Is cluster an end-of-chain marker? 818 */ 819 static int 820 fatend(u_int sz, u_int c) 821 { 822 return (c > (sz == 12 ? 0xff7U : sz == 16 ? 0xfff7U : 0xffffff7)); 823 } 824 825 /* 826 * Offset-based I/O primitive 827 */ 828 static int 829 ioread(DOS_FS *fs, u_int offset, void *buf, size_t nbyte) 830 { 831 char *s; 832 u_int off, n; 833 int err; 834 u_char local_buf[SECSIZ]; 835 836 s = buf; 837 if ((off = offset & (SECSIZ - 1))) { 838 offset -= off; 839 if ((n = SECSIZ - off) > nbyte) 840 n = nbyte; 841 err = ioget(fs->fd, bytsec(offset), local_buf, 842 sizeof(local_buf)); 843 if (err != 0) 844 return (err); 845 memcpy(s, local_buf + off, n); 846 offset += SECSIZ; 847 s += n; 848 nbyte -= n; 849 } 850 n = nbyte & (SECSIZ - 1); 851 if (nbyte -= n) { 852 if ((err = ioget(fs->fd, bytsec(offset), s, nbyte))) 853 return (err); 854 offset += nbyte; 855 s += nbyte; 856 } 857 if (n != 0) { 858 err = ioget(fs->fd, bytsec(offset), local_buf, 859 sizeof(local_buf)); 860 if (err != 0) 861 return (err); 862 memcpy(s, local_buf, n); 863 } 864 return (0); 865 } 866 867 /* 868 * Sector-based I/O primitive 869 */ 870 static int 871 ioget(struct open_file *fd, daddr_t lsec, void *buf, size_t size) 872 { 873 size_t rsize; 874 int rv; 875 876 /* Make sure we get full read or error. */ 877 rsize = 0; 878 rv = (fd->f_dev->dv_strategy)(fd->f_devdata, F_READ, lsec, 879 size, buf, &rsize); 880 if ((rv == 0) && (size != rsize)) 881 rv = EIO; 882 return (rv); 883 } 884