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