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