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