1 /* 2 * Copyright (c) 1996, 1998 Robert Nordier 3 * All rights reserved. 4 * Copyright 2024 MNX Cloud, Inc. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in 13 * the documentation and/or other materials provided with the 14 * distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS 17 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY 20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 22 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 24 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 25 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 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 ? (u_int)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 *, uint64_t, 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 uint64_t off; 369 size_t nb; 370 u_int clus, c, cnt, n; 371 DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 372 int err = 0; 373 374 /* 375 * as ioget() can be called *a lot*, use twiddle here. 376 * also 4 seems to be good value not to slow loading down too much: 377 * with 270MB file (~540k ioget() calls, twiddle can easily waste 378 * 4-5 sec. 379 */ 380 twiddle(4); 381 nb = nbyte; 382 if ((size = fsize(f->fs, &f->de)) == -1) 383 return (EINVAL); 384 if (nb > (n = size - f->offset)) 385 nb = n; 386 off = f->offset; 387 if ((clus = stclus(f->fs->fatsz, &f->de))) 388 off &= f->fs->bsize - 1; 389 c = f->c; 390 cnt = nb; 391 while (cnt) { 392 n = 0; 393 if (!c) { 394 if ((c = clus)) 395 n = bytblk(f->fs, f->offset); 396 } else if (!off) 397 n++; 398 while (n--) { 399 if ((err = fatget(f->fs, &c))) 400 goto out; 401 if (!okclus(f->fs, c)) { 402 err = EINVAL; 403 goto out; 404 } 405 } 406 if (!clus || (n = f->fs->bsize - off) > cnt) 407 n = cnt; 408 if (c != 0) 409 off += blkoff(f->fs, (uint64_t)c); 410 else 411 off += secbyt(f->fs->lsndir); 412 err = ioread(f->fs, off, buf, n); 413 if (err != 0) 414 goto out; 415 f->offset += n; 416 f->c = c; 417 off = 0; 418 buf = (char *)buf + n; 419 cnt -= n; 420 } 421 out: 422 if (resid) 423 *resid = nbyte - nb + cnt; 424 return (err); 425 } 426 427 /* 428 * Reposition within file 429 */ 430 static off_t 431 dos_seek(struct open_file *fd, off_t offset, int whence) 432 { 433 off_t off; 434 u_int size; 435 DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 436 437 size = cv4(f->de.size); 438 switch (whence) { 439 case SEEK_SET: 440 off = 0; 441 break; 442 case SEEK_CUR: 443 off = f->offset; 444 break; 445 case SEEK_END: 446 off = size; 447 break; 448 default: 449 errno = EINVAL; 450 return (-1); 451 } 452 off += offset; 453 if (off < 0 || off > size) { 454 errno = EINVAL; 455 return (-1); 456 } 457 f->offset = (u_int)off; 458 f->c = 0; 459 return (off); 460 } 461 462 /* 463 * Close open file 464 */ 465 static int 466 dos_close(struct open_file *fd) 467 { 468 DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 469 DOS_FS *fs = f->fs; 470 471 f->fs->links--; 472 free(f); 473 dos_unmount_impl(fs); 474 return (0); 475 } 476 477 /* 478 * Return some stat information on a file. 479 */ 480 static int 481 dos_stat(struct open_file *fd, struct stat *sb) 482 { 483 DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 484 485 /* only important stuff */ 486 sb->st_mode = f->de.attr & FA_DIR ? S_IFDIR | 0555 : S_IFREG | 0444; 487 sb->st_nlink = 1; 488 sb->st_uid = 0; 489 sb->st_gid = 0; 490 if ((sb->st_size = fsize(f->fs, &f->de)) == -1) 491 return (EINVAL); 492 return (0); 493 } 494 495 static int 496 dos_checksum(unsigned char *name, unsigned char *ext) 497 { 498 int x, i; 499 char buf[11]; 500 501 bcopy(name, buf, 8); 502 bcopy(ext, buf+8, 3); 503 x = 0; 504 for (i = 0; i < 11; i++) { 505 x = ((x & 1) << 7) | (x >> 1); 506 x += buf[i]; 507 x &= 0xff; 508 } 509 return (x); 510 } 511 512 static int 513 dos_readdir(struct open_file *fd, struct dirent *d) 514 { 515 /* DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; */ 516 u_char fn[261]; 517 DOS_DIR dd; 518 size_t res; 519 u_int chk, x, xdn; 520 int err; 521 522 x = chk = 0; 523 for (;;) { 524 xdn = x; 525 x = 0; 526 err = dos_read(fd, &dd, sizeof(dd), &res); 527 if (err) 528 return (err); 529 if (res == sizeof(dd)) 530 return (ENOENT); 531 if (dd.de.name[0] == 0) 532 return (ENOENT); 533 534 /* Skip deleted entries */ 535 if (dd.de.name[0] == 0xe5) 536 continue; 537 538 /* Check if directory entry is volume label */ 539 if (dd.de.attr & FA_LABEL) { 540 /* 541 * If volume label set, check if the current entry is 542 * extended entry (FA_XDE) for long file names. 543 */ 544 if ((dd.de.attr & FA_MASK) == FA_XDE) { 545 /* 546 * Read through all following extended entries 547 * to get the long file name. 0x40 marks the 548 * last entry containing part of long file name. 549 */ 550 if (dd.xde.seq & 0x40) 551 chk = dd.xde.chk; 552 else if (dd.xde.seq != xdn - 1 || 553 dd.xde.chk != chk) 554 continue; 555 x = dd.xde.seq & ~0x40; 556 if (x < 1 || x > 20) { 557 x = 0; 558 continue; 559 } 560 cp_xdnm(fn, &dd.xde); 561 } else { 562 /* skip only volume label entries */ 563 continue; 564 } 565 } else { 566 if (xdn == 1) { 567 x = dos_checksum(dd.de.name, dd.de.ext); 568 if (x == chk) 569 break; 570 } else { 571 cp_sfn(fn, &dd.de); 572 break; 573 } 574 x = 0; 575 } 576 } 577 578 d->d_fileno = (dd.de.clus[1] << 8) + dd.de.clus[0]; 579 d->d_reclen = sizeof(*d); 580 d->d_type = (dd.de.attr & FA_DIR) ? DT_DIR : DT_REG; 581 memcpy(d->d_name, fn, sizeof(d->d_name)); 582 return (0); 583 } 584 585 /* 586 * Parse DOS boot sector 587 */ 588 static int 589 parsebs(DOS_FS *fs, DOS_BS *bs) 590 { 591 u_int sc; 592 593 if ((bs->jmp[0] != 0x69 && 594 bs->jmp[0] != 0xe9 && 595 (bs->jmp[0] != 0xeb || bs->jmp[2] != 0x90)) || 596 bs->bpb.media < 0xf0) 597 return (EINVAL); 598 if (cv2(bs->bpb.secsiz) != SECSIZ) 599 return (EINVAL); 600 if (!(fs->spc = bs->bpb.spc) || fs->spc & (fs->spc - 1)) 601 return (EINVAL); 602 fs->bsize = secbyt(fs->spc); 603 fs->bshift = ffs(fs->bsize) - 1; 604 if ((fs->spf = cv2(bs->bpb.spf))) { 605 if (bs->bpb.fats != 2) 606 return (EINVAL); 607 if (!(fs->dirents = cv2(bs->bpb.dirents))) 608 return (EINVAL); 609 } else { 610 if (!(fs->spf = cv4(bs->bpb.lspf))) 611 return (EINVAL); 612 if (!bs->bpb.fats || bs->bpb.fats > 16) 613 return (EINVAL); 614 if ((fs->rdcl = cv4(bs->bpb.rdcl)) < LOCLUS) 615 return (EINVAL); 616 } 617 if (!(fs->lsnfat = cv2(bs->bpb.ressec))) 618 return (EINVAL); 619 fs->lsndir = fs->lsnfat + fs->spf * bs->bpb.fats; 620 fs->lsndta = fs->lsndir + entsec(fs->dirents); 621 if (!(sc = cv2(bs->bpb.secs)) && !(sc = cv4(bs->bpb.lsecs))) 622 return (EINVAL); 623 if (fs->lsndta > sc) 624 return (EINVAL); 625 if ((fs->xclus = secblk(fs, sc - fs->lsndta) + 1) < LOCLUS) 626 return (EINVAL); 627 fs->fatsz = fs->dirents ? fs->xclus < 0xff6 ? 12 : 16 : 32; 628 sc = (secbyt(fs->spf) << 1) / (fs->fatsz >> 2) - 1; 629 if (fs->xclus > sc) 630 fs->xclus = sc; 631 return (0); 632 } 633 634 /* 635 * Return directory entry from path 636 */ 637 static int 638 namede(DOS_FS *fs, const char *path, DOS_DE **dep) 639 { 640 char name[256]; 641 DOS_DE *de; 642 char *s; 643 size_t n; 644 int err; 645 646 err = 0; 647 de = &fs->root; 648 while (*path) { 649 while (*path == '/') 650 path++; 651 if (*path == '\0') 652 break; 653 if (!(s = strchr(path, '/'))) 654 s = strchr(path, 0); 655 if ((n = s - path) > 255) 656 return (ENAMETOOLONG); 657 memcpy(name, path, n); 658 name[n] = 0; 659 path = s; 660 if (!(de->attr & FA_DIR)) 661 return (ENOTDIR); 662 if ((err = lookup(fs, stclus(fs->fatsz, de), name, &de))) 663 return (err); 664 } 665 *dep = de; 666 return (0); 667 } 668 669 /* 670 * Lookup path segment 671 */ 672 static int 673 lookup(DOS_FS *fs, u_int clus, const char *name, DOS_DE **dep) 674 { 675 static DOS_DIR dir[DEPSEC]; 676 u_char lfn[261]; 677 u_char sfn[13]; 678 u_int nsec, lsec, xdn, chk, sec, ent, x; 679 int err, ok; 680 681 if (!clus) 682 for (ent = 0; ent < 2; ent++) 683 if (!strcasecmp(name, dotstr[ent])) { 684 *dep = dot + ent; 685 return (0); 686 } 687 if (!clus && fs->fatsz == 32) 688 clus = fs->rdcl; 689 nsec = !clus ? entsec(fs->dirents) : fs->spc; 690 lsec = 0; 691 xdn = chk = 0; 692 for (;;) { 693 if (!clus && !lsec) 694 lsec = fs->lsndir; 695 else if (okclus(fs, clus)) 696 lsec = blklsn(fs, clus); 697 else 698 return (EINVAL); 699 for (sec = 0; sec < nsec; sec++) { 700 if ((err = ioget(fs->fd, lsec + sec, dir, secbyt(1)))) 701 return (err); 702 for (ent = 0; ent < DEPSEC; ent++) { 703 if (!*dir[ent].de.name) 704 return (ENOENT); 705 if (*dir[ent].de.name != 0xe5) { 706 if ((dir[ent].de.attr & FA_MASK) == 707 FA_XDE) { 708 x = dir[ent].xde.seq; 709 if (x & 0x40 || (x + 1 == xdn && 710 dir[ent].xde.chk == chk)) { 711 if (x & 0x40) { 712 chk = dir[ent].xde.chk; 713 x &= ~0x40; 714 } 715 if (x >= 1 && x <= 20) { 716 cp_xdnm(lfn, &dir[ent].xde); 717 xdn = x; 718 continue; 719 } 720 } 721 } else if (!(dir[ent].de.attr & 722 FA_LABEL)) { 723 if ((ok = xdn == 1)) { 724 x = dos_checksum( 725 dir[ent].de.name, 726 dir[ent].de.ext); 727 ok = chk == x && 728 !strcasecmp(name, 729 (const char *)lfn); 730 } 731 if (!ok) { 732 cp_sfn(sfn, 733 &dir[ent].de); 734 ok = !strcasecmp(name, 735 (const char *)sfn); 736 } 737 if (ok) { 738 *dep = &dir[ent].de; 739 return (0); 740 } 741 } 742 } 743 xdn = 0; 744 } 745 } 746 if (!clus) 747 break; 748 if ((err = fatget(fs, &clus))) 749 return (err); 750 if (fatend(fs->fatsz, clus)) 751 break; 752 } 753 return (ENOENT); 754 } 755 756 /* 757 * Copy name from extended directory entry 758 */ 759 static void 760 cp_xdnm(u_char *lfn, DOS_XDE *xde) 761 { 762 static struct { 763 u_int off; 764 u_int dim; 765 } ix[3] = { 766 {offsetof(DOS_XDE, name1), sizeof(xde->name1) / 2}, 767 {offsetof(DOS_XDE, name2), sizeof(xde->name2) / 2}, 768 {offsetof(DOS_XDE, name3), sizeof(xde->name3) / 2} 769 }; 770 u_char *p; 771 u_int n, x, c; 772 773 lfn += 13 * ((xde->seq & ~0x40) - 1); 774 for (n = 0; n < 3; n++) 775 for (p = (u_char *)xde + ix[n].off, x = ix[n].dim; x; 776 p += 2, x--) { 777 if ((c = cv2(p)) && (c < 32 || c > 127)) 778 c = '?'; 779 if (!(*lfn++ = c)) 780 return; 781 } 782 if (xde->seq & 0x40) 783 *lfn = 0; 784 } 785 786 /* 787 * Copy short filename 788 */ 789 static void 790 cp_sfn(u_char *sfn, DOS_DE *de) 791 { 792 u_char *p; 793 int j, i; 794 795 p = sfn; 796 if (*de->name != ' ') { 797 for (j = 7; de->name[j] == ' '; j--) 798 ; 799 for (i = 0; i <= j; i++) 800 *p++ = de->name[i]; 801 if (*de->ext != ' ') { 802 *p++ = '.'; 803 for (j = 2; de->ext[j] == ' '; j--) 804 ; 805 for (i = 0; i <= j; i++) 806 *p++ = de->ext[i]; 807 } 808 } 809 *p = 0; 810 if (*sfn == 5) 811 *sfn = 0xe5; 812 } 813 814 /* 815 * Return size of file in bytes 816 */ 817 static off_t 818 fsize(DOS_FS *fs, DOS_DE *de) 819 { 820 u_long size; 821 u_int c; 822 int n; 823 824 if (!(size = cv4(de->size)) && de->attr & FA_DIR) { 825 if (!(c = stclus(fs->fatsz, de))) { 826 size = fs->dirents * sizeof(DOS_DE); 827 } else { 828 if ((n = fatcnt(fs, c)) == -1) 829 return (n); 830 size = blkbyt(fs, n); 831 } 832 } 833 return (size); 834 } 835 836 /* 837 * Count number of clusters in chain 838 */ 839 static int 840 fatcnt(DOS_FS *fs, u_int c) 841 { 842 int n; 843 844 for (n = 0; okclus(fs, c); n++) 845 if (fatget(fs, &c)) 846 return (-1); 847 return (fatend(fs->fatsz, c) ? n : -1); 848 } 849 850 /* 851 * Get next cluster in cluster chain. Use in core fat cache unless 852 * the number of current 128K block in FAT has changed. 853 */ 854 static int 855 fatget(DOS_FS *fs, u_int *c) 856 { 857 u_int val_in, val_out, offset, blknum, nbyte; 858 const u_char *p_entry; 859 int err; 860 861 /* check input value to prevent overflow in fatoff() */ 862 val_in = *c; 863 if (val_in & 0xf0000000) 864 return (EINVAL); 865 866 /* ensure that current 128K FAT block is cached */ 867 offset = fatoff(fs->fatsz, val_in); 868 nbyte = fs->fatsz != 32 ? 2 : 4; 869 if (offset + nbyte > secbyt(fs->spf)) 870 return (EINVAL); 871 blknum = offset / FATBLKSZ; 872 offset %= FATBLKSZ; 873 if (offset + nbyte > FATBLKSZ) 874 return (EINVAL); 875 if (blknum != fs->fatbuf_blknum) { 876 err = dos_read_fatblk(fs, fs->fd, blknum); 877 if (err != 0) 878 return (err); 879 } 880 p_entry = fs->fatbuf + offset; 881 882 /* extract cluster number from FAT entry */ 883 switch (fs->fatsz) { 884 case 32: 885 val_out = cv4(p_entry); 886 val_out &= 0x0fffffff; 887 break; 888 case 16: 889 val_out = cv2(p_entry); 890 break; 891 case 12: 892 val_out = cv2(p_entry); 893 if (val_in & 1) 894 val_out >>= 4; 895 else 896 val_out &= 0xfff; 897 break; 898 default: 899 return (EINVAL); 900 } 901 *c = val_out; 902 return (0); 903 } 904 905 /* 906 * Is cluster an end-of-chain marker? 907 */ 908 static int 909 fatend(u_int sz, u_int c) 910 { 911 return (c > (sz == 12 ? 0xff7U : sz == 16 ? 0xfff7U : 0xffffff7)); 912 } 913 914 /* 915 * Offset-based I/O primitive 916 */ 917 static int 918 ioread(DOS_FS *fs, uint64_t offset, void *buf, size_t nbyte) 919 { 920 char *s; 921 size_t n; 922 int err; 923 uint64_t off; 924 u_char local_buf[SECSIZ]; 925 926 s = buf; 927 if ((off = offset & (SECSIZ - 1))) { 928 offset -= off; 929 if ((n = SECSIZ - off) > nbyte) 930 n = nbyte; 931 err = ioget(fs->fd, bytsec(offset), local_buf, 932 sizeof(local_buf)); 933 if (err != 0) 934 return (err); 935 memcpy(s, local_buf + off, n); 936 offset += SECSIZ; 937 s += n; 938 nbyte -= n; 939 } 940 n = nbyte & (SECSIZ - 1); 941 if (nbyte -= n) { 942 if ((err = ioget(fs->fd, bytsec(offset), s, nbyte))) 943 return (err); 944 offset += nbyte; 945 s += nbyte; 946 } 947 if (n != 0) { 948 err = ioget(fs->fd, bytsec(offset), local_buf, 949 sizeof(local_buf)); 950 if (err != 0) 951 return (err); 952 memcpy(s, local_buf, n); 953 } 954 return (0); 955 } 956 957 /* 958 * Sector-based I/O primitive 959 */ 960 static int 961 ioget(struct open_file *fd, daddr_t lsec, void *buf, size_t size) 962 { 963 size_t rsize; 964 int rv; 965 966 /* Make sure we get full read or error. */ 967 rsize = 0; 968 rv = (fd->f_dev->dv_strategy)(fd->f_devdata, F_READ, lsec, 969 size, buf, &rsize); 970 if ((rv == 0) && (size != rsize)) 971 rv = EIO; 972 return (rv); 973 } 974