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