1 /* $NetBSD: dosfs.c,v 1.11 2006/12/02 00:38:22 dogcow 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 /* 154 * Mount DOS filesystem 155 */ 156 static int 157 dos_mount(DOS_FS * fs, struct open_file * fd) 158 { 159 int err; 160 161 bzero(fs, sizeof(DOS_FS)); 162 fs->fd = fd; 163 if ((err = !(fs->buf = alloc(SECSIZ)) ? errno : 0) || 164 (err = ioget(fs->fd, 0, fs->buf, 1)) || 165 (err = parsebs(fs, (DOS_BS *) fs->buf))) { 166 (void) dosunmount(fs); 167 return (err); 168 } 169 return 0; 170 } 171 172 #ifndef LIBSA_NO_FS_CLOSE 173 /* 174 * Unmount mounted filesystem 175 */ 176 static int 177 dos_unmount(DOS_FS * fs) 178 { 179 int err; 180 181 if (fs->links) 182 return (EBUSY); 183 if ((err = dosunmount(fs))) 184 return (err); 185 return 0; 186 } 187 #endif 188 189 /* 190 * Common code shared by dos_mount() and dos_unmount() 191 */ 192 static int 193 dosunmount(DOS_FS * fs) 194 { 195 if (fs->buf) 196 dealloc(fs->buf, SECSIZ); 197 dealloc(fs, sizeof(DOS_FS)); 198 return (0); 199 } 200 201 /* 202 * Open DOS file 203 */ 204 int 205 dosfs_open(const char *path, struct open_file *fd) 206 { 207 const struct direntry *de; 208 DOS_FILE *f; 209 DOS_FS *fs; 210 u_int size, clus; 211 int err = 0; 212 213 /* Allocate mount structure, associate with open */ 214 fs = alloc(sizeof(DOS_FS)); 215 216 if ((err = dos_mount(fs, fd))) 217 goto out; 218 219 if ((err = namede(fs, path, &de))) 220 goto out; 221 222 clus = stclus(fs->fatsz, de); 223 size = getulong(de->deFileSize); 224 225 if ((!(de->deAttributes & ATTR_DIRECTORY) && (!clus != !size)) || 226 ((de->deAttributes & ATTR_DIRECTORY) && size) || 227 (clus && !okclus(fs, clus))) { 228 err = EINVAL; 229 goto out; 230 } 231 232 f = alloc(sizeof(DOS_FILE)); 233 #ifdef BOOTXX 234 /* due to __internal_memset_ causing all sorts of register spillage 235 (and being completely unoptimized for zeroing small amounts of 236 memory), if we hand-initialize the remaining members of f to zero, 237 the code size drops 68 bytes. This makes no sense, admittedly. */ 238 f->offset = 0; 239 f->c = 0; 240 #else 241 bzero(f, sizeof(DOS_FILE)); 242 #endif 243 f->fs = fs; 244 fs->links++; 245 f->de = *de; 246 fd->f_fsdata = (void *) f; 247 248 out: 249 return (err); 250 } 251 252 /* 253 * Read from file 254 */ 255 int 256 dosfs_read(struct open_file * fd, void *vbuf, size_t nbyte, size_t * resid) 257 { 258 off_t size; 259 u_int8_t *buf = vbuf; 260 u_int nb, off, clus, c, cnt, n; 261 DOS_FILE *f = (DOS_FILE *) fd->f_fsdata; 262 int err = 0; 263 264 nb = (u_int) nbyte; 265 if ((size = fsize(f->fs, &f->de)) == -1) 266 return EINVAL; 267 if (nb > (n = size - f->offset)) 268 nb = n; 269 off = f->offset; 270 if ((clus = stclus(f->fs->fatsz, &f->de))) 271 off &= f->fs->bsize - 1; 272 c = f->c; 273 cnt = nb; 274 while (cnt) { 275 n = 0; 276 if (!c) { 277 if ((c = clus)) 278 n = bytblk(f->fs, f->offset); 279 } else if (!off) 280 n++; 281 while (n--) { 282 if ((err = fatget(f->fs, &c))) 283 goto out; 284 if (!okclus(f->fs, c)) { 285 err = EINVAL; 286 goto out; 287 } 288 } 289 if (!clus || (n = f->fs->bsize - off) > cnt) 290 n = cnt; 291 if ((err = ioread(f->fs, (c ? blkoff(f->fs, c) : 292 secbyt(f->fs->lsndir)) + off, 293 buf, n))) 294 goto out; 295 f->offset += n; 296 f->c = c; 297 off = 0; 298 buf += n; 299 cnt -= n; 300 } 301 out: 302 if (resid) 303 *resid = nbyte - nb + cnt; 304 return (err); 305 } 306 307 #ifndef LIBSA_NO_FS_WRITE 308 /* 309 * Not implemented. 310 */ 311 int 312 dosfs_write(struct open_file *fd, void *start, size_t size, size_t *resid) 313 { 314 315 return (EROFS); 316 } 317 #endif /* !LIBSA_NO_FS_WRITE */ 318 319 #ifndef LIBSA_NO_FS_SEEK 320 /* 321 * Reposition within file 322 */ 323 off_t 324 dosfs_seek(struct open_file * fd, off_t offset, int whence) 325 { 326 off_t off; 327 u_int size; 328 DOS_FILE *f = (DOS_FILE *) fd->f_fsdata; 329 330 size = getulong(f->de.deFileSize); 331 switch (whence) { 332 case SEEK_SET: 333 off = 0; 334 break; 335 case SEEK_CUR: 336 off = f->offset; 337 break; 338 case SEEK_END: 339 off = size; 340 break; 341 default: 342 return (-1); 343 } 344 off += offset; 345 if (off < 0 || off > size) 346 return (-1); 347 f->offset = (u_int) off; 348 f->c = 0; 349 return (off); 350 } 351 #endif /* !LIBSA_NO_FS_SEEK */ 352 353 #ifndef LIBSA_NO_FS_CLOSE 354 /* 355 * Close open file 356 */ 357 int 358 dosfs_close(struct open_file * fd) 359 { 360 DOS_FILE *f = (DOS_FILE *) fd->f_fsdata; 361 DOS_FS *fs = f->fs; 362 363 f->fs->links--; 364 dealloc(f, sizeof(DOS_FILE)); 365 dos_unmount(fs); 366 return 0; 367 } 368 #endif /* !LIBSA_NO_FS_CLOSE */ 369 370 /* 371 * Return some stat information on a file. 372 */ 373 int 374 dosfs_stat(struct open_file * fd, struct stat * sb) 375 { 376 DOS_FILE *f = (DOS_FILE *) fd->f_fsdata; 377 378 /* only important stuff */ 379 sb->st_mode = (f->de.deAttributes & ATTR_DIRECTORY) ? 380 (S_IFDIR | 0555) : (S_IFREG | 0444); 381 sb->st_nlink = 1; 382 sb->st_uid = 0; 383 sb->st_gid = 0; 384 if ((sb->st_size = fsize(f->fs, &f->de)) == -1) 385 return EINVAL; 386 return (0); 387 } 388 389 /* 390 * Parse DOS boot sector 391 */ 392 static int 393 parsebs(DOS_FS * fs, DOS_BS * bs) 394 { 395 u_int sc; 396 397 if ((bs->jmp[0] != 0x69 && 398 bs->jmp[0] != 0xe9 && 399 (bs->jmp[0] != 0xeb || bs->jmp[2] != 0x90)) || 400 bs->bpb.bpbMedia < 0xf0) 401 return EINVAL; 402 if (getushort(bs->bpb.bpbBytesPerSec) != SECSIZ) 403 return EINVAL; 404 if (!(fs->spc = bs->bpb.bpbSecPerClust) || fs->spc & (fs->spc - 1)) 405 return EINVAL; 406 fs->bsize = secbyt(fs->spc); 407 fs->bshift = ffs(fs->bsize) - 1; 408 if ((fs->spf = getushort(bs->bpb.bpbFATsecs))) { 409 if (bs->bpb.bpbFATs != 2) 410 return EINVAL; 411 if (!(fs->dirents = getushort(bs->bpb.bpbRootDirEnts))) 412 return EINVAL; 413 } else { 414 if (!(fs->spf = getulong(bs->bpb.bpbBigFATsecs))) 415 return EINVAL; 416 if (!bs->bpb.bpbFATs || bs->bpb.bpbFATs > 16) 417 return EINVAL; 418 if ((fs->rdcl = getulong(bs->bpb.bpbRootClust)) < LOCLUS) 419 return EINVAL; 420 } 421 if (!(fs->lsnfat = getushort(bs->bpb.bpbResSectors))) 422 return EINVAL; 423 fs->lsndir = fs->lsnfat + fs->spf * bs->bpb.bpbFATs; 424 fs->lsndta = fs->lsndir + entsec(fs->dirents); 425 if (!(sc = getushort(bs->bpb.bpbSectors)) && 426 !(sc = getulong(bs->bpb.bpbHugeSectors))) 427 return EINVAL; 428 if (fs->lsndta > sc) 429 return EINVAL; 430 if ((fs->xclus = secblk(fs, sc - fs->lsndta) + 1) < LOCLUS) 431 return EINVAL; 432 fs->fatsz = fs->dirents ? fs->xclus < 0xff6 ? 12 : 16 : 32; 433 sc = (secbyt(fs->spf) << 1) / (fs->fatsz >> 2) - 1; 434 if (fs->xclus > sc) 435 fs->xclus = sc; 436 return 0; 437 } 438 439 /* 440 * Return directory entry from path 441 */ 442 static int 443 namede(DOS_FS * fs, const char *path, const struct direntry ** dep) 444 { 445 char name[256]; 446 const struct direntry *de; 447 char *s; 448 size_t n; 449 int err; 450 451 err = 0; 452 de = dot; 453 if (*path == '/') 454 path++; 455 while (*path) { 456 if (!(s = strchr(path, '/'))) 457 s = strchr(path, 0); 458 if ((n = s - path) > 255) 459 return ENAMETOOLONG; 460 memcpy(name, path, n); 461 name[n] = 0; 462 path = s; 463 if (!(de->deAttributes & ATTR_DIRECTORY)) 464 return ENOTDIR; 465 if ((err = lookup(fs, stclus(fs->fatsz, de), name, &de))) 466 return err; 467 if (*path == '/') 468 path++; 469 } 470 *dep = de; 471 return 0; 472 } 473 474 /* 475 * Lookup path segment 476 */ 477 static int 478 lookup(DOS_FS * fs, u_int clus, const char *name, const struct direntry ** dep) 479 { 480 static DOS_DIR *dir = NULL; 481 u_char lfn[261]; 482 u_char sfn[13]; 483 u_int nsec, lsec, xdn, chk, sec, ent, x; 484 int err = 0, ok, i; 485 486 if (!clus) 487 for (ent = 0; ent < 2; ent++) 488 if (!strcasecmp(name, dotstr[ent])) { 489 *dep = dot + ent; 490 return 0; 491 } 492 493 if (dir == NULL) { 494 dir = alloc(sizeof(DOS_DIR) * DEPSEC); 495 if (dir == NULL) 496 return (ENOMEM); 497 } 498 499 if (!clus && fs->fatsz == 32) 500 clus = fs->rdcl; 501 nsec = !clus ? entsec(fs->dirents) : fs->spc; 502 lsec = 0; 503 xdn = chk = 0; 504 for (;;) { 505 if (!clus && !lsec) 506 lsec = fs->lsndir; 507 else if (okclus(fs, clus)) 508 lsec = blklsn(fs, clus); 509 else { 510 err = EINVAL; 511 goto out; 512 } 513 for (sec = 0; sec < nsec; sec++) { 514 if ((err = ioget(fs->fd, lsec + sec, dir, 1))) 515 goto out; 516 for (ent = 0; ent < DEPSEC; ent++) { 517 if (!*dir[ent].de.deName) { 518 err = ENOENT; 519 goto out; 520 } 521 if (*dir[ent].de.deName != 0xe5) { 522 if (dir[ent].de.deAttributes == 523 ATTR_WIN95) { 524 x = dir[ent].xde.weCnt; 525 if (x & WIN_LAST || 526 (x + 1 == xdn && 527 dir[ent].xde.weChksum == 528 chk)) { 529 if (x & WIN_LAST) { 530 chk = dir[ent].xde.weChksum; 531 x &= WIN_CNT; 532 } 533 if (x >= 1 && x <= 20) { 534 cp_xdnm(lfn, &dir[ent].xde); 535 xdn = x; 536 continue; 537 } 538 } 539 } else if (!(dir[ent].de.deAttributes & 540 ATTR_VOLUME)) { 541 if ((ok = xdn == 1)) { 542 for (x = 0, i = 0; 543 i < 11; i++) 544 x = ((((x & 1) << 7) | (x >> 1)) + 545 dir[ent].de.deName[i]) & 0xff; 546 ok = chk == x && 547 !strcasecmp(name, (const char *) lfn); 548 } 549 if (!ok) { 550 cp_sfn(sfn, &dir[ent].de); 551 ok = !strcasecmp(name, (const char *) sfn); 552 } 553 if (ok) { 554 *dep = &dir[ent].de; 555 goto out2; 556 } 557 } 558 } 559 xdn = 0; 560 } 561 } 562 if (!clus) 563 break; 564 if ((err = fatget(fs, &clus))) 565 goto out; 566 if (fatend(fs->fatsz, clus)) 567 break; 568 } 569 err = ENOENT; 570 out: 571 dealloc(dir, sizeof(DOS_DIR) * DEPSEC); 572 dir = NULL; 573 out2: 574 return (err); 575 } 576 577 /* 578 * Copy name from extended directory entry 579 */ 580 static void 581 cp_xdnm(u_char * lfn, struct winentry * xde) 582 { 583 static const struct { 584 u_int off; 585 u_int dim; 586 } ix[3] = { 587 { offsetof(struct winentry, wePart1), 588 sizeof(xde->wePart1) / 2 }, 589 { offsetof(struct winentry, wePart2), 590 sizeof(xde->wePart2) / 2 }, 591 { offsetof(struct winentry, wePart3), 592 sizeof(xde->wePart3) / 2 } 593 }; 594 u_char *p; 595 u_int n, x, c; 596 597 lfn += 13 * ((xde->weCnt & WIN_CNT) - 1); 598 for (n = 0; n < 3; n++) 599 for (p = (u_char *) xde + ix[n].off, x = ix[n].dim; x; 600 p += 2, x--) { 601 if ((c = getushort(p)) && (c < 32 || c > 127)) 602 c = '?'; 603 if (!(*lfn++ = c)) 604 return; 605 } 606 if (xde->weCnt & WIN_LAST) 607 *lfn = 0; 608 } 609 610 /* 611 * Copy short filename 612 */ 613 static void 614 cp_sfn(u_char * sfn, struct direntry * de) 615 { 616 u_char *p; 617 int j, i; 618 619 p = sfn; 620 if (*de->deName != ' ') { 621 for (j = 7; de->deName[j] == ' '; j--); 622 for (i = 0; i <= j; i++) 623 *p++ = de->deName[i]; 624 if (*de->deExtension != ' ') { 625 *p++ = '.'; 626 for (j = 2; de->deExtension[j] == ' '; j--); 627 for (i = 0; i <= j; i++) 628 *p++ = de->deExtension[i]; 629 } 630 } 631 *p = 0; 632 if (*sfn == 5) 633 *sfn = 0xe5; 634 } 635 636 /* 637 * Return size of file in bytes 638 */ 639 static off_t 640 fsize(DOS_FS * fs, struct direntry * de) 641 { 642 u_long size; 643 u_int c; 644 int n; 645 646 if (!(size = getulong(de->deFileSize)) && 647 de->deAttributes & ATTR_DIRECTORY) { 648 if (!(c = getushort(de->deStartCluster))) 649 size = fs->dirents * sizeof(struct direntry); 650 else { 651 if ((n = fatcnt(fs, c)) == -1) 652 return n; 653 size = blkbyt(fs, n); 654 } 655 } 656 return size; 657 } 658 659 /* 660 * Count number of clusters in chain 661 */ 662 static int 663 fatcnt(DOS_FS * fs, u_int c) 664 { 665 int n; 666 667 for (n = 0; okclus(fs, c); n++) 668 if (fatget(fs, &c)) 669 return -1; 670 return fatend(fs->fatsz, c) ? n : -1; 671 } 672 673 /* 674 * Get next cluster in cluster chain 675 */ 676 static int 677 fatget(DOS_FS * fs, u_int * c) 678 { 679 u_char buf[4]; 680 u_int x; 681 int err; 682 683 err = ioread(fs, secbyt(fs->lsnfat) + fatoff(fs->fatsz, *c), buf, 684 fs->fatsz != 32 ? 2 : 4); 685 if (err) 686 return err; 687 x = fs->fatsz != 32 ? getushort(buf) : getulong(buf); 688 *c = fs->fatsz == 12 ? *c & 1 ? x >> 4 : x & 0xfff : x; 689 return 0; 690 } 691 692 /* 693 * Is cluster an end-of-chain marker? 694 */ 695 static int 696 fatend(u_int sz, u_int c) 697 { 698 return c > (sz == 12 ? 0xff7U : sz == 16 ? 0xfff7U : 0xffffff7); 699 } 700 701 /* 702 * Offset-based I/O primitive 703 */ 704 static int 705 ioread(DOS_FS * fs, u_int offset, void *buf, u_int nbyte) 706 { 707 char *s; 708 u_int off, n; 709 int err; 710 711 s = buf; 712 if ((off = offset & (SECSIZ - 1))) { 713 offset -= off; 714 if ((err = iobuf(fs, bytsec(offset)))) 715 return err; 716 offset += SECSIZ; 717 if ((n = SECSIZ - off) > nbyte) 718 n = nbyte; 719 memcpy(s, fs->buf + off, n); 720 s += n; 721 nbyte -= n; 722 } 723 n = nbyte & (SECSIZ - 1); 724 if (nbyte -= n) { 725 if ((err = ioget(fs->fd, bytsec(offset), s, bytsec(nbyte)))) 726 return err; 727 offset += nbyte; 728 s += nbyte; 729 } 730 if (n) { 731 if ((err = iobuf(fs, bytsec(offset)))) 732 return err; 733 memcpy(s, fs->buf, n); 734 } 735 return 0; 736 } 737 738 /* 739 * Buffered sector-based I/O primitive 740 */ 741 static int 742 iobuf(DOS_FS * fs, u_int lsec) 743 { 744 int err; 745 746 if (fs->bufsec != lsec) { 747 if ((err = ioget(fs->fd, lsec, fs->buf, 1))) 748 return err; 749 fs->bufsec = lsec; 750 } 751 return 0; 752 } 753 754 /* 755 * Sector-based I/O primitive 756 */ 757 static int 758 ioget(struct open_file * fd, u_int lsec, void *buf, u_int nsec) 759 { 760 size_t rsize; 761 int err; 762 763 #ifndef LIBSA_NO_TWIDDLE 764 twiddle(); 765 #endif 766 err = DEV_STRATEGY(fd->f_dev)(fd->f_devdata, F_READ, lsec, 767 secbyt(nsec), buf, &rsize); 768 return (err); 769 } 770