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