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