14a5d661aSToomas Soome /* 24a5d661aSToomas Soome * Copyright (c) 1996, 1998 Robert Nordier 34a5d661aSToomas Soome * All rights reserved. 44a5d661aSToomas Soome * 54a5d661aSToomas Soome * Redistribution and use in source and binary forms, with or without 64a5d661aSToomas Soome * modification, are permitted provided that the following conditions 74a5d661aSToomas Soome * are met: 84a5d661aSToomas Soome * 1. Redistributions of source code must retain the above copyright 94a5d661aSToomas Soome * notice, this list of conditions and the following disclaimer. 104a5d661aSToomas Soome * 2. Redistributions in binary form must reproduce the above copyright 114a5d661aSToomas Soome * notice, this list of conditions and the following disclaimer in 124a5d661aSToomas Soome * the documentation and/or other materials provided with the 134a5d661aSToomas Soome * distribution. 144a5d661aSToomas Soome * 154a5d661aSToomas Soome * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS 164a5d661aSToomas Soome * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 174a5d661aSToomas Soome * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 184a5d661aSToomas Soome * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY 194a5d661aSToomas Soome * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 204a5d661aSToomas Soome * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 214a5d661aSToomas Soome * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 224a5d661aSToomas Soome * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 234a5d661aSToomas Soome * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 244a5d661aSToomas Soome * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 254a5d661aSToomas Soome * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 264a5d661aSToomas Soome */ 274a5d661aSToomas Soome 284a5d661aSToomas Soome #include <sys/cdefs.h> 294a5d661aSToomas Soome __FBSDID("$FreeBSD$"); 304a5d661aSToomas Soome 314a5d661aSToomas Soome /* 324a5d661aSToomas Soome * Readonly filesystem for Microsoft FAT12/FAT16/FAT32 filesystems, 334a5d661aSToomas Soome * also supports VFAT. 344a5d661aSToomas Soome */ 354a5d661aSToomas Soome 364a5d661aSToomas Soome #include <sys/types.h> 374a5d661aSToomas Soome #include <string.h> 384a5d661aSToomas Soome #include <stddef.h> 394a5d661aSToomas Soome 404a5d661aSToomas Soome #include "stand.h" 414a5d661aSToomas Soome 424a5d661aSToomas Soome #include "dosfs.h" 434a5d661aSToomas Soome 444a5d661aSToomas Soome 454a5d661aSToomas Soome static int dos_open(const char *path, struct open_file *fd); 464a5d661aSToomas Soome static int dos_close(struct open_file *fd); 474a5d661aSToomas Soome static int dos_read(struct open_file *fd, void *buf, size_t size, size_t *resid); 484a5d661aSToomas Soome static off_t dos_seek(struct open_file *fd, off_t offset, int whence); 494a5d661aSToomas Soome static int dos_stat(struct open_file *fd, struct stat *sb); 504a5d661aSToomas Soome static int dos_readdir(struct open_file *fd, struct dirent *d); 514a5d661aSToomas Soome 524a5d661aSToomas Soome struct fs_ops dosfs_fsops = { 534a5d661aSToomas Soome "dosfs", 544a5d661aSToomas Soome dos_open, 554a5d661aSToomas Soome dos_close, 564a5d661aSToomas Soome dos_read, 574a5d661aSToomas Soome null_write, 584a5d661aSToomas Soome dos_seek, 594a5d661aSToomas Soome dos_stat, 604a5d661aSToomas Soome dos_readdir 614a5d661aSToomas Soome }; 624a5d661aSToomas Soome 634a5d661aSToomas Soome #define SECSIZ 512 /* sector size */ 644a5d661aSToomas Soome #define SSHIFT 9 /* SECSIZ shift */ 654a5d661aSToomas Soome #define DEPSEC 16 /* directory entries per sector */ 664a5d661aSToomas Soome #define DSHIFT 4 /* DEPSEC shift */ 674a5d661aSToomas Soome #define LOCLUS 2 /* lowest cluster number */ 684a5d661aSToomas Soome 694a5d661aSToomas Soome /* DOS "BIOS Parameter Block" */ 704a5d661aSToomas Soome typedef struct { 714a5d661aSToomas Soome u_char secsiz[2]; /* sector size */ 724a5d661aSToomas Soome u_char spc; /* sectors per cluster */ 734a5d661aSToomas Soome u_char ressec[2]; /* reserved sectors */ 744a5d661aSToomas Soome u_char fats; /* FATs */ 754a5d661aSToomas Soome u_char dirents[2]; /* root directory entries */ 764a5d661aSToomas Soome u_char secs[2]; /* total sectors */ 774a5d661aSToomas Soome u_char media; /* media descriptor */ 784a5d661aSToomas Soome u_char spf[2]; /* sectors per FAT */ 794a5d661aSToomas Soome u_char spt[2]; /* sectors per track */ 804a5d661aSToomas Soome u_char heads[2]; /* drive heads */ 814a5d661aSToomas Soome u_char hidsec[4]; /* hidden sectors */ 824a5d661aSToomas Soome u_char lsecs[4]; /* huge sectors */ 834a5d661aSToomas Soome u_char lspf[4]; /* huge sectors per FAT */ 844a5d661aSToomas Soome u_char xflg[2]; /* flags */ 854a5d661aSToomas Soome u_char vers[2]; /* filesystem version */ 864a5d661aSToomas Soome u_char rdcl[4]; /* root directory start cluster */ 874a5d661aSToomas Soome u_char infs[2]; /* filesystem info sector */ 884a5d661aSToomas Soome u_char bkbs[2]; /* backup boot sector */ 894a5d661aSToomas Soome } DOS_BPB; 904a5d661aSToomas Soome 914a5d661aSToomas Soome /* Initial portion of DOS boot sector */ 924a5d661aSToomas Soome typedef struct { 934a5d661aSToomas Soome u_char jmp[3]; /* usually 80x86 'jmp' opcode */ 944a5d661aSToomas Soome u_char oem[8]; /* OEM name and version */ 954a5d661aSToomas Soome DOS_BPB bpb; /* BPB */ 964a5d661aSToomas Soome } DOS_BS; 974a5d661aSToomas Soome 984a5d661aSToomas Soome /* Supply missing "." and ".." root directory entries */ 994a5d661aSToomas Soome static const char *const dotstr[2] = {".", ".."}; 1004a5d661aSToomas Soome static DOS_DE dot[2] = { 1014a5d661aSToomas Soome {". ", " ", FA_DIR, {0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 1024a5d661aSToomas Soome {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}}, 1034a5d661aSToomas Soome {".. ", " ", FA_DIR, {0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 1044a5d661aSToomas Soome {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}} 1054a5d661aSToomas Soome }; 1064a5d661aSToomas Soome 1074a5d661aSToomas Soome /* The usual conversion macros to avoid multiplication and division */ 1084a5d661aSToomas Soome #define bytsec(n) ((n) >> SSHIFT) 1094a5d661aSToomas Soome #define secbyt(s) ((s) << SSHIFT) 1104a5d661aSToomas Soome #define entsec(e) ((e) >> DSHIFT) 1114a5d661aSToomas Soome #define bytblk(fs, n) ((n) >> (fs)->bshift) 1124a5d661aSToomas Soome #define blkbyt(fs, b) ((b) << (fs)->bshift) 1134a5d661aSToomas Soome #define secblk(fs, s) ((s) >> ((fs)->bshift - SSHIFT)) 1144a5d661aSToomas Soome #define blksec(fs, b) ((b) << ((fs)->bshift - SSHIFT)) 1154a5d661aSToomas Soome 1164a5d661aSToomas Soome /* Convert cluster number to offset within filesystem */ 1174a5d661aSToomas Soome #define blkoff(fs, b) (secbyt((fs)->lsndta) + blkbyt(fs, (b) - LOCLUS)) 1184a5d661aSToomas Soome 1194a5d661aSToomas Soome /* Convert cluster number to logical sector number */ 1204a5d661aSToomas Soome #define blklsn(fs, b) ((fs)->lsndta + blksec(fs, (b) - LOCLUS)) 1214a5d661aSToomas Soome 1224a5d661aSToomas Soome /* Convert cluster number to offset within FAT */ 1234a5d661aSToomas Soome #define fatoff(sz, c) ((sz) == 12 ? (c) + ((c) >> 1) : \ 1244a5d661aSToomas Soome (sz) == 16 ? (c) << 1 : \ 1254a5d661aSToomas Soome (c) << 2) 1264a5d661aSToomas Soome 1274a5d661aSToomas Soome /* Does cluster number reference a valid data cluster? */ 1284a5d661aSToomas Soome #define okclus(fs, c) ((c) >= LOCLUS && (c) <= (fs)->xclus) 1294a5d661aSToomas Soome 1304a5d661aSToomas Soome /* Get start cluster from directory entry */ 1314a5d661aSToomas Soome #define stclus(sz, de) ((sz) != 32 ? cv2((de)->clus) : \ 1324a5d661aSToomas Soome ((u_int)cv2((de)->dex.h_clus) << 16) | \ 1334a5d661aSToomas Soome cv2((de)->clus)) 1344a5d661aSToomas Soome 1354a5d661aSToomas Soome /* 1364a5d661aSToomas Soome * fat cache metadata 1374a5d661aSToomas Soome */ 1384a5d661aSToomas Soome struct fatcache { 1394a5d661aSToomas Soome int unit; /* disk unit number */ 1404a5d661aSToomas Soome int size; /* buffer (and fat) size in sectors */ 1414a5d661aSToomas Soome u_char *buf; 1424a5d661aSToomas Soome }; 1434a5d661aSToomas Soome 1444a5d661aSToomas Soome static struct fatcache fat; 1454a5d661aSToomas Soome 1464a5d661aSToomas Soome static int dosunmount(DOS_FS *); 1474a5d661aSToomas Soome static int parsebs(DOS_FS *, DOS_BS *); 1484a5d661aSToomas Soome static int namede(DOS_FS *, const char *, DOS_DE **); 1494a5d661aSToomas Soome static int lookup(DOS_FS *, u_int, const char *, DOS_DE **); 1504a5d661aSToomas Soome static void cp_xdnm(u_char *, DOS_XDE *); 1514a5d661aSToomas Soome static void cp_sfn(u_char *, DOS_DE *); 1524a5d661aSToomas Soome static off_t fsize(DOS_FS *, DOS_DE *); 1534a5d661aSToomas Soome static int fatcnt(DOS_FS *, u_int); 1544a5d661aSToomas Soome static int fatget(DOS_FS *, u_int *); 1554a5d661aSToomas Soome static int fatend(u_int, u_int); 1564a5d661aSToomas Soome static int ioread(DOS_FS *, u_int, void *, u_int); 157*976852c7SToomas Soome static int ioget(struct open_file *, daddr_t, void *, u_int); 1584a5d661aSToomas Soome 1594a5d661aSToomas Soome static void 1604a5d661aSToomas Soome dos_read_fat(DOS_FS *fs, struct open_file *fd) 1614a5d661aSToomas Soome { 1624a5d661aSToomas Soome struct devdesc *dd = fd->f_devdata; 1634a5d661aSToomas Soome 1644a5d661aSToomas Soome if (fat.buf != NULL) { /* can we reuse old buffer? */ 1654a5d661aSToomas Soome if (fat.size != fs->spf) { 1664a5d661aSToomas Soome free(fat.buf); /* no, free old buffer */ 1674a5d661aSToomas Soome fat.buf = NULL; 1684a5d661aSToomas Soome } 1694a5d661aSToomas Soome } 1704a5d661aSToomas Soome 1714a5d661aSToomas Soome if (fat.buf == NULL) 1724a5d661aSToomas Soome fat.buf = malloc(secbyt(fs->spf)); 1734a5d661aSToomas Soome 1744a5d661aSToomas Soome if (fat.buf != NULL) { 175*976852c7SToomas Soome if (ioget(fd, fs->lsnfat, fat.buf, secbyt(fs->spf)) == 0) { 1764a5d661aSToomas Soome fat.size = fs->spf; 1774a5d661aSToomas Soome fat.unit = dd->d_unit; 1784a5d661aSToomas Soome return; 1794a5d661aSToomas Soome } 1804a5d661aSToomas Soome } 1814a5d661aSToomas Soome if (fat.buf != NULL) /* got IO error */ 1824a5d661aSToomas Soome free(fat.buf); 1834a5d661aSToomas Soome fat.buf = NULL; 1844a5d661aSToomas Soome fat.unit = -1; /* impossible unit */ 1854a5d661aSToomas Soome fat.size = 0; 1864a5d661aSToomas Soome } 1874a5d661aSToomas Soome 1884a5d661aSToomas Soome /* 1894a5d661aSToomas Soome * Mount DOS filesystem 1904a5d661aSToomas Soome */ 1914a5d661aSToomas Soome static int 1924a5d661aSToomas Soome dos_mount(DOS_FS *fs, struct open_file *fd) 1934a5d661aSToomas Soome { 1944a5d661aSToomas Soome int err; 1954a5d661aSToomas Soome struct devdesc *dd = fd->f_devdata; 1964a5d661aSToomas Soome u_char *buf; 1974a5d661aSToomas Soome 1984a5d661aSToomas Soome bzero(fs, sizeof(DOS_FS)); 1994a5d661aSToomas Soome fs->fd = fd; 2004a5d661aSToomas Soome 2014a5d661aSToomas Soome if ((err = !(buf = malloc(secbyt(1))) ? errno : 0) || 202*976852c7SToomas Soome (err = ioget(fs->fd, 0, buf, secbyt(1))) || 2034a5d661aSToomas Soome (err = parsebs(fs, (DOS_BS *)buf))) { 2044a5d661aSToomas Soome if (buf != NULL) 2054a5d661aSToomas Soome free(buf); 2064a5d661aSToomas Soome (void)dosunmount(fs); 2074a5d661aSToomas Soome return (err); 2084a5d661aSToomas Soome } 2094a5d661aSToomas Soome free(buf); 2104a5d661aSToomas Soome 2114a5d661aSToomas Soome if (fat.buf == NULL || fat.unit != dd->d_unit) 2124a5d661aSToomas Soome dos_read_fat(fs, fd); 2134a5d661aSToomas Soome 2144a5d661aSToomas Soome fs->root = dot[0]; 2154a5d661aSToomas Soome fs->root.name[0] = ' '; 2164a5d661aSToomas Soome if (fs->fatsz == 32) { 2174a5d661aSToomas Soome fs->root.clus[0] = fs->rdcl & 0xff; 2184a5d661aSToomas Soome fs->root.clus[1] = (fs->rdcl >> 8) & 0xff; 2194a5d661aSToomas Soome fs->root.dex.h_clus[0] = (fs->rdcl >> 16) & 0xff; 2204a5d661aSToomas Soome fs->root.dex.h_clus[1] = (fs->rdcl >> 24) & 0xff; 2214a5d661aSToomas Soome } 222*976852c7SToomas Soome return (0); 2234a5d661aSToomas Soome } 2244a5d661aSToomas Soome 2254a5d661aSToomas Soome /* 2264a5d661aSToomas Soome * Unmount mounted filesystem 2274a5d661aSToomas Soome */ 2284a5d661aSToomas Soome static int 2294a5d661aSToomas Soome dos_unmount(DOS_FS *fs) 2304a5d661aSToomas Soome { 2314a5d661aSToomas Soome int err; 2324a5d661aSToomas Soome 2334a5d661aSToomas Soome if (fs->links) 2344a5d661aSToomas Soome return (EBUSY); 2354a5d661aSToomas Soome if ((err = dosunmount(fs))) 2364a5d661aSToomas Soome return (err); 237*976852c7SToomas Soome return (0); 2384a5d661aSToomas Soome } 2394a5d661aSToomas Soome 2404a5d661aSToomas Soome /* 2414a5d661aSToomas Soome * Common code shared by dos_mount() and dos_unmount() 2424a5d661aSToomas Soome */ 2434a5d661aSToomas Soome static int 2444a5d661aSToomas Soome dosunmount(DOS_FS *fs) 2454a5d661aSToomas Soome { 2464a5d661aSToomas Soome free(fs); 2474a5d661aSToomas Soome return (0); 2484a5d661aSToomas Soome } 2494a5d661aSToomas Soome 2504a5d661aSToomas Soome /* 2514a5d661aSToomas Soome * Open DOS file 2524a5d661aSToomas Soome */ 2534a5d661aSToomas Soome static int 2544a5d661aSToomas Soome dos_open(const char *path, struct open_file *fd) 2554a5d661aSToomas Soome { 2564a5d661aSToomas Soome DOS_DE *de; 2574a5d661aSToomas Soome DOS_FILE *f; 2584a5d661aSToomas Soome DOS_FS *fs; 2594a5d661aSToomas Soome u_int size, clus; 2604a5d661aSToomas Soome int err = 0; 2614a5d661aSToomas Soome 2624a5d661aSToomas Soome /* Allocate mount structure, associate with open */ 2634a5d661aSToomas Soome fs = malloc(sizeof(DOS_FS)); 2644a5d661aSToomas Soome 2654a5d661aSToomas Soome if ((err = dos_mount(fs, fd))) 2664a5d661aSToomas Soome goto out; 2674a5d661aSToomas Soome 2684a5d661aSToomas Soome if ((err = namede(fs, path, &de))) 2694a5d661aSToomas Soome goto out; 2704a5d661aSToomas Soome 2714a5d661aSToomas Soome clus = stclus(fs->fatsz, de); 2724a5d661aSToomas Soome size = cv4(de->size); 2734a5d661aSToomas Soome 2744a5d661aSToomas Soome if ((!(de->attr & FA_DIR) && (!clus != !size)) || 2754a5d661aSToomas Soome ((de->attr & FA_DIR) && size) || 2764a5d661aSToomas Soome (clus && !okclus(fs, clus))) { 2774a5d661aSToomas Soome err = EINVAL; 2784a5d661aSToomas Soome goto out; 2794a5d661aSToomas Soome } 2804a5d661aSToomas Soome f = malloc(sizeof(DOS_FILE)); 2814a5d661aSToomas Soome bzero(f, sizeof(DOS_FILE)); 2824a5d661aSToomas Soome f->fs = fs; 2834a5d661aSToomas Soome fs->links++; 2844a5d661aSToomas Soome f->de = *de; 2854a5d661aSToomas Soome fd->f_fsdata = (void *)f; 2864a5d661aSToomas Soome 2874a5d661aSToomas Soome out: 2884a5d661aSToomas Soome return (err); 2894a5d661aSToomas Soome } 2904a5d661aSToomas Soome 2914a5d661aSToomas Soome /* 2924a5d661aSToomas Soome * Read from file 2934a5d661aSToomas Soome */ 2944a5d661aSToomas Soome static int 2954a5d661aSToomas Soome dos_read(struct open_file *fd, void *buf, size_t nbyte, size_t *resid) 2964a5d661aSToomas Soome { 2974a5d661aSToomas Soome off_t size; 2984a5d661aSToomas Soome u_int nb, off, clus, c, cnt, n; 2994a5d661aSToomas Soome DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 3004a5d661aSToomas Soome int err = 0; 3014a5d661aSToomas Soome 3024a5d661aSToomas Soome /* 3034a5d661aSToomas Soome * as ioget() can be called *a lot*, use twiddle here. 3044a5d661aSToomas Soome * also 4 seems to be good value not to slow loading down too much: 3054a5d661aSToomas Soome * with 270MB file (~540k ioget() calls, twiddle can easily waste 4-5sec. 3064a5d661aSToomas Soome */ 3074a5d661aSToomas Soome twiddle(4); 3084a5d661aSToomas Soome nb = (u_int)nbyte; 3094a5d661aSToomas Soome if ((size = fsize(f->fs, &f->de)) == -1) 310*976852c7SToomas Soome return (EINVAL); 3114a5d661aSToomas Soome if (nb > (n = size - f->offset)) 3124a5d661aSToomas Soome nb = n; 3134a5d661aSToomas Soome off = f->offset; 3144a5d661aSToomas Soome if ((clus = stclus(f->fs->fatsz, &f->de))) 3154a5d661aSToomas Soome off &= f->fs->bsize - 1; 3164a5d661aSToomas Soome c = f->c; 3174a5d661aSToomas Soome cnt = nb; 3184a5d661aSToomas Soome while (cnt) { 3194a5d661aSToomas Soome n = 0; 3204a5d661aSToomas Soome if (!c) { 3214a5d661aSToomas Soome if ((c = clus)) 3224a5d661aSToomas Soome n = bytblk(f->fs, f->offset); 3234a5d661aSToomas Soome } else if (!off) 3244a5d661aSToomas Soome n++; 3254a5d661aSToomas Soome while (n--) { 3264a5d661aSToomas Soome if ((err = fatget(f->fs, &c))) 3274a5d661aSToomas Soome goto out; 3284a5d661aSToomas Soome if (!okclus(f->fs, c)) { 3294a5d661aSToomas Soome err = EINVAL; 3304a5d661aSToomas Soome goto out; 3314a5d661aSToomas Soome } 3324a5d661aSToomas Soome } 3334a5d661aSToomas Soome if (!clus || (n = f->fs->bsize - off) > cnt) 3344a5d661aSToomas Soome n = cnt; 3354a5d661aSToomas Soome if ((err = ioread(f->fs, (c ? blkoff(f->fs, c) : 3364a5d661aSToomas Soome secbyt(f->fs->lsndir)) + off, buf, n))) 3374a5d661aSToomas Soome goto out; 3384a5d661aSToomas Soome f->offset += n; 3394a5d661aSToomas Soome f->c = c; 3404a5d661aSToomas Soome off = 0; 3414a5d661aSToomas Soome buf = (char *)buf + n; 3424a5d661aSToomas Soome cnt -= n; 3434a5d661aSToomas Soome } 3444a5d661aSToomas Soome out: 3454a5d661aSToomas Soome if (resid) 3464a5d661aSToomas Soome *resid = nbyte - nb + cnt; 3474a5d661aSToomas Soome return (err); 3484a5d661aSToomas Soome } 3494a5d661aSToomas Soome 3504a5d661aSToomas Soome /* 3514a5d661aSToomas Soome * Reposition within file 3524a5d661aSToomas Soome */ 3534a5d661aSToomas Soome static off_t 3544a5d661aSToomas Soome dos_seek(struct open_file *fd, off_t offset, int whence) 3554a5d661aSToomas Soome { 3564a5d661aSToomas Soome off_t off; 3574a5d661aSToomas Soome u_int size; 3584a5d661aSToomas Soome DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 3594a5d661aSToomas Soome 3604a5d661aSToomas Soome size = cv4(f->de.size); 3614a5d661aSToomas Soome switch (whence) { 3624a5d661aSToomas Soome case SEEK_SET: 3634a5d661aSToomas Soome off = 0; 3644a5d661aSToomas Soome break; 3654a5d661aSToomas Soome case SEEK_CUR: 3664a5d661aSToomas Soome off = f->offset; 3674a5d661aSToomas Soome break; 3684a5d661aSToomas Soome case SEEK_END: 3694a5d661aSToomas Soome off = size; 3704a5d661aSToomas Soome break; 3714a5d661aSToomas Soome default: 3724a5d661aSToomas Soome errno = EINVAL; 3734a5d661aSToomas Soome return (-1); 3744a5d661aSToomas Soome } 3754a5d661aSToomas Soome off += offset; 3764a5d661aSToomas Soome if (off < 0 || off > size) { 3774a5d661aSToomas Soome errno = EINVAL; 3784a5d661aSToomas Soome return (-1); 3794a5d661aSToomas Soome } 3804a5d661aSToomas Soome f->offset = (u_int)off; 3814a5d661aSToomas Soome f->c = 0; 3824a5d661aSToomas Soome return (off); 3834a5d661aSToomas Soome } 3844a5d661aSToomas Soome 3854a5d661aSToomas Soome /* 3864a5d661aSToomas Soome * Close open file 3874a5d661aSToomas Soome */ 3884a5d661aSToomas Soome static int 3894a5d661aSToomas Soome dos_close(struct open_file *fd) 3904a5d661aSToomas Soome { 3914a5d661aSToomas Soome DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 3924a5d661aSToomas Soome DOS_FS *fs = f->fs; 3934a5d661aSToomas Soome 3944a5d661aSToomas Soome f->fs->links--; 3954a5d661aSToomas Soome free(f); 3964a5d661aSToomas Soome dos_unmount(fs); 397*976852c7SToomas Soome return (0); 3984a5d661aSToomas Soome } 3994a5d661aSToomas Soome 4004a5d661aSToomas Soome /* 4014a5d661aSToomas Soome * Return some stat information on a file. 4024a5d661aSToomas Soome */ 4034a5d661aSToomas Soome static int 4044a5d661aSToomas Soome dos_stat(struct open_file *fd, struct stat *sb) 4054a5d661aSToomas Soome { 4064a5d661aSToomas Soome DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 4074a5d661aSToomas Soome 4084a5d661aSToomas Soome /* only important stuff */ 4094a5d661aSToomas Soome sb->st_mode = f->de.attr & FA_DIR ? S_IFDIR | 0555 : S_IFREG | 0444; 4104a5d661aSToomas Soome sb->st_nlink = 1; 4114a5d661aSToomas Soome sb->st_uid = 0; 4124a5d661aSToomas Soome sb->st_gid = 0; 4134a5d661aSToomas Soome if ((sb->st_size = fsize(f->fs, &f->de)) == -1) 414*976852c7SToomas Soome return (EINVAL); 4154a5d661aSToomas Soome return (0); 4164a5d661aSToomas Soome } 4174a5d661aSToomas Soome 4184a5d661aSToomas Soome static int 4194a5d661aSToomas Soome dos_checksum(char *name, char *ext) 4204a5d661aSToomas Soome { 4214a5d661aSToomas Soome int x, i; 4224a5d661aSToomas Soome char buf[11]; 4234a5d661aSToomas Soome 4244a5d661aSToomas Soome bcopy(name, buf, 8); 4254a5d661aSToomas Soome bcopy(ext, buf+8, 3); 4264a5d661aSToomas Soome x = 0; 4274a5d661aSToomas Soome for (i = 0; i < 11; i++) { 4284a5d661aSToomas Soome x = ((x & 1) << 7) | (x >> 1); 4294a5d661aSToomas Soome x += buf[i]; 4304a5d661aSToomas Soome x &= 0xff; 4314a5d661aSToomas Soome } 4324a5d661aSToomas Soome return (x); 4334a5d661aSToomas Soome } 4344a5d661aSToomas Soome 4354a5d661aSToomas Soome static int 4364a5d661aSToomas Soome dos_readdir(struct open_file *fd, struct dirent *d) 4374a5d661aSToomas Soome { 4384a5d661aSToomas Soome /* DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; */ 4394a5d661aSToomas Soome u_char fn[261]; 4404a5d661aSToomas Soome DOS_DIR dd; 4414a5d661aSToomas Soome size_t res; 4424a5d661aSToomas Soome u_int chk, x, xdn; 4434a5d661aSToomas Soome int err; 4444a5d661aSToomas Soome 4454a5d661aSToomas Soome x = chk = 0; 4464a5d661aSToomas Soome while (1) { 4474a5d661aSToomas Soome xdn = x; 4484a5d661aSToomas Soome x = 0; 4494a5d661aSToomas Soome err = dos_read(fd, &dd, sizeof(dd), &res); 4504a5d661aSToomas Soome if (err) 4514a5d661aSToomas Soome return (err); 4524a5d661aSToomas Soome if (res == sizeof(dd)) 4534a5d661aSToomas Soome return (ENOENT); 4544a5d661aSToomas Soome if (dd.de.name[0] == 0) 4554a5d661aSToomas Soome return (ENOENT); 4564a5d661aSToomas Soome 4574a5d661aSToomas Soome /* Skip deleted entries */ 4584a5d661aSToomas Soome if (dd.de.name[0] == 0xe5) 4594a5d661aSToomas Soome continue; 4604a5d661aSToomas Soome 4614a5d661aSToomas Soome /* Check if directory entry is volume label */ 4624a5d661aSToomas Soome if (dd.de.attr & FA_LABEL) { 4634a5d661aSToomas Soome /* 4644a5d661aSToomas Soome * If volume label set, check if the current entry is 4654a5d661aSToomas Soome * extended entry (FA_XDE) for long file names. 4664a5d661aSToomas Soome */ 4674a5d661aSToomas Soome if ((dd.de.attr & FA_MASK) == FA_XDE) { 4684a5d661aSToomas Soome /* 4694a5d661aSToomas Soome * Read through all following extended entries 4704a5d661aSToomas Soome * to get the long file name. 0x40 marks the 4714a5d661aSToomas Soome * last entry containing part of long file name. 4724a5d661aSToomas Soome */ 4734a5d661aSToomas Soome if (dd.xde.seq & 0x40) 4744a5d661aSToomas Soome chk = dd.xde.chk; 4754a5d661aSToomas Soome else if (dd.xde.seq != xdn - 1 || dd.xde.chk != chk) 4764a5d661aSToomas Soome continue; 4774a5d661aSToomas Soome x = dd.xde.seq & ~0x40; 4784a5d661aSToomas Soome if (x < 1 || x > 20) { 4794a5d661aSToomas Soome x = 0; 4804a5d661aSToomas Soome continue; 4814a5d661aSToomas Soome } 4824a5d661aSToomas Soome cp_xdnm(fn, &dd.xde); 4834a5d661aSToomas Soome } else { 4844a5d661aSToomas Soome /* skip only volume label entries */ 4854a5d661aSToomas Soome continue; 4864a5d661aSToomas Soome } 4874a5d661aSToomas Soome } else { 4884a5d661aSToomas Soome if (xdn == 1) { 4894a5d661aSToomas Soome x = dos_checksum(dd.de.name, dd.de.ext); 4904a5d661aSToomas Soome if (x == chk) 4914a5d661aSToomas Soome break; 4924a5d661aSToomas Soome } else { 4934a5d661aSToomas Soome cp_sfn(fn, &dd.de); 4944a5d661aSToomas Soome break; 4954a5d661aSToomas Soome } 4964a5d661aSToomas Soome x = 0; 4974a5d661aSToomas Soome } 4984a5d661aSToomas Soome } 4994a5d661aSToomas Soome 5004a5d661aSToomas Soome d->d_fileno = (dd.de.clus[1] << 8) + dd.de.clus[0]; 5014a5d661aSToomas Soome d->d_reclen = sizeof(*d); 5024a5d661aSToomas Soome d->d_type = (dd.de.attr & FA_DIR) ? DT_DIR : DT_REG; 5034a5d661aSToomas Soome memcpy(d->d_name, fn, sizeof(d->d_name)); 5044a5d661aSToomas Soome return (0); 5054a5d661aSToomas Soome } 5064a5d661aSToomas Soome 5074a5d661aSToomas Soome /* 5084a5d661aSToomas Soome * Parse DOS boot sector 5094a5d661aSToomas Soome */ 5104a5d661aSToomas Soome static int 5114a5d661aSToomas Soome parsebs(DOS_FS *fs, DOS_BS *bs) 5124a5d661aSToomas Soome { 5134a5d661aSToomas Soome u_int sc; 5144a5d661aSToomas Soome 5154a5d661aSToomas Soome if ((bs->jmp[0] != 0x69 && 5164a5d661aSToomas Soome bs->jmp[0] != 0xe9 && 5174a5d661aSToomas Soome (bs->jmp[0] != 0xeb || bs->jmp[2] != 0x90)) || 5184a5d661aSToomas Soome bs->bpb.media < 0xf0) 519*976852c7SToomas Soome return (EINVAL); 5204a5d661aSToomas Soome if (cv2(bs->bpb.secsiz) != SECSIZ) 521*976852c7SToomas Soome return (EINVAL); 5224a5d661aSToomas Soome if (!(fs->spc = bs->bpb.spc) || fs->spc & (fs->spc - 1)) 523*976852c7SToomas Soome return (EINVAL); 5244a5d661aSToomas Soome fs->bsize = secbyt(fs->spc); 5254a5d661aSToomas Soome fs->bshift = ffs(fs->bsize) - 1; 5264a5d661aSToomas Soome if ((fs->spf = cv2(bs->bpb.spf))) { 5274a5d661aSToomas Soome if (bs->bpb.fats != 2) 528*976852c7SToomas Soome return (EINVAL); 5294a5d661aSToomas Soome if (!(fs->dirents = cv2(bs->bpb.dirents))) 530*976852c7SToomas Soome return (EINVAL); 5314a5d661aSToomas Soome } else { 5324a5d661aSToomas Soome if (!(fs->spf = cv4(bs->bpb.lspf))) 533*976852c7SToomas Soome return (EINVAL); 5344a5d661aSToomas Soome if (!bs->bpb.fats || bs->bpb.fats > 16) 535*976852c7SToomas Soome return (EINVAL); 5364a5d661aSToomas Soome if ((fs->rdcl = cv4(bs->bpb.rdcl)) < LOCLUS) 537*976852c7SToomas Soome return (EINVAL); 5384a5d661aSToomas Soome } 5394a5d661aSToomas Soome if (!(fs->lsnfat = cv2(bs->bpb.ressec))) 540*976852c7SToomas Soome return (EINVAL); 5414a5d661aSToomas Soome fs->lsndir = fs->lsnfat + fs->spf * bs->bpb.fats; 5424a5d661aSToomas Soome fs->lsndta = fs->lsndir + entsec(fs->dirents); 5434a5d661aSToomas Soome if (!(sc = cv2(bs->bpb.secs)) && !(sc = cv4(bs->bpb.lsecs))) 544*976852c7SToomas Soome return (EINVAL); 5454a5d661aSToomas Soome if (fs->lsndta > sc) 546*976852c7SToomas Soome return (EINVAL); 5474a5d661aSToomas Soome if ((fs->xclus = secblk(fs, sc - fs->lsndta) + 1) < LOCLUS) 548*976852c7SToomas Soome return (EINVAL); 5494a5d661aSToomas Soome fs->fatsz = fs->dirents ? fs->xclus < 0xff6 ? 12 : 16 : 32; 5504a5d661aSToomas Soome sc = (secbyt(fs->spf) << 1) / (fs->fatsz >> 2) - 1; 5514a5d661aSToomas Soome if (fs->xclus > sc) 5524a5d661aSToomas Soome fs->xclus = sc; 553*976852c7SToomas Soome return (0); 5544a5d661aSToomas Soome } 5554a5d661aSToomas Soome 5564a5d661aSToomas Soome /* 5574a5d661aSToomas Soome * Return directory entry from path 5584a5d661aSToomas Soome */ 5594a5d661aSToomas Soome static int 5604a5d661aSToomas Soome namede(DOS_FS *fs, const char *path, DOS_DE **dep) 5614a5d661aSToomas Soome { 5624a5d661aSToomas Soome char name[256]; 5634a5d661aSToomas Soome DOS_DE *de; 5644a5d661aSToomas Soome char *s; 5654a5d661aSToomas Soome size_t n; 5664a5d661aSToomas Soome int err; 5674a5d661aSToomas Soome 5684a5d661aSToomas Soome err = 0; 5694a5d661aSToomas Soome de = &fs->root; 5704a5d661aSToomas Soome while (*path) { 5714a5d661aSToomas Soome while (*path == '/') 5724a5d661aSToomas Soome path++; 5734a5d661aSToomas Soome if (*path == '\0') 5744a5d661aSToomas Soome break; 5754a5d661aSToomas Soome if (!(s = strchr(path, '/'))) 5764a5d661aSToomas Soome s = strchr(path, 0); 5774a5d661aSToomas Soome if ((n = s - path) > 255) 578*976852c7SToomas Soome return (ENAMETOOLONG); 5794a5d661aSToomas Soome memcpy(name, path, n); 5804a5d661aSToomas Soome name[n] = 0; 5814a5d661aSToomas Soome path = s; 5824a5d661aSToomas Soome if (!(de->attr & FA_DIR)) 583*976852c7SToomas Soome return (ENOTDIR); 5844a5d661aSToomas Soome if ((err = lookup(fs, stclus(fs->fatsz, de), name, &de))) 585*976852c7SToomas Soome return (err); 5864a5d661aSToomas Soome } 5874a5d661aSToomas Soome *dep = de; 588*976852c7SToomas Soome return (0); 5894a5d661aSToomas Soome } 5904a5d661aSToomas Soome 5914a5d661aSToomas Soome /* 5924a5d661aSToomas Soome * Lookup path segment 5934a5d661aSToomas Soome */ 5944a5d661aSToomas Soome static int 5954a5d661aSToomas Soome lookup(DOS_FS *fs, u_int clus, const char *name, DOS_DE **dep) 5964a5d661aSToomas Soome { 5974a5d661aSToomas Soome static DOS_DIR dir[DEPSEC]; 5984a5d661aSToomas Soome u_char lfn[261]; 5994a5d661aSToomas Soome u_char sfn[13]; 6004a5d661aSToomas Soome u_int nsec, lsec, xdn, chk, sec, ent, x; 6014a5d661aSToomas Soome int err, ok; 6024a5d661aSToomas Soome 6034a5d661aSToomas Soome if (!clus) 6044a5d661aSToomas Soome for (ent = 0; ent < 2; ent++) 6054a5d661aSToomas Soome if (!strcasecmp(name, dotstr[ent])) { 6064a5d661aSToomas Soome *dep = dot + ent; 607*976852c7SToomas Soome return (0); 6084a5d661aSToomas Soome } 6094a5d661aSToomas Soome if (!clus && fs->fatsz == 32) 6104a5d661aSToomas Soome clus = fs->rdcl; 6114a5d661aSToomas Soome nsec = !clus ? entsec(fs->dirents) : fs->spc; 6124a5d661aSToomas Soome lsec = 0; 6134a5d661aSToomas Soome xdn = chk = 0; 6144a5d661aSToomas Soome for (;;) { 6154a5d661aSToomas Soome if (!clus && !lsec) 6164a5d661aSToomas Soome lsec = fs->lsndir; 6174a5d661aSToomas Soome else if (okclus(fs, clus)) 6184a5d661aSToomas Soome lsec = blklsn(fs, clus); 6194a5d661aSToomas Soome else 620*976852c7SToomas Soome return (EINVAL); 6214a5d661aSToomas Soome for (sec = 0; sec < nsec; sec++) { 622*976852c7SToomas Soome if ((err = ioget(fs->fd, lsec + sec, dir, secbyt(1)))) 623*976852c7SToomas Soome return (err); 6244a5d661aSToomas Soome for (ent = 0; ent < DEPSEC; ent++) { 6254a5d661aSToomas Soome if (!*dir[ent].de.name) 626*976852c7SToomas Soome return (ENOENT); 6274a5d661aSToomas Soome if (*dir[ent].de.name != 0xe5) { 6284a5d661aSToomas Soome if ((dir[ent].de.attr & FA_MASK) == FA_XDE) { 6294a5d661aSToomas Soome x = dir[ent].xde.seq; 6304a5d661aSToomas Soome if (x & 0x40 || (x + 1 == xdn && 6314a5d661aSToomas Soome dir[ent].xde.chk == chk)) { 6324a5d661aSToomas Soome if (x & 0x40) { 6334a5d661aSToomas Soome chk = dir[ent].xde.chk; 6344a5d661aSToomas Soome x &= ~0x40; 6354a5d661aSToomas Soome } 6364a5d661aSToomas Soome if (x >= 1 && x <= 20) { 6374a5d661aSToomas Soome cp_xdnm(lfn, &dir[ent].xde); 6384a5d661aSToomas Soome xdn = x; 6394a5d661aSToomas Soome continue; 6404a5d661aSToomas Soome } 6414a5d661aSToomas Soome } 6424a5d661aSToomas Soome } else if (!(dir[ent].de.attr & FA_LABEL)) { 6434a5d661aSToomas Soome if ((ok = xdn == 1)) { 6444a5d661aSToomas Soome x = dos_checksum(dir[ent].de.name, dir[ent].de.ext); 6454a5d661aSToomas Soome ok = chk == x && 6464a5d661aSToomas Soome !strcasecmp(name, (const char *)lfn); 6474a5d661aSToomas Soome } 6484a5d661aSToomas Soome if (!ok) { 6494a5d661aSToomas Soome cp_sfn(sfn, &dir[ent].de); 6504a5d661aSToomas Soome ok = !strcasecmp(name, (const char *)sfn); 6514a5d661aSToomas Soome } 6524a5d661aSToomas Soome if (ok) { 6534a5d661aSToomas Soome *dep = &dir[ent].de; 654*976852c7SToomas Soome return (0); 6554a5d661aSToomas Soome } 6564a5d661aSToomas Soome } 6574a5d661aSToomas Soome } 6584a5d661aSToomas Soome xdn = 0; 6594a5d661aSToomas Soome } 6604a5d661aSToomas Soome } 6614a5d661aSToomas Soome if (!clus) 6624a5d661aSToomas Soome break; 6634a5d661aSToomas Soome if ((err = fatget(fs, &clus))) 664*976852c7SToomas Soome return (err); 6654a5d661aSToomas Soome if (fatend(fs->fatsz, clus)) 6664a5d661aSToomas Soome break; 6674a5d661aSToomas Soome } 668*976852c7SToomas Soome return (ENOENT); 6694a5d661aSToomas Soome } 6704a5d661aSToomas Soome 6714a5d661aSToomas Soome /* 6724a5d661aSToomas Soome * Copy name from extended directory entry 6734a5d661aSToomas Soome */ 6744a5d661aSToomas Soome static void 6754a5d661aSToomas Soome cp_xdnm(u_char *lfn, DOS_XDE *xde) 6764a5d661aSToomas Soome { 6774a5d661aSToomas Soome static struct { 6784a5d661aSToomas Soome u_int off; 6794a5d661aSToomas Soome u_int dim; 6804a5d661aSToomas Soome } ix[3] = { 6814a5d661aSToomas Soome {offsetof(DOS_XDE, name1), sizeof(xde->name1) / 2}, 6824a5d661aSToomas Soome {offsetof(DOS_XDE, name2), sizeof(xde->name2) / 2}, 6834a5d661aSToomas Soome {offsetof(DOS_XDE, name3), sizeof(xde->name3) / 2} 6844a5d661aSToomas Soome }; 6854a5d661aSToomas Soome u_char *p; 6864a5d661aSToomas Soome u_int n, x, c; 6874a5d661aSToomas Soome 6884a5d661aSToomas Soome lfn += 13 * ((xde->seq & ~0x40) - 1); 6894a5d661aSToomas Soome for (n = 0; n < 3; n++) 6904a5d661aSToomas Soome for (p = (u_char *)xde + ix[n].off, x = ix[n].dim; x; 6914a5d661aSToomas Soome p += 2, x--) { 6924a5d661aSToomas Soome if ((c = cv2(p)) && (c < 32 || c > 127)) 6934a5d661aSToomas Soome c = '?'; 6944a5d661aSToomas Soome if (!(*lfn++ = c)) 6954a5d661aSToomas Soome return; 6964a5d661aSToomas Soome } 6974a5d661aSToomas Soome if (xde->seq & 0x40) 6984a5d661aSToomas Soome *lfn = 0; 6994a5d661aSToomas Soome } 7004a5d661aSToomas Soome 7014a5d661aSToomas Soome /* 7024a5d661aSToomas Soome * Copy short filename 7034a5d661aSToomas Soome */ 7044a5d661aSToomas Soome static void 7054a5d661aSToomas Soome cp_sfn(u_char *sfn, DOS_DE *de) 7064a5d661aSToomas Soome { 7074a5d661aSToomas Soome u_char *p; 7084a5d661aSToomas Soome int j, i; 7094a5d661aSToomas Soome 7104a5d661aSToomas Soome p = sfn; 7114a5d661aSToomas Soome if (*de->name != ' ') { 7124a5d661aSToomas Soome for (j = 7; de->name[j] == ' '; j--); 7134a5d661aSToomas Soome for (i = 0; i <= j; i++) 7144a5d661aSToomas Soome *p++ = de->name[i]; 7154a5d661aSToomas Soome if (*de->ext != ' ') { 7164a5d661aSToomas Soome *p++ = '.'; 7174a5d661aSToomas Soome for (j = 2; de->ext[j] == ' '; j--); 7184a5d661aSToomas Soome for (i = 0; i <= j; i++) 7194a5d661aSToomas Soome *p++ = de->ext[i]; 7204a5d661aSToomas Soome } 7214a5d661aSToomas Soome } 7224a5d661aSToomas Soome *p = 0; 7234a5d661aSToomas Soome if (*sfn == 5) 7244a5d661aSToomas Soome *sfn = 0xe5; 7254a5d661aSToomas Soome } 7264a5d661aSToomas Soome 7274a5d661aSToomas Soome /* 7284a5d661aSToomas Soome * Return size of file in bytes 7294a5d661aSToomas Soome */ 7304a5d661aSToomas Soome static off_t 7314a5d661aSToomas Soome fsize(DOS_FS *fs, DOS_DE *de) 7324a5d661aSToomas Soome { 7334a5d661aSToomas Soome u_long size; 7344a5d661aSToomas Soome u_int c; 7354a5d661aSToomas Soome int n; 7364a5d661aSToomas Soome 7374a5d661aSToomas Soome if (!(size = cv4(de->size)) && de->attr & FA_DIR) { 7384a5d661aSToomas Soome if (!(c = cv2(de->clus))) 7394a5d661aSToomas Soome size = fs->dirents * sizeof(DOS_DE); 7404a5d661aSToomas Soome else { 7414a5d661aSToomas Soome if ((n = fatcnt(fs, c)) == -1) 742*976852c7SToomas Soome return (n); 7434a5d661aSToomas Soome size = blkbyt(fs, n); 7444a5d661aSToomas Soome } 7454a5d661aSToomas Soome } 746*976852c7SToomas Soome return (size); 7474a5d661aSToomas Soome } 7484a5d661aSToomas Soome 7494a5d661aSToomas Soome /* 7504a5d661aSToomas Soome * Count number of clusters in chain 7514a5d661aSToomas Soome */ 7524a5d661aSToomas Soome static int 7534a5d661aSToomas Soome fatcnt(DOS_FS *fs, u_int c) 7544a5d661aSToomas Soome { 7554a5d661aSToomas Soome int n; 7564a5d661aSToomas Soome 7574a5d661aSToomas Soome for (n = 0; okclus(fs, c); n++) 7584a5d661aSToomas Soome if (fatget(fs, &c)) 759*976852c7SToomas Soome return (-1); 760*976852c7SToomas Soome return (fatend(fs->fatsz, c) ? n : -1); 7614a5d661aSToomas Soome } 7624a5d661aSToomas Soome 7634a5d661aSToomas Soome /* 7644a5d661aSToomas Soome * Get next cluster in cluster chain. Use in core fat cache unless another 7654a5d661aSToomas Soome * device replaced it. 7664a5d661aSToomas Soome */ 7674a5d661aSToomas Soome static int 7684a5d661aSToomas Soome fatget(DOS_FS *fs, u_int *c) 7694a5d661aSToomas Soome { 7704a5d661aSToomas Soome u_char buf[4]; 771*976852c7SToomas Soome u_int x, offset, n, nbyte; 7724a5d661aSToomas Soome struct devdesc *dd = fs->fd->f_devdata; 7734a5d661aSToomas Soome int err = 0; 7744a5d661aSToomas Soome 7754a5d661aSToomas Soome if (fat.unit != dd->d_unit) { 7764a5d661aSToomas Soome /* fat cache was changed to another device, don't use it */ 7774a5d661aSToomas Soome err = ioread(fs, secbyt(fs->lsnfat) + fatoff(fs->fatsz, *c), buf, 7784a5d661aSToomas Soome fs->fatsz != 32 ? 2 : 4); 7794a5d661aSToomas Soome if (err) 7804a5d661aSToomas Soome return (err); 7814a5d661aSToomas Soome } else { 7824a5d661aSToomas Soome offset = fatoff(fs->fatsz, *c); 7834a5d661aSToomas Soome nbyte = fs->fatsz != 32 ? 2 : 4; 7844a5d661aSToomas Soome 785*976852c7SToomas Soome if (offset + nbyte > secbyt(fat.size)) 786*976852c7SToomas Soome return (EINVAL); 787*976852c7SToomas Soome memcpy(buf, fat.buf + offset, nbyte); 7884a5d661aSToomas Soome } 7894a5d661aSToomas Soome 7904a5d661aSToomas Soome x = fs->fatsz != 32 ? cv2(buf) : cv4(buf); 7914a5d661aSToomas Soome *c = fs->fatsz == 12 ? *c & 1 ? x >> 4 : x & 0xfff : x; 7924a5d661aSToomas Soome return (0); 7934a5d661aSToomas Soome } 7944a5d661aSToomas Soome 7954a5d661aSToomas Soome /* 7964a5d661aSToomas Soome * Is cluster an end-of-chain marker? 7974a5d661aSToomas Soome */ 7984a5d661aSToomas Soome static int 7994a5d661aSToomas Soome fatend(u_int sz, u_int c) 8004a5d661aSToomas Soome { 801*976852c7SToomas Soome return (c > (sz == 12 ? 0xff7U : sz == 16 ? 0xfff7U : 0xffffff7)); 8024a5d661aSToomas Soome } 8034a5d661aSToomas Soome 8044a5d661aSToomas Soome /* 8054a5d661aSToomas Soome * Offset-based I/O primitive 8064a5d661aSToomas Soome */ 8074a5d661aSToomas Soome static int 8084a5d661aSToomas Soome ioread(DOS_FS *fs, u_int offset, void *buf, u_int nbyte) 8094a5d661aSToomas Soome { 8104a5d661aSToomas Soome char *s; 8114a5d661aSToomas Soome u_int off, n; 8124a5d661aSToomas Soome int err; 813*976852c7SToomas Soome u_char local_buf[SECSIZ]; 8144a5d661aSToomas Soome 8154a5d661aSToomas Soome s = buf; 8164a5d661aSToomas Soome if ((off = offset & (SECSIZ - 1))) { 8174a5d661aSToomas Soome offset -= off; 8184a5d661aSToomas Soome if ((n = SECSIZ - off) > nbyte) 8194a5d661aSToomas Soome n = nbyte; 820*976852c7SToomas Soome if ((err = ioget(fs->fd, bytsec(offset), local_buf, sizeof(local_buf)))) 821*976852c7SToomas Soome return (err); 822*976852c7SToomas Soome memcpy(s, local_buf + off, n); 8234a5d661aSToomas Soome offset += SECSIZ; 8244a5d661aSToomas Soome s += n; 8254a5d661aSToomas Soome nbyte -= n; 8264a5d661aSToomas Soome } 8274a5d661aSToomas Soome n = nbyte & (SECSIZ - 1); 8284a5d661aSToomas Soome if (nbyte -= n) { 829*976852c7SToomas Soome if ((err = ioget(fs->fd, bytsec(offset), s, nbyte))) 830*976852c7SToomas Soome return (err); 8314a5d661aSToomas Soome offset += nbyte; 8324a5d661aSToomas Soome s += nbyte; 8334a5d661aSToomas Soome } 8344a5d661aSToomas Soome if (n) { 835*976852c7SToomas Soome if ((err = ioget(fs->fd, bytsec(offset), local_buf, sizeof(local_buf)))) 836*976852c7SToomas Soome return (err); 837*976852c7SToomas Soome memcpy(s, local_buf, n); 8384a5d661aSToomas Soome } 839*976852c7SToomas Soome return (0); 8404a5d661aSToomas Soome } 8414a5d661aSToomas Soome 8424a5d661aSToomas Soome /* 8434a5d661aSToomas Soome * Sector-based I/O primitive 8444a5d661aSToomas Soome */ 8454a5d661aSToomas Soome static int 846*976852c7SToomas Soome ioget(struct open_file *fd, daddr_t lsec, void *buf, u_int size) 8474a5d661aSToomas Soome { 848*976852c7SToomas Soome return ((fd->f_dev->dv_strategy)(fd->f_devdata, F_READ, lsec, 8494a5d661aSToomas Soome size, buf, NULL)); 8504a5d661aSToomas Soome } 851