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