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 304a5d661aSToomas Soome /* 314a5d661aSToomas Soome * Readonly filesystem for Microsoft FAT12/FAT16/FAT32 filesystems, 324a5d661aSToomas Soome * also supports VFAT. 334a5d661aSToomas Soome */ 344a5d661aSToomas Soome 354a5d661aSToomas Soome #include <sys/types.h> 364a5d661aSToomas Soome #include <string.h> 374a5d661aSToomas Soome #include <stddef.h> 384a5d661aSToomas Soome 394a5d661aSToomas Soome #include "stand.h" 404a5d661aSToomas Soome 414a5d661aSToomas Soome #include "dosfs.h" 424a5d661aSToomas Soome 434a5d661aSToomas Soome 444a5d661aSToomas Soome static int dos_open(const char *path, struct open_file *fd); 454a5d661aSToomas Soome static int dos_close(struct open_file *fd); 464a5d661aSToomas Soome static int dos_read(struct open_file *fd, void *buf, size_t size, size_t *resid); 474a5d661aSToomas Soome static off_t dos_seek(struct open_file *fd, off_t offset, int whence); 484a5d661aSToomas Soome static int dos_stat(struct open_file *fd, struct stat *sb); 494a5d661aSToomas Soome static int dos_readdir(struct open_file *fd, struct dirent *d); 504a5d661aSToomas Soome 514a5d661aSToomas Soome struct fs_ops dosfs_fsops = { 524a5d661aSToomas Soome "dosfs", 534a5d661aSToomas Soome dos_open, 544a5d661aSToomas Soome dos_close, 554a5d661aSToomas Soome dos_read, 564a5d661aSToomas Soome null_write, 574a5d661aSToomas Soome dos_seek, 584a5d661aSToomas Soome dos_stat, 594a5d661aSToomas Soome dos_readdir 604a5d661aSToomas Soome }; 614a5d661aSToomas Soome 624a5d661aSToomas Soome #define SECSIZ 512 /* sector size */ 634a5d661aSToomas Soome #define SSHIFT 9 /* SECSIZ shift */ 644a5d661aSToomas Soome #define DEPSEC 16 /* directory entries per sector */ 654a5d661aSToomas Soome #define DSHIFT 4 /* DEPSEC shift */ 664a5d661aSToomas Soome #define LOCLUS 2 /* lowest cluster number */ 67*8c8024e2SToomas Soome #define FATBLKSZ 0x20000 /* size of block in the FAT cache buffer */ 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 static int parsebs(DOS_FS *, DOS_BS *); 1364a5d661aSToomas Soome static int namede(DOS_FS *, const char *, DOS_DE **); 1374a5d661aSToomas Soome static int lookup(DOS_FS *, u_int, const char *, DOS_DE **); 1384a5d661aSToomas Soome static void cp_xdnm(u_char *, DOS_XDE *); 1394a5d661aSToomas Soome static void cp_sfn(u_char *, DOS_DE *); 1404a5d661aSToomas Soome static off_t fsize(DOS_FS *, DOS_DE *); 1414a5d661aSToomas Soome static int fatcnt(DOS_FS *, u_int); 1424a5d661aSToomas Soome static int fatget(DOS_FS *, u_int *); 1434a5d661aSToomas Soome static int fatend(u_int, u_int); 144*8c8024e2SToomas Soome static int ioread(DOS_FS *, u_int, void *, size_t); 145*8c8024e2SToomas Soome static int ioget(struct open_file *, daddr_t, void *, size_t); 1464a5d661aSToomas Soome 147*8c8024e2SToomas Soome static int 148*8c8024e2SToomas Soome dos_read_fatblk(DOS_FS *fs, struct open_file *fd, u_int blknum) 1494a5d661aSToomas Soome { 150*8c8024e2SToomas Soome int err; 151*8c8024e2SToomas Soome size_t io_size; 152*8c8024e2SToomas Soome daddr_t offset_in_fat, max_offset_in_fat; 1534a5d661aSToomas Soome 154*8c8024e2SToomas Soome offset_in_fat = ((daddr_t)blknum) * FATBLKSZ; 155*8c8024e2SToomas Soome max_offset_in_fat = secbyt(fs->spf); 156*8c8024e2SToomas Soome io_size = FATBLKSZ; 157*8c8024e2SToomas Soome if (offset_in_fat > max_offset_in_fat) 158*8c8024e2SToomas Soome offset_in_fat = max_offset_in_fat; 159*8c8024e2SToomas Soome if (offset_in_fat + io_size > max_offset_in_fat) 160*8c8024e2SToomas Soome io_size = ((size_t)(max_offset_in_fat - offset_in_fat)); 161*8c8024e2SToomas Soome 162*8c8024e2SToomas Soome if (io_size != 0) { 163*8c8024e2SToomas Soome err = ioget(fd, fs->lsnfat + bytsec(offset_in_fat), 164*8c8024e2SToomas Soome fs->fatbuf, io_size); 165*8c8024e2SToomas Soome if (err != 0) { 166*8c8024e2SToomas Soome fs->fatbuf_blknum = ((u_int)(-1)); 167*8c8024e2SToomas Soome return (err); 1684a5d661aSToomas Soome } 1694a5d661aSToomas Soome } 1704a5d661aSToomas Soome 171*8c8024e2SToomas Soome if (io_size < FATBLKSZ) 172*8c8024e2SToomas Soome memset(fs->fatbuf + io_size, 0, FATBLKSZ - io_size); 1734a5d661aSToomas Soome 174*8c8024e2SToomas Soome fs->fatbuf_blknum = blknum; 175*8c8024e2SToomas Soome return (0); 1764a5d661aSToomas Soome } 1774a5d661aSToomas Soome 1784a5d661aSToomas Soome /* 1794a5d661aSToomas Soome * Mount DOS filesystem 1804a5d661aSToomas Soome */ 1814a5d661aSToomas Soome static int 1824a5d661aSToomas Soome dos_mount(DOS_FS *fs, struct open_file *fd) 1834a5d661aSToomas Soome { 1844a5d661aSToomas Soome int err; 1854a5d661aSToomas Soome u_char *buf; 1864a5d661aSToomas Soome 1874a5d661aSToomas Soome bzero(fs, sizeof(DOS_FS)); 1884a5d661aSToomas Soome fs->fd = fd; 1894a5d661aSToomas Soome 190*8c8024e2SToomas Soome if ((buf = malloc(secbyt(1))) == NULL) 191*8c8024e2SToomas Soome return (errno); 192*8c8024e2SToomas Soome if ((err = ioget(fs->fd, 0, buf, secbyt(1))) || 1934a5d661aSToomas Soome (err = parsebs(fs, (DOS_BS *)buf))) { 1944a5d661aSToomas Soome free(buf); 1954a5d661aSToomas Soome return (err); 1964a5d661aSToomas Soome } 1974a5d661aSToomas Soome free(buf); 1984a5d661aSToomas Soome 199*8c8024e2SToomas Soome if ((fs->fatbuf = malloc(FATBLKSZ)) == NULL) 200*8c8024e2SToomas Soome return (errno); 201*8c8024e2SToomas Soome err = dos_read_fatblk(fs, fd, 0); 202*8c8024e2SToomas Soome if (err != 0) { 203*8c8024e2SToomas Soome free(fs->fatbuf); 204*8c8024e2SToomas Soome return (err); 205*8c8024e2SToomas Soome } 2064a5d661aSToomas Soome 2074a5d661aSToomas Soome fs->root = dot[0]; 2084a5d661aSToomas Soome fs->root.name[0] = ' '; 2094a5d661aSToomas Soome if (fs->fatsz == 32) { 2104a5d661aSToomas Soome fs->root.clus[0] = fs->rdcl & 0xff; 2114a5d661aSToomas Soome fs->root.clus[1] = (fs->rdcl >> 8) & 0xff; 2124a5d661aSToomas Soome fs->root.dex.h_clus[0] = (fs->rdcl >> 16) & 0xff; 2134a5d661aSToomas Soome fs->root.dex.h_clus[1] = (fs->rdcl >> 24) & 0xff; 2144a5d661aSToomas Soome } 215976852c7SToomas Soome return (0); 2164a5d661aSToomas Soome } 2174a5d661aSToomas Soome 2184a5d661aSToomas Soome /* 2194a5d661aSToomas Soome * Unmount mounted filesystem 2204a5d661aSToomas Soome */ 2214a5d661aSToomas Soome static int 2224a5d661aSToomas Soome dos_unmount(DOS_FS *fs) 2234a5d661aSToomas Soome { 2244a5d661aSToomas Soome if (fs->links) 2254a5d661aSToomas Soome return (EBUSY); 226*8c8024e2SToomas Soome free(fs->fatbuf); 2274a5d661aSToomas Soome free(fs); 2284a5d661aSToomas Soome return (0); 2294a5d661aSToomas Soome } 2304a5d661aSToomas Soome 2314a5d661aSToomas Soome /* 2324a5d661aSToomas Soome * Open DOS file 2334a5d661aSToomas Soome */ 2344a5d661aSToomas Soome static int 2354a5d661aSToomas Soome dos_open(const char *path, struct open_file *fd) 2364a5d661aSToomas Soome { 2374a5d661aSToomas Soome DOS_DE *de; 2384a5d661aSToomas Soome DOS_FILE *f; 2394a5d661aSToomas Soome DOS_FS *fs; 2404a5d661aSToomas Soome u_int size, clus; 241*8c8024e2SToomas Soome int err; 2424a5d661aSToomas Soome 2434a5d661aSToomas Soome /* Allocate mount structure, associate with open */ 244*8c8024e2SToomas Soome if ((fs = malloc(sizeof(DOS_FS))) == NULL) 245*8c8024e2SToomas Soome return (errno); 246*8c8024e2SToomas Soome if ((err = dos_mount(fs, fd))) { 247*8c8024e2SToomas Soome free(fs); 248*8c8024e2SToomas Soome return (err); 249*8c8024e2SToomas Soome } 2504a5d661aSToomas Soome 251*8c8024e2SToomas Soome if ((err = namede(fs, path, &de))) { 252*8c8024e2SToomas Soome dos_unmount(fs); 253*8c8024e2SToomas Soome return (err); 254*8c8024e2SToomas Soome } 2554a5d661aSToomas Soome 2564a5d661aSToomas Soome clus = stclus(fs->fatsz, de); 2574a5d661aSToomas Soome size = cv4(de->size); 2584a5d661aSToomas Soome 2594a5d661aSToomas Soome if ((!(de->attr & FA_DIR) && (!clus != !size)) || 2604a5d661aSToomas Soome ((de->attr & FA_DIR) && size) || 2614a5d661aSToomas Soome (clus && !okclus(fs, clus))) { 262*8c8024e2SToomas Soome dos_unmount(fs); 263*8c8024e2SToomas Soome return (EINVAL); 2644a5d661aSToomas Soome } 265*8c8024e2SToomas Soome if ((f = malloc(sizeof(DOS_FILE))) == NULL) { 266*8c8024e2SToomas Soome err = errno; 267*8c8024e2SToomas Soome dos_unmount(fs); 268*8c8024e2SToomas Soome return (err); 269*8c8024e2SToomas Soome } 2704a5d661aSToomas Soome bzero(f, sizeof(DOS_FILE)); 2714a5d661aSToomas Soome f->fs = fs; 2724a5d661aSToomas Soome fs->links++; 2734a5d661aSToomas Soome f->de = *de; 2744a5d661aSToomas Soome fd->f_fsdata = (void *)f; 275*8c8024e2SToomas Soome return (0); 2764a5d661aSToomas Soome } 2774a5d661aSToomas Soome 2784a5d661aSToomas Soome /* 2794a5d661aSToomas Soome * Read from file 2804a5d661aSToomas Soome */ 2814a5d661aSToomas Soome static int 2824a5d661aSToomas Soome dos_read(struct open_file *fd, void *buf, size_t nbyte, size_t *resid) 2834a5d661aSToomas Soome { 2844a5d661aSToomas Soome off_t size; 2854a5d661aSToomas Soome u_int nb, off, clus, c, cnt, n; 2864a5d661aSToomas Soome DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 2874a5d661aSToomas Soome int err = 0; 2884a5d661aSToomas Soome 2894a5d661aSToomas Soome /* 2904a5d661aSToomas Soome * as ioget() can be called *a lot*, use twiddle here. 2914a5d661aSToomas Soome * also 4 seems to be good value not to slow loading down too much: 2924a5d661aSToomas Soome * with 270MB file (~540k ioget() calls, twiddle can easily waste 4-5sec. 2934a5d661aSToomas Soome */ 2944a5d661aSToomas Soome twiddle(4); 2954a5d661aSToomas Soome nb = (u_int)nbyte; 2964a5d661aSToomas Soome if ((size = fsize(f->fs, &f->de)) == -1) 297976852c7SToomas Soome return (EINVAL); 2984a5d661aSToomas Soome if (nb > (n = size - f->offset)) 2994a5d661aSToomas Soome nb = n; 3004a5d661aSToomas Soome off = f->offset; 3014a5d661aSToomas Soome if ((clus = stclus(f->fs->fatsz, &f->de))) 3024a5d661aSToomas Soome off &= f->fs->bsize - 1; 3034a5d661aSToomas Soome c = f->c; 3044a5d661aSToomas Soome cnt = nb; 3054a5d661aSToomas Soome while (cnt) { 3064a5d661aSToomas Soome n = 0; 3074a5d661aSToomas Soome if (!c) { 3084a5d661aSToomas Soome if ((c = clus)) 3094a5d661aSToomas Soome n = bytblk(f->fs, f->offset); 3104a5d661aSToomas Soome } else if (!off) 3114a5d661aSToomas Soome n++; 3124a5d661aSToomas Soome while (n--) { 3134a5d661aSToomas Soome if ((err = fatget(f->fs, &c))) 3144a5d661aSToomas Soome goto out; 3154a5d661aSToomas Soome if (!okclus(f->fs, c)) { 3164a5d661aSToomas Soome err = EINVAL; 3174a5d661aSToomas Soome goto out; 3184a5d661aSToomas Soome } 3194a5d661aSToomas Soome } 3204a5d661aSToomas Soome if (!clus || (n = f->fs->bsize - off) > cnt) 3214a5d661aSToomas Soome n = cnt; 3224a5d661aSToomas Soome if ((err = ioread(f->fs, (c ? blkoff(f->fs, c) : 3234a5d661aSToomas Soome secbyt(f->fs->lsndir)) + off, buf, n))) 3244a5d661aSToomas Soome goto out; 3254a5d661aSToomas Soome f->offset += n; 3264a5d661aSToomas Soome f->c = c; 3274a5d661aSToomas Soome off = 0; 3284a5d661aSToomas Soome buf = (char *)buf + n; 3294a5d661aSToomas Soome cnt -= n; 3304a5d661aSToomas Soome } 3314a5d661aSToomas Soome out: 3324a5d661aSToomas Soome if (resid) 3334a5d661aSToomas Soome *resid = nbyte - nb + cnt; 3344a5d661aSToomas Soome return (err); 3354a5d661aSToomas Soome } 3364a5d661aSToomas Soome 3374a5d661aSToomas Soome /* 3384a5d661aSToomas Soome * Reposition within file 3394a5d661aSToomas Soome */ 3404a5d661aSToomas Soome static off_t 3414a5d661aSToomas Soome dos_seek(struct open_file *fd, off_t offset, int whence) 3424a5d661aSToomas Soome { 3434a5d661aSToomas Soome off_t off; 3444a5d661aSToomas Soome u_int size; 3454a5d661aSToomas Soome DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 3464a5d661aSToomas Soome 3474a5d661aSToomas Soome size = cv4(f->de.size); 3484a5d661aSToomas Soome switch (whence) { 3494a5d661aSToomas Soome case SEEK_SET: 3504a5d661aSToomas Soome off = 0; 3514a5d661aSToomas Soome break; 3524a5d661aSToomas Soome case SEEK_CUR: 3534a5d661aSToomas Soome off = f->offset; 3544a5d661aSToomas Soome break; 3554a5d661aSToomas Soome case SEEK_END: 3564a5d661aSToomas Soome off = size; 3574a5d661aSToomas Soome break; 3584a5d661aSToomas Soome default: 3594a5d661aSToomas Soome errno = EINVAL; 3604a5d661aSToomas Soome return (-1); 3614a5d661aSToomas Soome } 3624a5d661aSToomas Soome off += offset; 3634a5d661aSToomas Soome if (off < 0 || off > size) { 3644a5d661aSToomas Soome errno = EINVAL; 3654a5d661aSToomas Soome return (-1); 3664a5d661aSToomas Soome } 3674a5d661aSToomas Soome f->offset = (u_int)off; 3684a5d661aSToomas Soome f->c = 0; 3694a5d661aSToomas Soome return (off); 3704a5d661aSToomas Soome } 3714a5d661aSToomas Soome 3724a5d661aSToomas Soome /* 3734a5d661aSToomas Soome * Close open file 3744a5d661aSToomas Soome */ 3754a5d661aSToomas Soome static int 3764a5d661aSToomas Soome dos_close(struct open_file *fd) 3774a5d661aSToomas Soome { 3784a5d661aSToomas Soome DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 3794a5d661aSToomas Soome DOS_FS *fs = f->fs; 3804a5d661aSToomas Soome 3814a5d661aSToomas Soome f->fs->links--; 3824a5d661aSToomas Soome free(f); 3834a5d661aSToomas Soome dos_unmount(fs); 384976852c7SToomas Soome return (0); 3854a5d661aSToomas Soome } 3864a5d661aSToomas Soome 3874a5d661aSToomas Soome /* 3884a5d661aSToomas Soome * Return some stat information on a file. 3894a5d661aSToomas Soome */ 3904a5d661aSToomas Soome static int 3914a5d661aSToomas Soome dos_stat(struct open_file *fd, struct stat *sb) 3924a5d661aSToomas Soome { 3934a5d661aSToomas Soome DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 3944a5d661aSToomas Soome 3954a5d661aSToomas Soome /* only important stuff */ 3964a5d661aSToomas Soome sb->st_mode = f->de.attr & FA_DIR ? S_IFDIR | 0555 : S_IFREG | 0444; 3974a5d661aSToomas Soome sb->st_nlink = 1; 3984a5d661aSToomas Soome sb->st_uid = 0; 3994a5d661aSToomas Soome sb->st_gid = 0; 4004a5d661aSToomas Soome if ((sb->st_size = fsize(f->fs, &f->de)) == -1) 401976852c7SToomas Soome return (EINVAL); 4024a5d661aSToomas Soome return (0); 4034a5d661aSToomas Soome } 4044a5d661aSToomas Soome 4054a5d661aSToomas Soome static int 4064a5d661aSToomas Soome dos_checksum(char *name, char *ext) 4074a5d661aSToomas Soome { 4084a5d661aSToomas Soome int x, i; 4094a5d661aSToomas Soome char buf[11]; 4104a5d661aSToomas Soome 4114a5d661aSToomas Soome bcopy(name, buf, 8); 4124a5d661aSToomas Soome bcopy(ext, buf+8, 3); 4134a5d661aSToomas Soome x = 0; 4144a5d661aSToomas Soome for (i = 0; i < 11; i++) { 4154a5d661aSToomas Soome x = ((x & 1) << 7) | (x >> 1); 4164a5d661aSToomas Soome x += buf[i]; 4174a5d661aSToomas Soome x &= 0xff; 4184a5d661aSToomas Soome } 4194a5d661aSToomas Soome return (x); 4204a5d661aSToomas Soome } 4214a5d661aSToomas Soome 4224a5d661aSToomas Soome static int 4234a5d661aSToomas Soome dos_readdir(struct open_file *fd, struct dirent *d) 4244a5d661aSToomas Soome { 4254a5d661aSToomas Soome /* DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; */ 4264a5d661aSToomas Soome u_char fn[261]; 4274a5d661aSToomas Soome DOS_DIR dd; 4284a5d661aSToomas Soome size_t res; 4294a5d661aSToomas Soome u_int chk, x, xdn; 4304a5d661aSToomas Soome int err; 4314a5d661aSToomas Soome 4324a5d661aSToomas Soome x = chk = 0; 4334a5d661aSToomas Soome while (1) { 4344a5d661aSToomas Soome xdn = x; 4354a5d661aSToomas Soome x = 0; 4364a5d661aSToomas Soome err = dos_read(fd, &dd, sizeof(dd), &res); 4374a5d661aSToomas Soome if (err) 4384a5d661aSToomas Soome return (err); 4394a5d661aSToomas Soome if (res == sizeof(dd)) 4404a5d661aSToomas Soome return (ENOENT); 4414a5d661aSToomas Soome if (dd.de.name[0] == 0) 4424a5d661aSToomas Soome return (ENOENT); 4434a5d661aSToomas Soome 4444a5d661aSToomas Soome /* Skip deleted entries */ 4454a5d661aSToomas Soome if (dd.de.name[0] == 0xe5) 4464a5d661aSToomas Soome continue; 4474a5d661aSToomas Soome 4484a5d661aSToomas Soome /* Check if directory entry is volume label */ 4494a5d661aSToomas Soome if (dd.de.attr & FA_LABEL) { 4504a5d661aSToomas Soome /* 4514a5d661aSToomas Soome * If volume label set, check if the current entry is 4524a5d661aSToomas Soome * extended entry (FA_XDE) for long file names. 4534a5d661aSToomas Soome */ 4544a5d661aSToomas Soome if ((dd.de.attr & FA_MASK) == FA_XDE) { 4554a5d661aSToomas Soome /* 4564a5d661aSToomas Soome * Read through all following extended entries 4574a5d661aSToomas Soome * to get the long file name. 0x40 marks the 4584a5d661aSToomas Soome * last entry containing part of long file name. 4594a5d661aSToomas Soome */ 4604a5d661aSToomas Soome if (dd.xde.seq & 0x40) 4614a5d661aSToomas Soome chk = dd.xde.chk; 4624a5d661aSToomas Soome else if (dd.xde.seq != xdn - 1 || dd.xde.chk != chk) 4634a5d661aSToomas Soome continue; 4644a5d661aSToomas Soome x = dd.xde.seq & ~0x40; 4654a5d661aSToomas Soome if (x < 1 || x > 20) { 4664a5d661aSToomas Soome x = 0; 4674a5d661aSToomas Soome continue; 4684a5d661aSToomas Soome } 4694a5d661aSToomas Soome cp_xdnm(fn, &dd.xde); 4704a5d661aSToomas Soome } else { 4714a5d661aSToomas Soome /* skip only volume label entries */ 4724a5d661aSToomas Soome continue; 4734a5d661aSToomas Soome } 4744a5d661aSToomas Soome } else { 4754a5d661aSToomas Soome if (xdn == 1) { 4764a5d661aSToomas Soome x = dos_checksum(dd.de.name, dd.de.ext); 4774a5d661aSToomas Soome if (x == chk) 4784a5d661aSToomas Soome break; 4794a5d661aSToomas Soome } else { 4804a5d661aSToomas Soome cp_sfn(fn, &dd.de); 4814a5d661aSToomas Soome break; 4824a5d661aSToomas Soome } 4834a5d661aSToomas Soome x = 0; 4844a5d661aSToomas Soome } 4854a5d661aSToomas Soome } 4864a5d661aSToomas Soome 4874a5d661aSToomas Soome d->d_fileno = (dd.de.clus[1] << 8) + dd.de.clus[0]; 4884a5d661aSToomas Soome d->d_reclen = sizeof(*d); 4894a5d661aSToomas Soome d->d_type = (dd.de.attr & FA_DIR) ? DT_DIR : DT_REG; 4904a5d661aSToomas Soome memcpy(d->d_name, fn, sizeof(d->d_name)); 4914a5d661aSToomas Soome return (0); 4924a5d661aSToomas Soome } 4934a5d661aSToomas Soome 4944a5d661aSToomas Soome /* 4954a5d661aSToomas Soome * Parse DOS boot sector 4964a5d661aSToomas Soome */ 4974a5d661aSToomas Soome static int 4984a5d661aSToomas Soome parsebs(DOS_FS *fs, DOS_BS *bs) 4994a5d661aSToomas Soome { 5004a5d661aSToomas Soome u_int sc; 5014a5d661aSToomas Soome 5024a5d661aSToomas Soome if ((bs->jmp[0] != 0x69 && 5034a5d661aSToomas Soome bs->jmp[0] != 0xe9 && 5044a5d661aSToomas Soome (bs->jmp[0] != 0xeb || bs->jmp[2] != 0x90)) || 5054a5d661aSToomas Soome bs->bpb.media < 0xf0) 506976852c7SToomas Soome return (EINVAL); 5074a5d661aSToomas Soome if (cv2(bs->bpb.secsiz) != SECSIZ) 508976852c7SToomas Soome return (EINVAL); 5094a5d661aSToomas Soome if (!(fs->spc = bs->bpb.spc) || fs->spc & (fs->spc - 1)) 510976852c7SToomas Soome return (EINVAL); 5114a5d661aSToomas Soome fs->bsize = secbyt(fs->spc); 5124a5d661aSToomas Soome fs->bshift = ffs(fs->bsize) - 1; 5134a5d661aSToomas Soome if ((fs->spf = cv2(bs->bpb.spf))) { 5144a5d661aSToomas Soome if (bs->bpb.fats != 2) 515976852c7SToomas Soome return (EINVAL); 5164a5d661aSToomas Soome if (!(fs->dirents = cv2(bs->bpb.dirents))) 517976852c7SToomas Soome return (EINVAL); 5184a5d661aSToomas Soome } else { 5194a5d661aSToomas Soome if (!(fs->spf = cv4(bs->bpb.lspf))) 520976852c7SToomas Soome return (EINVAL); 5214a5d661aSToomas Soome if (!bs->bpb.fats || bs->bpb.fats > 16) 522976852c7SToomas Soome return (EINVAL); 5234a5d661aSToomas Soome if ((fs->rdcl = cv4(bs->bpb.rdcl)) < LOCLUS) 524976852c7SToomas Soome return (EINVAL); 5254a5d661aSToomas Soome } 5264a5d661aSToomas Soome if (!(fs->lsnfat = cv2(bs->bpb.ressec))) 527976852c7SToomas Soome return (EINVAL); 5284a5d661aSToomas Soome fs->lsndir = fs->lsnfat + fs->spf * bs->bpb.fats; 5294a5d661aSToomas Soome fs->lsndta = fs->lsndir + entsec(fs->dirents); 5304a5d661aSToomas Soome if (!(sc = cv2(bs->bpb.secs)) && !(sc = cv4(bs->bpb.lsecs))) 531976852c7SToomas Soome return (EINVAL); 5324a5d661aSToomas Soome if (fs->lsndta > sc) 533976852c7SToomas Soome return (EINVAL); 5344a5d661aSToomas Soome if ((fs->xclus = secblk(fs, sc - fs->lsndta) + 1) < LOCLUS) 535976852c7SToomas Soome return (EINVAL); 5364a5d661aSToomas Soome fs->fatsz = fs->dirents ? fs->xclus < 0xff6 ? 12 : 16 : 32; 5374a5d661aSToomas Soome sc = (secbyt(fs->spf) << 1) / (fs->fatsz >> 2) - 1; 5384a5d661aSToomas Soome if (fs->xclus > sc) 5394a5d661aSToomas Soome fs->xclus = sc; 540976852c7SToomas Soome return (0); 5414a5d661aSToomas Soome } 5424a5d661aSToomas Soome 5434a5d661aSToomas Soome /* 5444a5d661aSToomas Soome * Return directory entry from path 5454a5d661aSToomas Soome */ 5464a5d661aSToomas Soome static int 5474a5d661aSToomas Soome namede(DOS_FS *fs, const char *path, DOS_DE **dep) 5484a5d661aSToomas Soome { 5494a5d661aSToomas Soome char name[256]; 5504a5d661aSToomas Soome DOS_DE *de; 5514a5d661aSToomas Soome char *s; 5524a5d661aSToomas Soome size_t n; 5534a5d661aSToomas Soome int err; 5544a5d661aSToomas Soome 5554a5d661aSToomas Soome err = 0; 5564a5d661aSToomas Soome de = &fs->root; 5574a5d661aSToomas Soome while (*path) { 5584a5d661aSToomas Soome while (*path == '/') 5594a5d661aSToomas Soome path++; 5604a5d661aSToomas Soome if (*path == '\0') 5614a5d661aSToomas Soome break; 5624a5d661aSToomas Soome if (!(s = strchr(path, '/'))) 5634a5d661aSToomas Soome s = strchr(path, 0); 5644a5d661aSToomas Soome if ((n = s - path) > 255) 565976852c7SToomas Soome return (ENAMETOOLONG); 5664a5d661aSToomas Soome memcpy(name, path, n); 5674a5d661aSToomas Soome name[n] = 0; 5684a5d661aSToomas Soome path = s; 5694a5d661aSToomas Soome if (!(de->attr & FA_DIR)) 570976852c7SToomas Soome return (ENOTDIR); 5714a5d661aSToomas Soome if ((err = lookup(fs, stclus(fs->fatsz, de), name, &de))) 572976852c7SToomas Soome return (err); 5734a5d661aSToomas Soome } 5744a5d661aSToomas Soome *dep = de; 575976852c7SToomas Soome return (0); 5764a5d661aSToomas Soome } 5774a5d661aSToomas Soome 5784a5d661aSToomas Soome /* 5794a5d661aSToomas Soome * Lookup path segment 5804a5d661aSToomas Soome */ 5814a5d661aSToomas Soome static int 5824a5d661aSToomas Soome lookup(DOS_FS *fs, u_int clus, const char *name, DOS_DE **dep) 5834a5d661aSToomas Soome { 5844a5d661aSToomas Soome static DOS_DIR dir[DEPSEC]; 5854a5d661aSToomas Soome u_char lfn[261]; 5864a5d661aSToomas Soome u_char sfn[13]; 5874a5d661aSToomas Soome u_int nsec, lsec, xdn, chk, sec, ent, x; 5884a5d661aSToomas Soome int err, ok; 5894a5d661aSToomas Soome 5904a5d661aSToomas Soome if (!clus) 5914a5d661aSToomas Soome for (ent = 0; ent < 2; ent++) 5924a5d661aSToomas Soome if (!strcasecmp(name, dotstr[ent])) { 5934a5d661aSToomas Soome *dep = dot + ent; 594976852c7SToomas Soome return (0); 5954a5d661aSToomas Soome } 5964a5d661aSToomas Soome if (!clus && fs->fatsz == 32) 5974a5d661aSToomas Soome clus = fs->rdcl; 5984a5d661aSToomas Soome nsec = !clus ? entsec(fs->dirents) : fs->spc; 5994a5d661aSToomas Soome lsec = 0; 6004a5d661aSToomas Soome xdn = chk = 0; 6014a5d661aSToomas Soome for (;;) { 6024a5d661aSToomas Soome if (!clus && !lsec) 6034a5d661aSToomas Soome lsec = fs->lsndir; 6044a5d661aSToomas Soome else if (okclus(fs, clus)) 6054a5d661aSToomas Soome lsec = blklsn(fs, clus); 6064a5d661aSToomas Soome else 607976852c7SToomas Soome return (EINVAL); 6084a5d661aSToomas Soome for (sec = 0; sec < nsec; sec++) { 609976852c7SToomas Soome if ((err = ioget(fs->fd, lsec + sec, dir, secbyt(1)))) 610976852c7SToomas Soome return (err); 6114a5d661aSToomas Soome for (ent = 0; ent < DEPSEC; ent++) { 6124a5d661aSToomas Soome if (!*dir[ent].de.name) 613976852c7SToomas Soome return (ENOENT); 6144a5d661aSToomas Soome if (*dir[ent].de.name != 0xe5) { 6154a5d661aSToomas Soome if ((dir[ent].de.attr & FA_MASK) == FA_XDE) { 6164a5d661aSToomas Soome x = dir[ent].xde.seq; 6174a5d661aSToomas Soome if (x & 0x40 || (x + 1 == xdn && 6184a5d661aSToomas Soome dir[ent].xde.chk == chk)) { 6194a5d661aSToomas Soome if (x & 0x40) { 6204a5d661aSToomas Soome chk = dir[ent].xde.chk; 6214a5d661aSToomas Soome x &= ~0x40; 6224a5d661aSToomas Soome } 6234a5d661aSToomas Soome if (x >= 1 && x <= 20) { 6244a5d661aSToomas Soome cp_xdnm(lfn, &dir[ent].xde); 6254a5d661aSToomas Soome xdn = x; 6264a5d661aSToomas Soome continue; 6274a5d661aSToomas Soome } 6284a5d661aSToomas Soome } 6294a5d661aSToomas Soome } else if (!(dir[ent].de.attr & FA_LABEL)) { 6304a5d661aSToomas Soome if ((ok = xdn == 1)) { 6314a5d661aSToomas Soome x = dos_checksum(dir[ent].de.name, dir[ent].de.ext); 6324a5d661aSToomas Soome ok = chk == x && 6334a5d661aSToomas Soome !strcasecmp(name, (const char *)lfn); 6344a5d661aSToomas Soome } 6354a5d661aSToomas Soome if (!ok) { 6364a5d661aSToomas Soome cp_sfn(sfn, &dir[ent].de); 6374a5d661aSToomas Soome ok = !strcasecmp(name, (const char *)sfn); 6384a5d661aSToomas Soome } 6394a5d661aSToomas Soome if (ok) { 6404a5d661aSToomas Soome *dep = &dir[ent].de; 641976852c7SToomas Soome return (0); 6424a5d661aSToomas Soome } 6434a5d661aSToomas Soome } 6444a5d661aSToomas Soome } 6454a5d661aSToomas Soome xdn = 0; 6464a5d661aSToomas Soome } 6474a5d661aSToomas Soome } 6484a5d661aSToomas Soome if (!clus) 6494a5d661aSToomas Soome break; 6504a5d661aSToomas Soome if ((err = fatget(fs, &clus))) 651976852c7SToomas Soome return (err); 6524a5d661aSToomas Soome if (fatend(fs->fatsz, clus)) 6534a5d661aSToomas Soome break; 6544a5d661aSToomas Soome } 655976852c7SToomas Soome return (ENOENT); 6564a5d661aSToomas Soome } 6574a5d661aSToomas Soome 6584a5d661aSToomas Soome /* 6594a5d661aSToomas Soome * Copy name from extended directory entry 6604a5d661aSToomas Soome */ 6614a5d661aSToomas Soome static void 6624a5d661aSToomas Soome cp_xdnm(u_char *lfn, DOS_XDE *xde) 6634a5d661aSToomas Soome { 6644a5d661aSToomas Soome static struct { 6654a5d661aSToomas Soome u_int off; 6664a5d661aSToomas Soome u_int dim; 6674a5d661aSToomas Soome } ix[3] = { 6684a5d661aSToomas Soome {offsetof(DOS_XDE, name1), sizeof(xde->name1) / 2}, 6694a5d661aSToomas Soome {offsetof(DOS_XDE, name2), sizeof(xde->name2) / 2}, 6704a5d661aSToomas Soome {offsetof(DOS_XDE, name3), sizeof(xde->name3) / 2} 6714a5d661aSToomas Soome }; 6724a5d661aSToomas Soome u_char *p; 6734a5d661aSToomas Soome u_int n, x, c; 6744a5d661aSToomas Soome 6754a5d661aSToomas Soome lfn += 13 * ((xde->seq & ~0x40) - 1); 6764a5d661aSToomas Soome for (n = 0; n < 3; n++) 6774a5d661aSToomas Soome for (p = (u_char *)xde + ix[n].off, x = ix[n].dim; x; 6784a5d661aSToomas Soome p += 2, x--) { 6794a5d661aSToomas Soome if ((c = cv2(p)) && (c < 32 || c > 127)) 6804a5d661aSToomas Soome c = '?'; 6814a5d661aSToomas Soome if (!(*lfn++ = c)) 6824a5d661aSToomas Soome return; 6834a5d661aSToomas Soome } 6844a5d661aSToomas Soome if (xde->seq & 0x40) 6854a5d661aSToomas Soome *lfn = 0; 6864a5d661aSToomas Soome } 6874a5d661aSToomas Soome 6884a5d661aSToomas Soome /* 6894a5d661aSToomas Soome * Copy short filename 6904a5d661aSToomas Soome */ 6914a5d661aSToomas Soome static void 6924a5d661aSToomas Soome cp_sfn(u_char *sfn, DOS_DE *de) 6934a5d661aSToomas Soome { 6944a5d661aSToomas Soome u_char *p; 6954a5d661aSToomas Soome int j, i; 6964a5d661aSToomas Soome 6974a5d661aSToomas Soome p = sfn; 6984a5d661aSToomas Soome if (*de->name != ' ') { 6994a5d661aSToomas Soome for (j = 7; de->name[j] == ' '; j--); 7004a5d661aSToomas Soome for (i = 0; i <= j; i++) 7014a5d661aSToomas Soome *p++ = de->name[i]; 7024a5d661aSToomas Soome if (*de->ext != ' ') { 7034a5d661aSToomas Soome *p++ = '.'; 7044a5d661aSToomas Soome for (j = 2; de->ext[j] == ' '; j--); 7054a5d661aSToomas Soome for (i = 0; i <= j; i++) 7064a5d661aSToomas Soome *p++ = de->ext[i]; 7074a5d661aSToomas Soome } 7084a5d661aSToomas Soome } 7094a5d661aSToomas Soome *p = 0; 7104a5d661aSToomas Soome if (*sfn == 5) 7114a5d661aSToomas Soome *sfn = 0xe5; 7124a5d661aSToomas Soome } 7134a5d661aSToomas Soome 7144a5d661aSToomas Soome /* 7154a5d661aSToomas Soome * Return size of file in bytes 7164a5d661aSToomas Soome */ 7174a5d661aSToomas Soome static off_t 7184a5d661aSToomas Soome fsize(DOS_FS *fs, DOS_DE *de) 7194a5d661aSToomas Soome { 7204a5d661aSToomas Soome u_long size; 7214a5d661aSToomas Soome u_int c; 7224a5d661aSToomas Soome int n; 7234a5d661aSToomas Soome 7244a5d661aSToomas Soome if (!(size = cv4(de->size)) && de->attr & FA_DIR) { 7254a5d661aSToomas Soome if (!(c = cv2(de->clus))) 7264a5d661aSToomas Soome size = fs->dirents * sizeof(DOS_DE); 7274a5d661aSToomas Soome else { 7284a5d661aSToomas Soome if ((n = fatcnt(fs, c)) == -1) 729976852c7SToomas Soome return (n); 7304a5d661aSToomas Soome size = blkbyt(fs, n); 7314a5d661aSToomas Soome } 7324a5d661aSToomas Soome } 733976852c7SToomas Soome return (size); 7344a5d661aSToomas Soome } 7354a5d661aSToomas Soome 7364a5d661aSToomas Soome /* 7374a5d661aSToomas Soome * Count number of clusters in chain 7384a5d661aSToomas Soome */ 7394a5d661aSToomas Soome static int 7404a5d661aSToomas Soome fatcnt(DOS_FS *fs, u_int c) 7414a5d661aSToomas Soome { 7424a5d661aSToomas Soome int n; 7434a5d661aSToomas Soome 7444a5d661aSToomas Soome for (n = 0; okclus(fs, c); n++) 7454a5d661aSToomas Soome if (fatget(fs, &c)) 746976852c7SToomas Soome return (-1); 747976852c7SToomas Soome return (fatend(fs->fatsz, c) ? n : -1); 7484a5d661aSToomas Soome } 7494a5d661aSToomas Soome 7504a5d661aSToomas Soome /* 751*8c8024e2SToomas Soome * Get next cluster in cluster chain. Use in core fat cache unless 752*8c8024e2SToomas Soome * the number of current 128K block in FAT has changed. 7534a5d661aSToomas Soome */ 7544a5d661aSToomas Soome static int 7554a5d661aSToomas Soome fatget(DOS_FS *fs, u_int *c) 7564a5d661aSToomas Soome { 757*8c8024e2SToomas Soome u_int val_in, val_out, offset, blknum, nbyte; 758*8c8024e2SToomas Soome const u_char *p_entry; 759*8c8024e2SToomas Soome int err; 7604a5d661aSToomas Soome 761*8c8024e2SToomas Soome /* check input value to prevent overflow in fatoff() */ 762*8c8024e2SToomas Soome val_in = *c; 763*8c8024e2SToomas Soome if (val_in & 0xf0000000) 764976852c7SToomas Soome return (EINVAL); 7654a5d661aSToomas Soome 766*8c8024e2SToomas Soome /* ensure that current 128K FAT block is cached */ 767*8c8024e2SToomas Soome offset = fatoff(fs->fatsz, val_in); 768*8c8024e2SToomas Soome nbyte = fs->fatsz != 32 ? 2 : 4; 769*8c8024e2SToomas Soome if (offset + nbyte > secbyt(fs->spf)) 770*8c8024e2SToomas Soome return (EINVAL); 771*8c8024e2SToomas Soome blknum = offset / FATBLKSZ; 772*8c8024e2SToomas Soome offset %= FATBLKSZ; 773*8c8024e2SToomas Soome if (offset + nbyte > FATBLKSZ) 774*8c8024e2SToomas Soome return (EINVAL); 775*8c8024e2SToomas Soome if (blknum != fs->fatbuf_blknum) { 776*8c8024e2SToomas Soome err = dos_read_fatblk(fs, fs->fd, blknum); 777*8c8024e2SToomas Soome if (err != 0) 778*8c8024e2SToomas Soome return (err); 779*8c8024e2SToomas Soome } 780*8c8024e2SToomas Soome p_entry = fs->fatbuf + offset; 781*8c8024e2SToomas Soome 782*8c8024e2SToomas Soome /* extract cluster number from FAT entry */ 783*8c8024e2SToomas Soome switch (fs->fatsz) { 784*8c8024e2SToomas Soome case 32: 785*8c8024e2SToomas Soome val_out = cv4(p_entry); 786*8c8024e2SToomas Soome val_out &= 0x0fffffff; 787*8c8024e2SToomas Soome break; 788*8c8024e2SToomas Soome case 16: 789*8c8024e2SToomas Soome val_out = cv2(p_entry); 790*8c8024e2SToomas Soome break; 791*8c8024e2SToomas Soome case 12: 792*8c8024e2SToomas Soome val_out = cv2(p_entry); 793*8c8024e2SToomas Soome if (val_in & 1) 794*8c8024e2SToomas Soome val_out >>= 4; 795*8c8024e2SToomas Soome else 796*8c8024e2SToomas Soome val_out &= 0xfff; 797*8c8024e2SToomas Soome break; 798*8c8024e2SToomas Soome default: 799*8c8024e2SToomas Soome return (EINVAL); 800*8c8024e2SToomas Soome } 801*8c8024e2SToomas Soome *c = val_out; 8024a5d661aSToomas Soome return (0); 8034a5d661aSToomas Soome } 8044a5d661aSToomas Soome 8054a5d661aSToomas Soome /* 8064a5d661aSToomas Soome * Is cluster an end-of-chain marker? 8074a5d661aSToomas Soome */ 8084a5d661aSToomas Soome static int 8094a5d661aSToomas Soome fatend(u_int sz, u_int c) 8104a5d661aSToomas Soome { 811976852c7SToomas Soome return (c > (sz == 12 ? 0xff7U : sz == 16 ? 0xfff7U : 0xffffff7)); 8124a5d661aSToomas Soome } 8134a5d661aSToomas Soome 8144a5d661aSToomas Soome /* 8154a5d661aSToomas Soome * Offset-based I/O primitive 8164a5d661aSToomas Soome */ 8174a5d661aSToomas Soome static int 818*8c8024e2SToomas Soome ioread(DOS_FS *fs, u_int offset, void *buf, size_t nbyte) 8194a5d661aSToomas Soome { 8204a5d661aSToomas Soome char *s; 8214a5d661aSToomas Soome u_int off, n; 8224a5d661aSToomas Soome int err; 823976852c7SToomas Soome u_char local_buf[SECSIZ]; 8244a5d661aSToomas Soome 8254a5d661aSToomas Soome s = buf; 8264a5d661aSToomas Soome if ((off = offset & (SECSIZ - 1))) { 8274a5d661aSToomas Soome offset -= off; 8284a5d661aSToomas Soome if ((n = SECSIZ - off) > nbyte) 8294a5d661aSToomas Soome n = nbyte; 830976852c7SToomas Soome if ((err = ioget(fs->fd, bytsec(offset), local_buf, sizeof(local_buf)))) 831976852c7SToomas Soome return (err); 832976852c7SToomas Soome memcpy(s, local_buf + off, n); 8334a5d661aSToomas Soome offset += SECSIZ; 8344a5d661aSToomas Soome s += n; 8354a5d661aSToomas Soome nbyte -= n; 8364a5d661aSToomas Soome } 8374a5d661aSToomas Soome n = nbyte & (SECSIZ - 1); 8384a5d661aSToomas Soome if (nbyte -= n) { 839976852c7SToomas Soome if ((err = ioget(fs->fd, bytsec(offset), s, nbyte))) 840976852c7SToomas Soome return (err); 8414a5d661aSToomas Soome offset += nbyte; 8424a5d661aSToomas Soome s += nbyte; 8434a5d661aSToomas Soome } 8444a5d661aSToomas Soome if (n) { 845976852c7SToomas Soome if ((err = ioget(fs->fd, bytsec(offset), local_buf, sizeof(local_buf)))) 846976852c7SToomas Soome return (err); 847976852c7SToomas Soome memcpy(s, local_buf, n); 8484a5d661aSToomas Soome } 849976852c7SToomas Soome return (0); 8504a5d661aSToomas Soome } 8514a5d661aSToomas Soome 8524a5d661aSToomas Soome /* 8534a5d661aSToomas Soome * Sector-based I/O primitive 8544a5d661aSToomas Soome */ 8554a5d661aSToomas Soome static int 856*8c8024e2SToomas Soome ioget(struct open_file *fd, daddr_t lsec, void *buf, size_t size) 8574a5d661aSToomas Soome { 858*8c8024e2SToomas Soome size_t rsize; 859*8c8024e2SToomas Soome int rv; 860*8c8024e2SToomas Soome 861*8c8024e2SToomas Soome /* Make sure we get full read or error. */ 862*8c8024e2SToomas Soome rsize = 0; 863*8c8024e2SToomas Soome rv = (fd->f_dev->dv_strategy)(fd->f_devdata, F_READ, lsec, 864*8c8024e2SToomas Soome size, buf, &rsize); 865*8c8024e2SToomas Soome if ((rv == 0) && (size != rsize)) 866*8c8024e2SToomas Soome rv = EIO; 867*8c8024e2SToomas Soome return (rv); 8684a5d661aSToomas Soome } 869