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