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 * $FreeBSD: src/lib/libstand/dosfs.c,v 1.4.2.1 2000/05/04 13:47:49 ps Exp $ 28 * $DragonFly: src/lib/libstand/dosfs.c,v 1.2 2003/06/17 04:26:51 dillon Exp $ 29 */ 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 *path, struct open_file *fd); 46 static int dos_close(struct open_file *fd); 47 static int dos_read(struct open_file *fd, void *buf, size_t size, size_t *resid); 48 static off_t dos_seek(struct open_file *fd, off_t offset, int whence); 49 static int dos_stat(struct open_file *fd, struct stat *sb); 50 static int dos_readdir(struct open_file *fd, struct dirent *d); 51 52 struct fs_ops dosfs_fsops = { 53 "dosfs", 54 dos_open, 55 dos_close, 56 dos_read, 57 null_write, 58 dos_seek, 59 dos_stat, 60 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 69 /* DOS "BIOS Parameter Block" */ 70 typedef struct { 71 u_char secsiz[2]; /* sector size */ 72 u_char spc; /* sectors per cluster */ 73 u_char ressec[2]; /* reserved sectors */ 74 u_char fats; /* FATs */ 75 u_char dirents[2]; /* root directory entries */ 76 u_char secs[2]; /* total sectors */ 77 u_char media; /* media descriptor */ 78 u_char spf[2]; /* sectors per FAT */ 79 u_char spt[2]; /* sectors per track */ 80 u_char heads[2]; /* drive heads */ 81 u_char hidsec[4]; /* hidden sectors */ 82 u_char lsecs[4]; /* huge sectors */ 83 u_char lspf[4]; /* huge sectors per FAT */ 84 u_char xflg[2]; /* flags */ 85 u_char vers[2]; /* filesystem version */ 86 u_char rdcl[4]; /* root directory start cluster */ 87 u_char infs[2]; /* filesystem info sector */ 88 u_char bkbs[2]; /* backup boot sector */ 89 } DOS_BPB; 90 91 /* Initial portion of DOS boot sector */ 92 typedef struct { 93 u_char jmp[3]; /* usually 80x86 'jmp' opcode */ 94 u_char 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 ((u_int)cv2((de)->dex.h_clus) << 16) | \ 133 cv2((de)->clus)) 134 135 static int dosunmount(DOS_FS *); 136 static int parsebs(DOS_FS *, DOS_BS *); 137 static int namede(DOS_FS *, const char *, DOS_DE **); 138 static int lookup(DOS_FS *, u_int, const char *, DOS_DE **); 139 static void cp_xdnm(u_char *, DOS_XDE *); 140 static void cp_sfn(u_char *, DOS_DE *); 141 static off_t fsize(DOS_FS *, DOS_DE *); 142 static int fatcnt(DOS_FS *, u_int); 143 static int fatget(DOS_FS *, u_int *); 144 static int fatend(u_int, u_int); 145 static int ioread(DOS_FS *, u_int, void *, u_int); 146 static int iobuf(DOS_FS *, u_int); 147 static int ioget(struct open_file *, u_int, void *, u_int); 148 149 /* 150 * Mount DOS filesystem 151 */ 152 static int 153 dos_mount(DOS_FS *fs, struct open_file *fd) 154 { 155 int err; 156 157 bzero(fs, sizeof(DOS_FS)); 158 fs->fd = fd; 159 if ((err = !(fs->buf = malloc(SECSIZ)) ? errno : 0) || 160 (err = ioget(fs->fd, 0, fs->buf, 1)) || 161 (err = parsebs(fs, (DOS_BS *)fs->buf))) { 162 (void)dosunmount(fs); 163 return(err); 164 } 165 fs->root = dot[0]; 166 fs->root.name[0] = ' '; 167 if (fs->fatsz == 32) { 168 fs->root.clus[0] = fs->rdcl & 0xff; 169 fs->root.clus[1] = (fs->rdcl >> 8) & 0xff; 170 fs->root.dex.h_clus[0] = (fs->rdcl >> 16) & 0xff; 171 fs->root.dex.h_clus[1] = (fs->rdcl >> 24) & 0xff; 172 } 173 return 0; 174 } 175 176 /* 177 * Unmount mounted filesystem 178 */ 179 static int 180 dos_unmount(DOS_FS *fs) 181 { 182 int err; 183 184 if (fs->links) 185 return(EBUSY); 186 if ((err = dosunmount(fs))) 187 return(err); 188 return 0; 189 } 190 191 /* 192 * Common code shared by dos_mount() and dos_unmount() 193 */ 194 static int 195 dosunmount(DOS_FS *fs) 196 { 197 if (fs->buf) 198 free(fs->buf); 199 free(fs); 200 return(0); 201 } 202 203 /* 204 * Open DOS file 205 */ 206 static int 207 dos_open(const char *path, struct open_file *fd) 208 { 209 DOS_DE *de; 210 DOS_FILE *f; 211 DOS_FS *fs; 212 u_int size, clus; 213 int err = 0; 214 215 /* Allocate mount structure, associate with open */ 216 fs = malloc(sizeof(DOS_FS)); 217 218 if ((err = dos_mount(fs, fd))) 219 goto out; 220 221 if ((err = namede(fs, path, &de))) 222 goto out; 223 224 clus = stclus(fs->fatsz, de); 225 size = cv4(de->size); 226 227 if ((!(de->attr & FA_DIR) && (!clus != !size)) || 228 ((de->attr & FA_DIR) && size) || 229 (clus && !okclus(fs, clus))) { 230 err = EINVAL; 231 goto out; 232 } 233 f = malloc(sizeof(DOS_FILE)); 234 bzero(f, sizeof(DOS_FILE)); 235 f->fs = fs; 236 fs->links++; 237 f->de = *de; 238 fd->f_fsdata = (void *)f; 239 240 out: 241 return(err); 242 } 243 244 /* 245 * Read from file 246 */ 247 static int 248 dos_read(struct open_file *fd, void *buf, size_t nbyte, size_t *resid) 249 { 250 off_t size; 251 u_int nb, off, clus, c, cnt, n; 252 DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 253 int err = 0; 254 255 nb = (u_int)nbyte; 256 if ((size = fsize(f->fs, &f->de)) == -1) 257 return EINVAL; 258 if (nb > (n = size - f->offset)) 259 nb = n; 260 off = f->offset; 261 if ((clus = stclus(f->fs->fatsz, &f->de))) 262 off &= f->fs->bsize - 1; 263 c = f->c; 264 cnt = nb; 265 while (cnt) { 266 n = 0; 267 if (!c) { 268 if ((c = clus)) 269 n = bytblk(f->fs, f->offset); 270 } else if (!off) 271 n++; 272 while (n--) { 273 if ((err = fatget(f->fs, &c))) 274 goto out; 275 if (!okclus(f->fs, c)) { 276 err = EINVAL; 277 goto out; 278 } 279 } 280 if (!clus || (n = f->fs->bsize - off) > cnt) 281 n = cnt; 282 if ((err = ioread(f->fs, (c ? blkoff(f->fs, c) : 283 secbyt(f->fs->lsndir)) + off, 284 buf, n))) 285 goto out; 286 f->offset += n; 287 f->c = c; 288 off = 0; 289 buf += n; 290 cnt -= n; 291 } 292 out: 293 if (resid) 294 *resid = nbyte - nb + cnt; 295 return(err); 296 } 297 298 /* 299 * Reposition within file 300 */ 301 static off_t 302 dos_seek(struct open_file *fd, off_t offset, int whence) 303 { 304 off_t off; 305 u_int size; 306 DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 307 308 size = cv4(f->de.size); 309 switch (whence) { 310 case SEEK_SET: 311 off = 0; 312 break; 313 case SEEK_CUR: 314 off = f->offset; 315 break; 316 case SEEK_END: 317 off = size; 318 break; 319 default: 320 return(-1); 321 } 322 off += offset; 323 if (off < 0 || off > size) 324 return(-1); 325 f->offset = (u_int)off; 326 f->c = 0; 327 return(off); 328 } 329 330 /* 331 * Close open file 332 */ 333 static int 334 dos_close(struct open_file *fd) 335 { 336 DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 337 DOS_FS *fs; 338 339 fd->f_fsdata = NULL; 340 if (f) { 341 fs = f->fs; 342 f->fs = NULL; 343 fs->links--; 344 free(f); 345 dos_unmount(fs); 346 } 347 return 0; 348 } 349 350 /* 351 * Return some stat information on a file. 352 */ 353 static int 354 dos_stat(struct open_file *fd, struct stat *sb) 355 { 356 DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 357 358 /* only important stuff */ 359 sb->st_mode = f->de.attr & FA_DIR ? S_IFDIR | 0555 : S_IFREG | 0444; 360 sb->st_nlink = 1; 361 sb->st_uid = 0; 362 sb->st_gid = 0; 363 if ((sb->st_size = fsize(f->fs, &f->de)) == -1) 364 return EINVAL; 365 return (0); 366 } 367 368 static int 369 dos_readdir(struct open_file *fd, struct dirent *d) 370 { 371 /* DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; */ 372 u_char fn[261]; 373 DOS_DIR dd; 374 size_t res; 375 u_int chk, i, x, xdn; 376 int err; 377 378 x = chk = 0; 379 while (1) { 380 xdn = x; 381 x = 0; 382 err = dos_read(fd, &dd, sizeof(dd), &res); 383 if (err) 384 return (err); 385 if (res == sizeof(dd)) 386 return (ENOENT); 387 if (dd.de.name[0] == 0) 388 return (ENOENT); 389 390 /* Skip deleted entries */ 391 if (dd.de.name[0] == 0xe5) 392 continue; 393 394 /* Check if directory entry is volume label */ 395 if (dd.de.attr & FA_LABEL) { 396 /* 397 * If volume label set, check if the current entry is 398 * extended entry (FA_XDE) for long file names. 399 */ 400 if ((dd.de.attr & FA_MASK) == FA_XDE) { 401 /* 402 * Read through all following extended entries 403 * to get the long file name. 0x40 marks the 404 * last entry containing part of long file name. 405 */ 406 if (dd.xde.seq & 0x40) 407 chk = dd.xde.chk; 408 else if (dd.xde.seq != xdn - 1 || dd.xde.chk != chk) 409 continue; 410 x = dd.xde.seq & ~0x40; 411 if (x < 1 || x > 20) { 412 x = 0; 413 continue; 414 } 415 cp_xdnm(fn, &dd.xde); 416 } else { 417 /* skip only volume label entries */ 418 continue; 419 } 420 } else { 421 if (xdn == 1) { 422 x = 0; 423 for (i = 0; i < 11; i++) { 424 x = ((x & 1) << 7) | (x >> 1); 425 x += (i < 8) ? dd.de.name[i] : dd.de.ext[i-8]; 426 x &= 0xff; 427 } 428 if (x == chk) 429 break; 430 } else { 431 cp_sfn(fn, &dd.de); 432 break; 433 } 434 x = 0; 435 } 436 } 437 438 d->d_fileno = (dd.de.clus[1] << 8) + dd.de.clus[0]; 439 d->d_type = (dd.de.attr & FA_DIR) ? DT_DIR : DT_REG; 440 memcpy(d->d_name, fn, sizeof(d->d_name)); 441 return(0); 442 } 443 444 /* 445 * Parse DOS boot sector 446 */ 447 static int 448 parsebs(DOS_FS *fs, DOS_BS *bs) 449 { 450 u_int sc; 451 452 if ((bs->jmp[0] != 0x69 && 453 bs->jmp[0] != 0xe9 && 454 (bs->jmp[0] != 0xeb || bs->jmp[2] != 0x90)) || 455 bs->bpb.media < 0xf0) 456 return EINVAL; 457 if (cv2(bs->bpb.secsiz) != SECSIZ) 458 return EINVAL; 459 if (!(fs->spc = bs->bpb.spc) || fs->spc & (fs->spc - 1)) 460 return EINVAL; 461 fs->bsize = secbyt(fs->spc); 462 fs->bshift = ffs(fs->bsize) - 1; 463 if ((fs->spf = cv2(bs->bpb.spf))) { 464 if (bs->bpb.fats != 2) 465 return EINVAL; 466 if (!(fs->dirents = cv2(bs->bpb.dirents))) 467 return EINVAL; 468 } else { 469 if (!(fs->spf = cv4(bs->bpb.lspf))) 470 return EINVAL; 471 if (!bs->bpb.fats || bs->bpb.fats > 16) 472 return EINVAL; 473 if ((fs->rdcl = cv4(bs->bpb.rdcl)) < LOCLUS) 474 return EINVAL; 475 } 476 if (!(fs->lsnfat = cv2(bs->bpb.ressec))) 477 return EINVAL; 478 fs->lsndir = fs->lsnfat + fs->spf * bs->bpb.fats; 479 fs->lsndta = fs->lsndir + entsec(fs->dirents); 480 if (!(sc = cv2(bs->bpb.secs)) && !(sc = cv4(bs->bpb.lsecs))) 481 return EINVAL; 482 if (fs->lsndta > sc) 483 return EINVAL; 484 if ((fs->xclus = secblk(fs, sc - fs->lsndta) + 1) < LOCLUS) 485 return EINVAL; 486 fs->fatsz = fs->dirents ? fs->xclus < 0xff6 ? 12 : 16 : 32; 487 sc = (secbyt(fs->spf) << 1) / (fs->fatsz >> 2) - 1; 488 if (fs->xclus > sc) 489 fs->xclus = sc; 490 return 0; 491 } 492 493 /* 494 * Return directory entry from path 495 */ 496 static int 497 namede(DOS_FS *fs, const char *path, DOS_DE **dep) 498 { 499 char name[256]; 500 DOS_DE *de; 501 char *s; 502 size_t n; 503 int err; 504 505 err = 0; 506 de = &fs->root; 507 while (*path) { 508 while (*path == '/') 509 path++; 510 if (*path == '\0') 511 break; 512 if (!(s = strchr(path, '/'))) 513 s = strchr(path, 0); 514 if ((n = s - path) > 255) 515 return ENAMETOOLONG; 516 memcpy(name, path, n); 517 name[n] = 0; 518 path = s; 519 if (!(de->attr & FA_DIR)) 520 return ENOTDIR; 521 if ((err = lookup(fs, stclus(fs->fatsz, de), name, &de))) 522 return err; 523 } 524 *dep = de; 525 return 0; 526 } 527 528 /* 529 * Lookup path segment 530 */ 531 static int 532 lookup(DOS_FS *fs, u_int clus, const char *name, DOS_DE **dep) 533 { 534 static DOS_DIR dir[DEPSEC]; 535 u_char lfn[261]; 536 u_char sfn[13]; 537 u_int nsec, lsec, xdn, chk, sec, ent, x; 538 int err, ok, i; 539 540 if (!clus) 541 for (ent = 0; ent < 2; ent++) 542 if (!strcasecmp(name, dotstr[ent])) { 543 *dep = dot + ent; 544 return 0; 545 } 546 if (!clus && fs->fatsz == 32) 547 clus = fs->rdcl; 548 nsec = !clus ? entsec(fs->dirents) : fs->spc; 549 lsec = 0; 550 xdn = chk = 0; 551 for (;;) { 552 if (!clus && !lsec) 553 lsec = fs->lsndir; 554 else if (okclus(fs, clus)) 555 lsec = blklsn(fs, clus); 556 else 557 return EINVAL; 558 for (sec = 0; sec < nsec; sec++) { 559 if ((err = ioget(fs->fd, lsec + sec, dir, 1))) 560 return err; 561 for (ent = 0; ent < DEPSEC; ent++) { 562 if (!*dir[ent].de.name) 563 return ENOENT; 564 if (*dir[ent].de.name != 0xe5) { 565 if ((dir[ent].de.attr & FA_MASK) == FA_XDE) { 566 x = dir[ent].xde.seq; 567 if (x & 0x40 || (x + 1 == xdn && 568 dir[ent].xde.chk == chk)) { 569 if (x & 0x40) { 570 chk = dir[ent].xde.chk; 571 x &= ~0x40; 572 } 573 if (x >= 1 && x <= 20) { 574 cp_xdnm(lfn, &dir[ent].xde); 575 xdn = x; 576 continue; 577 } 578 } 579 } else if (!(dir[ent].de.attr & FA_LABEL)) { 580 if ((ok = xdn == 1)) { 581 for (x = 0, i = 0; i < 11; i++) 582 x = ((((x & 1) << 7) | (x >> 1)) + 583 ((i < 8) ? dir[ent].de.name[i] : 584 dir[ent].de.ext[i - 8]) 585 ) & 0xff; 586 ok = chk == x && 587 !strcasecmp(name, (const char *)lfn); 588 } 589 if (!ok) { 590 cp_sfn(sfn, &dir[ent].de); 591 ok = !strcasecmp(name, (const char *)sfn); 592 } 593 if (ok) { 594 *dep = &dir[ent].de; 595 return 0; 596 } 597 } 598 } 599 xdn = 0; 600 } 601 } 602 if (!clus) 603 break; 604 if ((err = fatget(fs, &clus))) 605 return err; 606 if (fatend(fs->fatsz, clus)) 607 break; 608 } 609 return ENOENT; 610 } 611 612 /* 613 * Copy name from extended directory entry 614 */ 615 static void 616 cp_xdnm(u_char *lfn, DOS_XDE *xde) 617 { 618 static struct { 619 u_int off; 620 u_int dim; 621 } ix[3] = { 622 {offsetof(DOS_XDE, name1), sizeof(xde->name1) / 2}, 623 {offsetof(DOS_XDE, name2), sizeof(xde->name2) / 2}, 624 {offsetof(DOS_XDE, name3), sizeof(xde->name3) / 2} 625 }; 626 u_char *p; 627 u_int n, x, c; 628 629 lfn += 13 * ((xde->seq & ~0x40) - 1); 630 for (n = 0; n < 3; n++) 631 for (p = (u_char *)xde + ix[n].off, x = ix[n].dim; x; 632 p += 2, x--) { 633 if ((c = cv2(p)) && (c < 32 || c > 127)) 634 c = '?'; 635 if (!(*lfn++ = c)) 636 return; 637 } 638 if (xde->seq & 0x40) 639 *lfn = 0; 640 } 641 642 /* 643 * Copy short filename 644 */ 645 static void 646 cp_sfn(u_char *sfn, DOS_DE *de) 647 { 648 u_char *p; 649 int j, i; 650 651 p = sfn; 652 if (*de->name != ' ') { 653 for (j = 7; de->name[j] == ' '; j--); 654 for (i = 0; i <= j; i++) 655 *p++ = de->name[i]; 656 if (*de->ext != ' ') { 657 *p++ = '.'; 658 for (j = 2; de->ext[j] == ' '; j--); 659 for (i = 0; i <= j; i++) 660 *p++ = de->ext[i]; 661 } 662 } 663 *p = 0; 664 if (*sfn == 5) 665 *sfn = 0xe5; 666 } 667 668 /* 669 * Return size of file in bytes 670 */ 671 static off_t 672 fsize(DOS_FS *fs, DOS_DE *de) 673 { 674 u_long size; 675 u_int c; 676 int n; 677 678 if (!(size = cv4(de->size)) && de->attr & FA_DIR) { 679 if (!(c = cv2(de->clus))) 680 size = fs->dirents * sizeof(DOS_DE); 681 else { 682 if ((n = fatcnt(fs, c)) == -1) 683 return n; 684 size = blkbyt(fs, n); 685 } 686 } 687 return 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 ? cv2(buf) : cv4(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 int err; 792 793 twiddle(); 794 if ((err = (fd->f_dev->dv_strategy)(fd->f_devdata, F_READ, lsec, 795 secbyt(nsec), buf, NULL))) 796 return(err); 797 return(0); 798 } 799