1 /* $NetBSD: dosfs.c,v 1.22 2019/03/31 20:08:45 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1996, 1998 Robert Nordier 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in 14 * the documentation and/or other materials provided with the 15 * distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS 18 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 23 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 26 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 /* 31 * Readonly filesystem for Microsoft FAT12/FAT16/FAT32 filesystems, 32 * also supports VFAT. 33 */ 34 35 /* 36 * XXX DOES NOT SUPPORT: 37 * 38 * LIBSA_FS_SINGLECOMPONENT 39 */ 40 41 #include <sys/param.h> 42 43 #include <fs/msdosfs/bpb.h> 44 #include <fs/msdosfs/direntry.h> 45 46 #ifdef _STANDALONE 47 #include <lib/libkern/libkern.h> 48 #else 49 #include <string.h> 50 #include <stddef.h> 51 #endif 52 53 #include "stand.h" 54 #include "dosfs.h" 55 56 #define SECSIZ 512 /* sector size */ 57 #define SSHIFT 9 /* SECSIZ shift */ 58 #define DEPSEC 16 /* directory entries per sector */ 59 #define DSHIFT 4 /* DEPSEC shift */ 60 #define LOCLUS 2 /* lowest cluster number */ 61 62 typedef union { 63 struct direntry de; /* standard directory entry */ 64 struct winentry xde; /* extended directory entry */ 65 } DOS_DIR; 66 67 typedef struct { 68 struct open_file *fd; /* file descriptor */ 69 u_char *buf; /* buffer */ 70 u_int bufsec; /* buffered sector */ 71 u_int links; /* active links to structure */ 72 u_int spc; /* sectors per cluster */ 73 u_int bsize; /* cluster size in bytes */ 74 u_int bshift; /* cluster conversion shift */ 75 u_int dirents; /* root directory entries */ 76 u_int spf; /* sectors per fat */ 77 u_int rdcl; /* root directory start cluster */ 78 u_int lsnfat; /* start of fat */ 79 u_int lsndir; /* start of root dir */ 80 u_int lsndta; /* start of data area */ 81 u_int fatsz; /* FAT entry size */ 82 u_int xclus; /* maximum cluster number */ 83 } DOS_FS; 84 85 typedef struct { 86 DOS_FS *fs; /* associated filesystem */ 87 struct direntry de; /* directory entry */ 88 u_int offset; /* current offset */ 89 u_int c; /* last cluster read */ 90 } DOS_FILE; 91 92 /* Initial portion of DOS boot sector */ 93 typedef struct { 94 u_char jmp[3]; /* usually 80x86 'jmp' opcode */ 95 u_char oem[8]; /* OEM name and version */ 96 struct byte_bpb710 bpb; /* BPB */ 97 } DOS_BS; 98 99 /* Supply missing "." and ".." root directory entries */ 100 static const char *const dotstr[2] = {".", ".."}; 101 static const struct direntry dot[2] = { 102 {". ", " ", ATTR_DIRECTORY, 103 0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}, 104 {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}}, 105 106 {".. ", " ", ATTR_DIRECTORY, 107 0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}, 108 {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}} 109 }; 110 111 /* The usual conversion macros to avoid multiplication and division */ 112 #define bytsec(n) ((n) >> SSHIFT) 113 #define secbyt(s) ((s) << SSHIFT) 114 #define entsec(e) ((e) >> DSHIFT) 115 #define bytblk(fs, n) ((n) >> (fs)->bshift) 116 #define blkbyt(fs, b) ((b) << (fs)->bshift) 117 #define secblk(fs, s) ((s) >> ((fs)->bshift - SSHIFT)) 118 #define blksec(fs, b) ((b) << ((fs)->bshift - SSHIFT)) 119 120 /* Convert cluster number to offset within filesystem */ 121 #define blkoff(fs, b) (secbyt((fs)->lsndta) + blkbyt(fs, (b) - LOCLUS)) 122 123 /* Convert cluster number to logical sector number */ 124 #define blklsn(fs, b) ((fs)->lsndta + blksec(fs, (b) - LOCLUS)) 125 126 /* Convert cluster number to offset within FAT */ 127 #define fatoff(sz, c) ((sz) == 12 ? (c) + ((c) >> 1) : \ 128 (sz) == 16 ? (c) << 1 : \ 129 (c) << 2) 130 131 /* Does cluster number reference a valid data cluster? */ 132 #define okclus(fs, c) ((c) >= LOCLUS && (c) <= (fs)->xclus) 133 134 /* Get start cluster from directory entry */ 135 #define stclus(sz, de) ((sz) != 32 ? (u_int)getushort((de)->deStartCluster) : \ 136 ((u_int)getushort((de)->deHighClust) << 16) | \ 137 (u_int)getushort((de)->deStartCluster)) 138 139 static int dosunmount(DOS_FS *); 140 static int parsebs(DOS_FS *, DOS_BS *); 141 static int namede(DOS_FS *, const char *, const struct direntry **); 142 static int lookup(DOS_FS *, u_int, const char *, const struct direntry **); 143 static void cp_xdnm(u_char *, struct winentry *); 144 static void cp_sfn(u_char *, struct direntry *); 145 static off_t fsize(DOS_FS *, struct direntry *); 146 static int fatcnt(DOS_FS *, u_int); 147 static int fatget(DOS_FS *, u_int *); 148 static int fatend(u_int, u_int); 149 static int ioread(DOS_FS *, u_int, void *, u_int); 150 static int iobuf(DOS_FS *, u_int); 151 static int ioget(struct open_file *, u_int, void *, u_int); 152 153 #define strcasecmp(s1, s2) dos_strcasecmp(s1, s2) 154 static int 155 strcasecmp(const char *s1, const char *s2) 156 { 157 char c1, c2; 158 #define TO_UPPER(c) ((c) >= 'a' && (c) <= 'z' ? (c) - ('a' - 'A') : (c)) 159 for (;;) { 160 c1 = *s1++; 161 c2 = *s2++; 162 if (TO_UPPER(c1) != TO_UPPER(c2)) 163 return 1; 164 if (c1 == 0) 165 return 0; 166 } 167 #undef TO_UPPER 168 } 169 170 /* 171 * Mount DOS filesystem 172 */ 173 static int 174 dos_mount(DOS_FS *fs, struct open_file *fd) 175 { 176 int err; 177 178 (void)memset(fs, 0, sizeof(DOS_FS)); 179 fs->fd = fd; 180 if ((err = !(fs->buf = alloc(SECSIZ)) ? errno : 0) || 181 (err = ioget(fs->fd, 0, fs->buf, 1)) || 182 (err = parsebs(fs, (DOS_BS *)fs->buf))) { 183 (void) dosunmount(fs); 184 return err; 185 } 186 return 0; 187 } 188 189 #ifndef LIBSA_NO_FS_CLOSE 190 /* 191 * Unmount mounted filesystem 192 */ 193 static int 194 dos_unmount(DOS_FS *fs) 195 { 196 int err; 197 198 if (fs->links) 199 return EBUSY; 200 if ((err = dosunmount(fs))) 201 return err; 202 return 0; 203 } 204 #endif 205 206 /* 207 * Common code shared by dos_mount() and dos_unmount() 208 */ 209 static int 210 dosunmount(DOS_FS *fs) 211 { 212 if (fs->buf) 213 dealloc(fs->buf, SECSIZ); 214 dealloc(fs, sizeof(DOS_FS)); 215 return 0; 216 } 217 218 /* 219 * Open DOS file 220 */ 221 __compactcall int 222 dosfs_open(const char *path, struct open_file *fd) 223 { 224 const struct direntry *de; 225 DOS_FILE *f; 226 DOS_FS *fs; 227 u_int size, clus; 228 int err = 0; 229 230 /* Allocate mount structure, associate with open */ 231 fs = alloc(sizeof(DOS_FS)); 232 233 if ((err = dos_mount(fs, fd))) 234 goto out; 235 236 if ((err = namede(fs, path, &de))) 237 goto out; 238 239 clus = stclus(fs->fatsz, de); 240 size = getulong(de->deFileSize); 241 242 if ((!(de->deAttributes & ATTR_DIRECTORY) && (!clus != !size)) || 243 ((de->deAttributes & ATTR_DIRECTORY) && size) || 244 (clus && !okclus(fs, clus))) { 245 err = EINVAL; 246 goto out; 247 } 248 249 f = alloc(sizeof(DOS_FILE)); 250 #ifdef BOOTXX 251 /* due to __internal_memset_ causing all sorts of register spillage 252 (and being completely unoptimized for zeroing small amounts of 253 memory), if we hand-initialize the remaining members of f to zero, 254 the code size drops 68 bytes. This makes no sense, admittedly. */ 255 f->offset = 0; 256 f->c = 0; 257 #else 258 (void)memset(f, 0, sizeof(DOS_FILE)); 259 #endif 260 f->fs = fs; 261 fs->links++; 262 f->de = *de; 263 fd->f_fsdata = (void *)f; 264 fsmod = "msdos"; 265 266 out: 267 return err; 268 } 269 270 /* 271 * Read from file 272 */ 273 __compactcall int 274 dosfs_read(struct open_file *fd, void *vbuf, size_t nbyte, size_t *resid) 275 { 276 off_t size; 277 u_int8_t *buf = vbuf; 278 u_int nb, off, clus, c, cnt, n; 279 DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 280 int err = 0; 281 282 nb = (u_int) nbyte; 283 if ((size = fsize(f->fs, &f->de)) == -1) 284 return EINVAL; 285 n = (u_int)(size - f->offset); 286 if (nb > n) 287 nb = n; 288 off = f->offset; 289 if ((clus = stclus(f->fs->fatsz, &f->de))) 290 off &= f->fs->bsize - 1; 291 c = f->c; 292 cnt = nb; 293 while (cnt) { 294 n = 0; 295 if (!c) { 296 if ((c = clus)) 297 n = bytblk(f->fs, f->offset); 298 } else if (!off) { 299 n++; 300 } 301 while (n--) { 302 if ((err = fatget(f->fs, &c))) 303 goto out; 304 if (!okclus(f->fs, c)) { 305 err = EINVAL; 306 goto out; 307 } 308 } 309 if (!clus || (n = f->fs->bsize - off) > cnt) 310 n = cnt; 311 if ((err = ioread(f->fs, (c ? blkoff(f->fs, c) : 312 secbyt(f->fs->lsndir)) + off, 313 buf, n))) 314 goto out; 315 f->offset += n; 316 f->c = c; 317 off = 0; 318 buf += n; 319 cnt -= n; 320 } 321 out: 322 if (resid) 323 *resid = nbyte - nb + cnt; 324 return err; 325 } 326 327 #ifndef LIBSA_NO_FS_WRITE 328 /* 329 * Not implemented. 330 */ 331 __compactcall int 332 dosfs_write(struct open_file *fd, void *start, size_t size, size_t *resid) 333 { 334 335 return EROFS; 336 } 337 #endif /* !LIBSA_NO_FS_WRITE */ 338 339 #ifndef LIBSA_NO_FS_SEEK 340 /* 341 * Reposition within file 342 */ 343 __compactcall off_t 344 dosfs_seek(struct open_file *fd, off_t offset, int whence) 345 { 346 off_t off; 347 u_int size; 348 DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 349 350 size = getulong(f->de.deFileSize); 351 switch (whence) { 352 case SEEK_SET: 353 off = 0; 354 break; 355 case SEEK_CUR: 356 off = f->offset; 357 break; 358 case SEEK_END: 359 off = size; 360 break; 361 default: 362 return -1; 363 } 364 off += offset; 365 if (off < 0 || off > size) 366 return -1; 367 f->offset = (u_int) off; 368 f->c = 0; 369 return off; 370 } 371 #endif /* !LIBSA_NO_FS_SEEK */ 372 373 #ifndef LIBSA_NO_FS_CLOSE 374 /* 375 * Close open file 376 */ 377 __compactcall int 378 dosfs_close(struct open_file *fd) 379 { 380 DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 381 DOS_FS *fs = f->fs; 382 383 f->fs->links--; 384 dealloc(f, sizeof(DOS_FILE)); 385 dos_unmount(fs); 386 return 0; 387 } 388 #endif /* !LIBSA_NO_FS_CLOSE */ 389 390 /* 391 * Return some stat information on a file. 392 */ 393 __compactcall int 394 dosfs_stat(struct open_file *fd, struct stat *sb) 395 { 396 DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 397 398 /* only important stuff */ 399 sb->st_mode = (f->de.deAttributes & ATTR_DIRECTORY) ? 400 (S_IFDIR | 0555) : (S_IFREG | 0444); 401 sb->st_nlink = 1; 402 sb->st_uid = 0; 403 sb->st_gid = 0; 404 if ((sb->st_size = fsize(f->fs, &f->de)) == -1) 405 return EINVAL; 406 return 0; 407 } 408 409 #if defined(LIBSA_ENABLE_LS_OP) 410 #include "ls.h" 411 __compactcall void 412 dosfs_ls(struct open_file *f, const char *pattern) 413 { 414 lsunsup("dosfs"); 415 } 416 #endif 417 418 /* 419 * Parse DOS boot sector 420 */ 421 static int 422 parsebs(DOS_FS *fs, DOS_BS *bs) 423 { 424 u_int sc; 425 426 if ((bs->jmp[0] != 0x69 && 427 bs->jmp[0] != 0xe9 && 428 (bs->jmp[0] != 0xeb || bs->jmp[2] != 0x90)) || 429 bs->bpb.bpbMedia < 0xf0) 430 return EINVAL; 431 if (getushort(bs->bpb.bpbBytesPerSec) != SECSIZ) 432 return EINVAL; 433 if (!(fs->spc = bs->bpb.bpbSecPerClust) || fs->spc & (fs->spc - 1)) 434 return EINVAL; 435 fs->bsize = secbyt(fs->spc); 436 fs->bshift = (u_int)ffs((int)fs->bsize) - 1; 437 if ((fs->spf = getushort(bs->bpb.bpbFATsecs))) { 438 if (bs->bpb.bpbFATs != 2) 439 return EINVAL; 440 if (!(fs->dirents = getushort(bs->bpb.bpbRootDirEnts))) 441 return EINVAL; 442 } else { 443 if (!(fs->spf = getulong(bs->bpb.bpbBigFATsecs))) 444 return EINVAL; 445 if (!bs->bpb.bpbFATs || bs->bpb.bpbFATs > 16) 446 return EINVAL; 447 if ((fs->rdcl = getulong(bs->bpb.bpbRootClust)) < LOCLUS) 448 return EINVAL; 449 } 450 if (!(fs->lsnfat = getushort(bs->bpb.bpbResSectors))) 451 return EINVAL; 452 fs->lsndir = fs->lsnfat + fs->spf * bs->bpb.bpbFATs; 453 fs->lsndta = fs->lsndir + entsec(fs->dirents); 454 if (!(sc = getushort(bs->bpb.bpbSectors)) && 455 !(sc = getulong(bs->bpb.bpbHugeSectors))) 456 return EINVAL; 457 if (fs->lsndta > sc) 458 return EINVAL; 459 if ((fs->xclus = secblk(fs, sc - fs->lsndta) + 1) < LOCLUS) 460 return EINVAL; 461 fs->fatsz = fs->dirents ? fs->xclus < 0xff6 ? 12 : 16 : 32; 462 sc = (secbyt(fs->spf) << 1) / (fs->fatsz >> 2) - 1; 463 if (fs->xclus > sc) 464 fs->xclus = sc; 465 return 0; 466 } 467 468 /* 469 * Return directory entry from path 470 */ 471 static int 472 namede(DOS_FS *fs, const char *path, const struct direntry **dep) 473 { 474 char name[256]; 475 const struct direntry *de; 476 char *s; 477 size_t n; 478 int err; 479 480 err = 0; 481 de = dot; 482 if (*path == '/') 483 path++; 484 while (*path) { 485 if (!(s = strchr(path, '/'))) 486 s = strchr(path, 0); 487 n = (size_t)(s - path); 488 if (n > 255) 489 return ENAMETOOLONG; 490 memcpy(name, path, n); 491 name[n] = 0; 492 path = s; 493 if (!(de->deAttributes & ATTR_DIRECTORY)) 494 return ENOTDIR; 495 if ((err = lookup(fs, stclus(fs->fatsz, de), name, &de))) 496 return err; 497 if (*path == '/') 498 path++; 499 } 500 *dep = de; 501 return 0; 502 } 503 504 /* 505 * Lookup path segment 506 */ 507 static int 508 lookup(DOS_FS *fs, u_int clus, const char *name, const struct direntry **dep) 509 { 510 static DOS_DIR *dir = NULL; 511 u_char lfn[261]; 512 u_char sfn[13]; 513 u_int nsec, lsec, xdn, chk, sec, ent, x; 514 int err = 0, ok, i; 515 516 if (!clus) 517 for (ent = 0; ent < 2; ent++) 518 if (!strcasecmp(name, dotstr[ent])) { 519 *dep = dot + ent; 520 return 0; 521 } 522 523 if (dir == NULL) { 524 dir = alloc(sizeof(DOS_DIR) * DEPSEC); 525 if (dir == NULL) 526 return ENOMEM; 527 } 528 529 if (!clus && fs->fatsz == 32) 530 clus = fs->rdcl; 531 nsec = !clus ? entsec(fs->dirents) : fs->spc; 532 lsec = 0; 533 xdn = chk = 0; 534 for (;;) { 535 if (!clus && !lsec) 536 lsec = fs->lsndir; 537 else if (okclus(fs, clus)) 538 lsec = blklsn(fs, clus); 539 else { 540 err = EINVAL; 541 goto out; 542 } 543 for (sec = 0; sec < nsec; sec++) { 544 if ((err = ioget(fs->fd, lsec + sec, dir, 1))) 545 goto out; 546 for (ent = 0; ent < DEPSEC; ent++) { 547 if (!*dir[ent].de.deName) { 548 err = ENOENT; 549 goto out; 550 } 551 if (*dir[ent].de.deName != 0xe5) { 552 if (dir[ent].de.deAttributes == 553 ATTR_WIN95) { 554 x = dir[ent].xde.weCnt; 555 if (x & WIN_LAST || 556 (x + 1 == xdn && 557 dir[ent].xde.weChksum == 558 chk)) { 559 if (x & WIN_LAST) { 560 chk = dir[ent].xde.weChksum; 561 x &= WIN_CNT; 562 } 563 if (x >= 1 && x <= 20) { 564 cp_xdnm(lfn, &dir[ent].xde); 565 xdn = x; 566 continue; 567 } 568 } 569 } else if (!(dir[ent].de.deAttributes & 570 ATTR_VOLUME)) { 571 ok = xdn == 1; 572 if (ok) { 573 for (x = 0, i = 0; 574 i < 11; i++) 575 x = ((((x & 1) << 7) | (x >> 1)) + 576 (size_t)msdos_dirchar(&dir[ent].de,(size_t)i)) & 0xff; 577 ok = chk == x && 578 !strcasecmp(name, (const char *)lfn); 579 } 580 if (!ok) { 581 cp_sfn(sfn, &dir[ent].de); 582 ok = !strcasecmp(name, (const char *)sfn); 583 } 584 if (ok) { 585 *dep = &dir[ent].de; 586 goto out2; 587 } 588 } 589 } 590 xdn = 0; 591 } 592 } 593 if (!clus) 594 break; 595 if ((err = fatget(fs, &clus))) 596 goto out; 597 if (fatend(fs->fatsz, clus)) 598 break; 599 } 600 err = ENOENT; 601 out: 602 dealloc(dir, sizeof(DOS_DIR) * DEPSEC); 603 dir = NULL; 604 out2: 605 return err; 606 } 607 608 /* 609 * Copy name from extended directory entry 610 */ 611 static void 612 cp_xdnm(u_char *lfn, struct winentry *xde) 613 { 614 static const struct { 615 u_int off; 616 u_int dim; 617 } ix[3] = { 618 { offsetof(struct winentry, wePart1), 619 sizeof(xde->wePart1) / 2 }, 620 { offsetof(struct winentry, wePart2), 621 sizeof(xde->wePart2) / 2 }, 622 { offsetof(struct winentry, wePart3), 623 sizeof(xde->wePart3) / 2 } 624 }; 625 u_char *p; 626 u_int n, x, c; 627 628 lfn += 13 * ((xde->weCnt & WIN_CNT) - 1); 629 for (n = 0; n < 3; n++) 630 for (p = (u_char *)xde + ix[n].off, x = ix[n].dim; x; 631 p += 2, x--) { 632 if ((c = getushort(p)) && (c < 32 || c > 127)) 633 c = '?'; 634 if (!(*lfn++ = (u_char)c)) 635 return; 636 } 637 if (xde->weCnt & WIN_LAST) 638 *lfn = 0; 639 } 640 641 /* 642 * Copy short filename 643 */ 644 static void 645 cp_sfn(u_char *sfn, struct direntry *de) 646 { 647 u_char *p; 648 int j, i; 649 650 p = sfn; 651 if (*de->deName != ' ') { 652 for (j = 7; de->deName[j] == ' '; j--); 653 for (i = 0; i <= j; i++) 654 *p++ = de->deName[i]; 655 if (*de->deExtension != ' ') { 656 *p++ = '.'; 657 for (j = 2; de->deExtension[j] == ' '; j--); 658 for (i = 0; i <= j; i++) 659 *p++ = de->deExtension[i]; 660 } 661 } 662 *p = 0; 663 if (*sfn == 5) 664 *sfn = 0xe5; 665 } 666 667 /* 668 * Return size of file in bytes 669 */ 670 static off_t 671 fsize(DOS_FS *fs, struct direntry *de) 672 { 673 size_t size; 674 u_int c; 675 int n; 676 677 if (!(size = getulong(de->deFileSize)) && 678 de->deAttributes & ATTR_DIRECTORY) { 679 if (!(c = getushort(de->deStartCluster))) { 680 size = fs->dirents * sizeof(struct direntry); 681 } else { 682 if ((n = fatcnt(fs, c)) == -1) 683 return n; 684 size = (size_t)blkbyt(fs, n); 685 } 686 } 687 return (off_t)size; 688 } 689 690 /* 691 * Count number of clusters in chain 692 */ 693 static int 694 fatcnt(DOS_FS *fs, u_int c) 695 { 696 int n; 697 698 for (n = 0; okclus(fs, c); n++) 699 if (fatget(fs, &c)) 700 return -1; 701 return fatend(fs->fatsz, c) ? n : -1; 702 } 703 704 /* 705 * Get next cluster in cluster chain 706 */ 707 static int 708 fatget(DOS_FS *fs, u_int *c) 709 { 710 u_char buf[4]; 711 u_int x; 712 int err; 713 714 err = ioread(fs, secbyt(fs->lsnfat) + fatoff(fs->fatsz, *c), buf, 715 fs->fatsz != 32 ? 2 : 4); 716 if (err) 717 return err; 718 x = fs->fatsz != 32 ? getushort(buf) : getulong(buf); 719 *c = fs->fatsz == 12 ? *c & 1 ? x >> 4 : x & 0xfff : x; 720 return 0; 721 } 722 723 /* 724 * Is cluster an end-of-chain marker? 725 */ 726 static int 727 fatend(u_int sz, u_int c) 728 { 729 return c > (sz == 12 ? 0xff7U : sz == 16 ? 0xfff7U : 0xffffff7); 730 } 731 732 /* 733 * Offset-based I/O primitive 734 */ 735 static int 736 ioread(DOS_FS *fs, u_int offset, void *buf, u_int nbyte) 737 { 738 char *s; 739 u_int off, n; 740 int err; 741 742 s = buf; 743 if ((off = offset & (SECSIZ - 1))) { 744 offset -= off; 745 if ((err = iobuf(fs, bytsec(offset)))) 746 return err; 747 offset += SECSIZ; 748 if ((n = SECSIZ - off) > nbyte) 749 n = nbyte; 750 memcpy(s, fs->buf + off, n); 751 s += n; 752 nbyte -= n; 753 } 754 n = nbyte & (SECSIZ - 1); 755 if (nbyte -= n) { 756 if ((err = ioget(fs->fd, bytsec(offset), s, bytsec(nbyte)))) 757 return err; 758 offset += nbyte; 759 s += nbyte; 760 } 761 if (n) { 762 if ((err = iobuf(fs, bytsec(offset)))) 763 return err; 764 memcpy(s, fs->buf, n); 765 } 766 return 0; 767 } 768 769 /* 770 * Buffered sector-based I/O primitive 771 */ 772 static int 773 iobuf(DOS_FS *fs, u_int lsec) 774 { 775 int err; 776 777 if (fs->bufsec != lsec) { 778 if ((err = ioget(fs->fd, lsec, fs->buf, 1))) 779 return err; 780 fs->bufsec = lsec; 781 } 782 return 0; 783 } 784 785 /* 786 * Sector-based I/O primitive 787 */ 788 static int 789 ioget(struct open_file *fd, u_int lsec, void *buf, u_int nsec) 790 { 791 size_t rsize; 792 int err; 793 794 #ifndef LIBSA_NO_TWIDDLE 795 twiddle(); 796 #endif 797 err = DEV_STRATEGY(fd->f_dev)(fd->f_devdata, F_READ, lsec, 798 secbyt(nsec), buf, &rsize); 799 return err; 800 } 801