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