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