1*4a5d661aSToomas Soome /* 2*4a5d661aSToomas Soome * Copyright (c) 1996, 1998 Robert Nordier 3*4a5d661aSToomas Soome * All rights reserved. 4*4a5d661aSToomas Soome * 5*4a5d661aSToomas Soome * Redistribution and use in source and binary forms, with or without 6*4a5d661aSToomas Soome * modification, are permitted provided that the following conditions 7*4a5d661aSToomas Soome * are met: 8*4a5d661aSToomas Soome * 1. Redistributions of source code must retain the above copyright 9*4a5d661aSToomas Soome * notice, this list of conditions and the following disclaimer. 10*4a5d661aSToomas Soome * 2. Redistributions in binary form must reproduce the above copyright 11*4a5d661aSToomas Soome * notice, this list of conditions and the following disclaimer in 12*4a5d661aSToomas Soome * the documentation and/or other materials provided with the 13*4a5d661aSToomas Soome * distribution. 14*4a5d661aSToomas Soome * 15*4a5d661aSToomas Soome * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS 16*4a5d661aSToomas Soome * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17*4a5d661aSToomas Soome * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18*4a5d661aSToomas Soome * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY 19*4a5d661aSToomas Soome * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20*4a5d661aSToomas Soome * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 21*4a5d661aSToomas Soome * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22*4a5d661aSToomas Soome * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 23*4a5d661aSToomas Soome * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24*4a5d661aSToomas Soome * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 25*4a5d661aSToomas Soome * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26*4a5d661aSToomas Soome */ 27*4a5d661aSToomas Soome 28*4a5d661aSToomas Soome #include <sys/cdefs.h> 29*4a5d661aSToomas Soome __FBSDID("$FreeBSD$"); 30*4a5d661aSToomas Soome 31*4a5d661aSToomas Soome /* 32*4a5d661aSToomas Soome * Readonly filesystem for Microsoft FAT12/FAT16/FAT32 filesystems, 33*4a5d661aSToomas Soome * also supports VFAT. 34*4a5d661aSToomas Soome */ 35*4a5d661aSToomas Soome 36*4a5d661aSToomas Soome #include <sys/types.h> 37*4a5d661aSToomas Soome #include <string.h> 38*4a5d661aSToomas Soome #include <stddef.h> 39*4a5d661aSToomas Soome 40*4a5d661aSToomas Soome #include "stand.h" 41*4a5d661aSToomas Soome 42*4a5d661aSToomas Soome #include "dosfs.h" 43*4a5d661aSToomas Soome 44*4a5d661aSToomas Soome 45*4a5d661aSToomas Soome static int dos_open(const char *path, struct open_file *fd); 46*4a5d661aSToomas Soome static int dos_close(struct open_file *fd); 47*4a5d661aSToomas Soome static int dos_read(struct open_file *fd, void *buf, size_t size, size_t *resid); 48*4a5d661aSToomas Soome static off_t dos_seek(struct open_file *fd, off_t offset, int whence); 49*4a5d661aSToomas Soome static int dos_stat(struct open_file *fd, struct stat *sb); 50*4a5d661aSToomas Soome static int dos_readdir(struct open_file *fd, struct dirent *d); 51*4a5d661aSToomas Soome 52*4a5d661aSToomas Soome struct fs_ops dosfs_fsops = { 53*4a5d661aSToomas Soome "dosfs", 54*4a5d661aSToomas Soome dos_open, 55*4a5d661aSToomas Soome dos_close, 56*4a5d661aSToomas Soome dos_read, 57*4a5d661aSToomas Soome null_write, 58*4a5d661aSToomas Soome dos_seek, 59*4a5d661aSToomas Soome dos_stat, 60*4a5d661aSToomas Soome dos_readdir 61*4a5d661aSToomas Soome }; 62*4a5d661aSToomas Soome 63*4a5d661aSToomas Soome #define SECSIZ 512 /* sector size */ 64*4a5d661aSToomas Soome #define SSHIFT 9 /* SECSIZ shift */ 65*4a5d661aSToomas Soome #define DEPSEC 16 /* directory entries per sector */ 66*4a5d661aSToomas Soome #define DSHIFT 4 /* DEPSEC shift */ 67*4a5d661aSToomas Soome #define LOCLUS 2 /* lowest cluster number */ 68*4a5d661aSToomas Soome 69*4a5d661aSToomas Soome /* DOS "BIOS Parameter Block" */ 70*4a5d661aSToomas Soome typedef struct { 71*4a5d661aSToomas Soome u_char secsiz[2]; /* sector size */ 72*4a5d661aSToomas Soome u_char spc; /* sectors per cluster */ 73*4a5d661aSToomas Soome u_char ressec[2]; /* reserved sectors */ 74*4a5d661aSToomas Soome u_char fats; /* FATs */ 75*4a5d661aSToomas Soome u_char dirents[2]; /* root directory entries */ 76*4a5d661aSToomas Soome u_char secs[2]; /* total sectors */ 77*4a5d661aSToomas Soome u_char media; /* media descriptor */ 78*4a5d661aSToomas Soome u_char spf[2]; /* sectors per FAT */ 79*4a5d661aSToomas Soome u_char spt[2]; /* sectors per track */ 80*4a5d661aSToomas Soome u_char heads[2]; /* drive heads */ 81*4a5d661aSToomas Soome u_char hidsec[4]; /* hidden sectors */ 82*4a5d661aSToomas Soome u_char lsecs[4]; /* huge sectors */ 83*4a5d661aSToomas Soome u_char lspf[4]; /* huge sectors per FAT */ 84*4a5d661aSToomas Soome u_char xflg[2]; /* flags */ 85*4a5d661aSToomas Soome u_char vers[2]; /* filesystem version */ 86*4a5d661aSToomas Soome u_char rdcl[4]; /* root directory start cluster */ 87*4a5d661aSToomas Soome u_char infs[2]; /* filesystem info sector */ 88*4a5d661aSToomas Soome u_char bkbs[2]; /* backup boot sector */ 89*4a5d661aSToomas Soome } DOS_BPB; 90*4a5d661aSToomas Soome 91*4a5d661aSToomas Soome /* Initial portion of DOS boot sector */ 92*4a5d661aSToomas Soome typedef struct { 93*4a5d661aSToomas Soome u_char jmp[3]; /* usually 80x86 'jmp' opcode */ 94*4a5d661aSToomas Soome u_char oem[8]; /* OEM name and version */ 95*4a5d661aSToomas Soome DOS_BPB bpb; /* BPB */ 96*4a5d661aSToomas Soome } DOS_BS; 97*4a5d661aSToomas Soome 98*4a5d661aSToomas Soome /* Supply missing "." and ".." root directory entries */ 99*4a5d661aSToomas Soome static const char *const dotstr[2] = {".", ".."}; 100*4a5d661aSToomas Soome static DOS_DE dot[2] = { 101*4a5d661aSToomas Soome {". ", " ", FA_DIR, {0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 102*4a5d661aSToomas Soome {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}}, 103*4a5d661aSToomas Soome {".. ", " ", FA_DIR, {0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 104*4a5d661aSToomas Soome {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}} 105*4a5d661aSToomas Soome }; 106*4a5d661aSToomas Soome 107*4a5d661aSToomas Soome /* The usual conversion macros to avoid multiplication and division */ 108*4a5d661aSToomas Soome #define bytsec(n) ((n) >> SSHIFT) 109*4a5d661aSToomas Soome #define secbyt(s) ((s) << SSHIFT) 110*4a5d661aSToomas Soome #define entsec(e) ((e) >> DSHIFT) 111*4a5d661aSToomas Soome #define bytblk(fs, n) ((n) >> (fs)->bshift) 112*4a5d661aSToomas Soome #define blkbyt(fs, b) ((b) << (fs)->bshift) 113*4a5d661aSToomas Soome #define secblk(fs, s) ((s) >> ((fs)->bshift - SSHIFT)) 114*4a5d661aSToomas Soome #define blksec(fs, b) ((b) << ((fs)->bshift - SSHIFT)) 115*4a5d661aSToomas Soome 116*4a5d661aSToomas Soome /* Convert cluster number to offset within filesystem */ 117*4a5d661aSToomas Soome #define blkoff(fs, b) (secbyt((fs)->lsndta) + blkbyt(fs, (b) - LOCLUS)) 118*4a5d661aSToomas Soome 119*4a5d661aSToomas Soome /* Convert cluster number to logical sector number */ 120*4a5d661aSToomas Soome #define blklsn(fs, b) ((fs)->lsndta + blksec(fs, (b) - LOCLUS)) 121*4a5d661aSToomas Soome 122*4a5d661aSToomas Soome /* Convert cluster number to offset within FAT */ 123*4a5d661aSToomas Soome #define fatoff(sz, c) ((sz) == 12 ? (c) + ((c) >> 1) : \ 124*4a5d661aSToomas Soome (sz) == 16 ? (c) << 1 : \ 125*4a5d661aSToomas Soome (c) << 2) 126*4a5d661aSToomas Soome 127*4a5d661aSToomas Soome /* Does cluster number reference a valid data cluster? */ 128*4a5d661aSToomas Soome #define okclus(fs, c) ((c) >= LOCLUS && (c) <= (fs)->xclus) 129*4a5d661aSToomas Soome 130*4a5d661aSToomas Soome /* Get start cluster from directory entry */ 131*4a5d661aSToomas Soome #define stclus(sz, de) ((sz) != 32 ? cv2((de)->clus) : \ 132*4a5d661aSToomas Soome ((u_int)cv2((de)->dex.h_clus) << 16) | \ 133*4a5d661aSToomas Soome cv2((de)->clus)) 134*4a5d661aSToomas Soome 135*4a5d661aSToomas Soome /* 136*4a5d661aSToomas Soome * fat cache metadata 137*4a5d661aSToomas Soome */ 138*4a5d661aSToomas Soome struct fatcache { 139*4a5d661aSToomas Soome int unit; /* disk unit number */ 140*4a5d661aSToomas Soome int size; /* buffer (and fat) size in sectors */ 141*4a5d661aSToomas Soome u_char *buf; 142*4a5d661aSToomas Soome }; 143*4a5d661aSToomas Soome 144*4a5d661aSToomas Soome static struct fatcache fat; 145*4a5d661aSToomas Soome 146*4a5d661aSToomas Soome static int dosunmount(DOS_FS *); 147*4a5d661aSToomas Soome static int parsebs(DOS_FS *, DOS_BS *); 148*4a5d661aSToomas Soome static int namede(DOS_FS *, const char *, DOS_DE **); 149*4a5d661aSToomas Soome static int lookup(DOS_FS *, u_int, const char *, DOS_DE **); 150*4a5d661aSToomas Soome static void cp_xdnm(u_char *, DOS_XDE *); 151*4a5d661aSToomas Soome static void cp_sfn(u_char *, DOS_DE *); 152*4a5d661aSToomas Soome static off_t fsize(DOS_FS *, DOS_DE *); 153*4a5d661aSToomas Soome static int fatcnt(DOS_FS *, u_int); 154*4a5d661aSToomas Soome static int fatget(DOS_FS *, u_int *); 155*4a5d661aSToomas Soome static int fatend(u_int, u_int); 156*4a5d661aSToomas Soome static int ioread(DOS_FS *, u_int, void *, u_int); 157*4a5d661aSToomas Soome static int ioget(struct open_file *, daddr_t, size_t, void *, u_int); 158*4a5d661aSToomas Soome 159*4a5d661aSToomas Soome static void 160*4a5d661aSToomas Soome dos_read_fat(DOS_FS *fs, struct open_file *fd) 161*4a5d661aSToomas Soome { 162*4a5d661aSToomas Soome struct devdesc *dd = fd->f_devdata; 163*4a5d661aSToomas Soome 164*4a5d661aSToomas Soome if (fat.buf != NULL) { /* can we reuse old buffer? */ 165*4a5d661aSToomas Soome if (fat.size != fs->spf) { 166*4a5d661aSToomas Soome free(fat.buf); /* no, free old buffer */ 167*4a5d661aSToomas Soome fat.buf = NULL; 168*4a5d661aSToomas Soome } 169*4a5d661aSToomas Soome } 170*4a5d661aSToomas Soome 171*4a5d661aSToomas Soome if (fat.buf == NULL) 172*4a5d661aSToomas Soome fat.buf = malloc(secbyt(fs->spf)); 173*4a5d661aSToomas Soome 174*4a5d661aSToomas Soome if (fat.buf != NULL) { 175*4a5d661aSToomas Soome if (ioget(fd, fs->lsnfat, 0, fat.buf, secbyt(fs->spf)) == 0) { 176*4a5d661aSToomas Soome fat.size = fs->spf; 177*4a5d661aSToomas Soome fat.unit = dd->d_unit; 178*4a5d661aSToomas Soome return; 179*4a5d661aSToomas Soome } 180*4a5d661aSToomas Soome } 181*4a5d661aSToomas Soome if (fat.buf != NULL) /* got IO error */ 182*4a5d661aSToomas Soome free(fat.buf); 183*4a5d661aSToomas Soome fat.buf = NULL; 184*4a5d661aSToomas Soome fat.unit = -1; /* impossible unit */ 185*4a5d661aSToomas Soome fat.size = 0; 186*4a5d661aSToomas Soome } 187*4a5d661aSToomas Soome 188*4a5d661aSToomas Soome /* 189*4a5d661aSToomas Soome * Mount DOS filesystem 190*4a5d661aSToomas Soome */ 191*4a5d661aSToomas Soome static int 192*4a5d661aSToomas Soome dos_mount(DOS_FS *fs, struct open_file *fd) 193*4a5d661aSToomas Soome { 194*4a5d661aSToomas Soome int err; 195*4a5d661aSToomas Soome struct devdesc *dd = fd->f_devdata; 196*4a5d661aSToomas Soome u_char *buf; 197*4a5d661aSToomas Soome 198*4a5d661aSToomas Soome bzero(fs, sizeof(DOS_FS)); 199*4a5d661aSToomas Soome fs->fd = fd; 200*4a5d661aSToomas Soome 201*4a5d661aSToomas Soome if ((err = !(buf = malloc(secbyt(1))) ? errno : 0) || 202*4a5d661aSToomas Soome (err = ioget(fs->fd, 0, 0, buf, secbyt(1))) || 203*4a5d661aSToomas Soome (err = parsebs(fs, (DOS_BS *)buf))) { 204*4a5d661aSToomas Soome if (buf != NULL) 205*4a5d661aSToomas Soome free(buf); 206*4a5d661aSToomas Soome (void)dosunmount(fs); 207*4a5d661aSToomas Soome return(err); 208*4a5d661aSToomas Soome } 209*4a5d661aSToomas Soome free(buf); 210*4a5d661aSToomas Soome 211*4a5d661aSToomas Soome if (fat.buf == NULL || fat.unit != dd->d_unit) 212*4a5d661aSToomas Soome dos_read_fat(fs, fd); 213*4a5d661aSToomas Soome 214*4a5d661aSToomas Soome fs->root = dot[0]; 215*4a5d661aSToomas Soome fs->root.name[0] = ' '; 216*4a5d661aSToomas Soome if (fs->fatsz == 32) { 217*4a5d661aSToomas Soome fs->root.clus[0] = fs->rdcl & 0xff; 218*4a5d661aSToomas Soome fs->root.clus[1] = (fs->rdcl >> 8) & 0xff; 219*4a5d661aSToomas Soome fs->root.dex.h_clus[0] = (fs->rdcl >> 16) & 0xff; 220*4a5d661aSToomas Soome fs->root.dex.h_clus[1] = (fs->rdcl >> 24) & 0xff; 221*4a5d661aSToomas Soome } 222*4a5d661aSToomas Soome return 0; 223*4a5d661aSToomas Soome } 224*4a5d661aSToomas Soome 225*4a5d661aSToomas Soome /* 226*4a5d661aSToomas Soome * Unmount mounted filesystem 227*4a5d661aSToomas Soome */ 228*4a5d661aSToomas Soome static int 229*4a5d661aSToomas Soome dos_unmount(DOS_FS *fs) 230*4a5d661aSToomas Soome { 231*4a5d661aSToomas Soome int err; 232*4a5d661aSToomas Soome 233*4a5d661aSToomas Soome if (fs->links) 234*4a5d661aSToomas Soome return(EBUSY); 235*4a5d661aSToomas Soome if ((err = dosunmount(fs))) 236*4a5d661aSToomas Soome return(err); 237*4a5d661aSToomas Soome return 0; 238*4a5d661aSToomas Soome } 239*4a5d661aSToomas Soome 240*4a5d661aSToomas Soome /* 241*4a5d661aSToomas Soome * Common code shared by dos_mount() and dos_unmount() 242*4a5d661aSToomas Soome */ 243*4a5d661aSToomas Soome static int 244*4a5d661aSToomas Soome dosunmount(DOS_FS *fs) 245*4a5d661aSToomas Soome { 246*4a5d661aSToomas Soome free(fs); 247*4a5d661aSToomas Soome return(0); 248*4a5d661aSToomas Soome } 249*4a5d661aSToomas Soome 250*4a5d661aSToomas Soome /* 251*4a5d661aSToomas Soome * Open DOS file 252*4a5d661aSToomas Soome */ 253*4a5d661aSToomas Soome static int 254*4a5d661aSToomas Soome dos_open(const char *path, struct open_file *fd) 255*4a5d661aSToomas Soome { 256*4a5d661aSToomas Soome DOS_DE *de; 257*4a5d661aSToomas Soome DOS_FILE *f; 258*4a5d661aSToomas Soome DOS_FS *fs; 259*4a5d661aSToomas Soome u_int size, clus; 260*4a5d661aSToomas Soome int err = 0; 261*4a5d661aSToomas Soome 262*4a5d661aSToomas Soome /* Allocate mount structure, associate with open */ 263*4a5d661aSToomas Soome fs = malloc(sizeof(DOS_FS)); 264*4a5d661aSToomas Soome 265*4a5d661aSToomas Soome if ((err = dos_mount(fs, fd))) 266*4a5d661aSToomas Soome goto out; 267*4a5d661aSToomas Soome 268*4a5d661aSToomas Soome if ((err = namede(fs, path, &de))) 269*4a5d661aSToomas Soome goto out; 270*4a5d661aSToomas Soome 271*4a5d661aSToomas Soome clus = stclus(fs->fatsz, de); 272*4a5d661aSToomas Soome size = cv4(de->size); 273*4a5d661aSToomas Soome 274*4a5d661aSToomas Soome if ((!(de->attr & FA_DIR) && (!clus != !size)) || 275*4a5d661aSToomas Soome ((de->attr & FA_DIR) && size) || 276*4a5d661aSToomas Soome (clus && !okclus(fs, clus))) { 277*4a5d661aSToomas Soome err = EINVAL; 278*4a5d661aSToomas Soome goto out; 279*4a5d661aSToomas Soome } 280*4a5d661aSToomas Soome f = malloc(sizeof(DOS_FILE)); 281*4a5d661aSToomas Soome bzero(f, sizeof(DOS_FILE)); 282*4a5d661aSToomas Soome f->fs = fs; 283*4a5d661aSToomas Soome fs->links++; 284*4a5d661aSToomas Soome f->de = *de; 285*4a5d661aSToomas Soome fd->f_fsdata = (void *)f; 286*4a5d661aSToomas Soome 287*4a5d661aSToomas Soome out: 288*4a5d661aSToomas Soome return(err); 289*4a5d661aSToomas Soome } 290*4a5d661aSToomas Soome 291*4a5d661aSToomas Soome /* 292*4a5d661aSToomas Soome * Read from file 293*4a5d661aSToomas Soome */ 294*4a5d661aSToomas Soome static int 295*4a5d661aSToomas Soome dos_read(struct open_file *fd, void *buf, size_t nbyte, size_t *resid) 296*4a5d661aSToomas Soome { 297*4a5d661aSToomas Soome off_t size; 298*4a5d661aSToomas Soome u_int nb, off, clus, c, cnt, n; 299*4a5d661aSToomas Soome DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 300*4a5d661aSToomas Soome int err = 0; 301*4a5d661aSToomas Soome 302*4a5d661aSToomas Soome /* 303*4a5d661aSToomas Soome * as ioget() can be called *a lot*, use twiddle here. 304*4a5d661aSToomas Soome * also 4 seems to be good value not to slow loading down too much: 305*4a5d661aSToomas Soome * with 270MB file (~540k ioget() calls, twiddle can easily waste 4-5sec. 306*4a5d661aSToomas Soome */ 307*4a5d661aSToomas Soome twiddle(4); 308*4a5d661aSToomas Soome nb = (u_int)nbyte; 309*4a5d661aSToomas Soome if ((size = fsize(f->fs, &f->de)) == -1) 310*4a5d661aSToomas Soome return EINVAL; 311*4a5d661aSToomas Soome if (nb > (n = size - f->offset)) 312*4a5d661aSToomas Soome nb = n; 313*4a5d661aSToomas Soome off = f->offset; 314*4a5d661aSToomas Soome if ((clus = stclus(f->fs->fatsz, &f->de))) 315*4a5d661aSToomas Soome off &= f->fs->bsize - 1; 316*4a5d661aSToomas Soome c = f->c; 317*4a5d661aSToomas Soome cnt = nb; 318*4a5d661aSToomas Soome while (cnt) { 319*4a5d661aSToomas Soome n = 0; 320*4a5d661aSToomas Soome if (!c) { 321*4a5d661aSToomas Soome if ((c = clus)) 322*4a5d661aSToomas Soome n = bytblk(f->fs, f->offset); 323*4a5d661aSToomas Soome } else if (!off) 324*4a5d661aSToomas Soome n++; 325*4a5d661aSToomas Soome while (n--) { 326*4a5d661aSToomas Soome if ((err = fatget(f->fs, &c))) 327*4a5d661aSToomas Soome goto out; 328*4a5d661aSToomas Soome if (!okclus(f->fs, c)) { 329*4a5d661aSToomas Soome err = EINVAL; 330*4a5d661aSToomas Soome goto out; 331*4a5d661aSToomas Soome } 332*4a5d661aSToomas Soome } 333*4a5d661aSToomas Soome if (!clus || (n = f->fs->bsize - off) > cnt) 334*4a5d661aSToomas Soome n = cnt; 335*4a5d661aSToomas Soome if ((err = ioread(f->fs, (c ? blkoff(f->fs, c) : 336*4a5d661aSToomas Soome secbyt(f->fs->lsndir)) + off, buf, n))) 337*4a5d661aSToomas Soome goto out; 338*4a5d661aSToomas Soome f->offset += n; 339*4a5d661aSToomas Soome f->c = c; 340*4a5d661aSToomas Soome off = 0; 341*4a5d661aSToomas Soome buf = (char *)buf + n; 342*4a5d661aSToomas Soome cnt -= n; 343*4a5d661aSToomas Soome } 344*4a5d661aSToomas Soome out: 345*4a5d661aSToomas Soome if (resid) 346*4a5d661aSToomas Soome *resid = nbyte - nb + cnt; 347*4a5d661aSToomas Soome return(err); 348*4a5d661aSToomas Soome } 349*4a5d661aSToomas Soome 350*4a5d661aSToomas Soome /* 351*4a5d661aSToomas Soome * Reposition within file 352*4a5d661aSToomas Soome */ 353*4a5d661aSToomas Soome static off_t 354*4a5d661aSToomas Soome dos_seek(struct open_file *fd, off_t offset, int whence) 355*4a5d661aSToomas Soome { 356*4a5d661aSToomas Soome off_t off; 357*4a5d661aSToomas Soome u_int size; 358*4a5d661aSToomas Soome DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 359*4a5d661aSToomas Soome 360*4a5d661aSToomas Soome size = cv4(f->de.size); 361*4a5d661aSToomas Soome switch (whence) { 362*4a5d661aSToomas Soome case SEEK_SET: 363*4a5d661aSToomas Soome off = 0; 364*4a5d661aSToomas Soome break; 365*4a5d661aSToomas Soome case SEEK_CUR: 366*4a5d661aSToomas Soome off = f->offset; 367*4a5d661aSToomas Soome break; 368*4a5d661aSToomas Soome case SEEK_END: 369*4a5d661aSToomas Soome off = size; 370*4a5d661aSToomas Soome break; 371*4a5d661aSToomas Soome default: 372*4a5d661aSToomas Soome errno = EINVAL; 373*4a5d661aSToomas Soome return(-1); 374*4a5d661aSToomas Soome } 375*4a5d661aSToomas Soome off += offset; 376*4a5d661aSToomas Soome if (off < 0 || off > size) { 377*4a5d661aSToomas Soome errno = EINVAL; 378*4a5d661aSToomas Soome return(-1); 379*4a5d661aSToomas Soome } 380*4a5d661aSToomas Soome f->offset = (u_int)off; 381*4a5d661aSToomas Soome f->c = 0; 382*4a5d661aSToomas Soome return(off); 383*4a5d661aSToomas Soome } 384*4a5d661aSToomas Soome 385*4a5d661aSToomas Soome /* 386*4a5d661aSToomas Soome * Close open file 387*4a5d661aSToomas Soome */ 388*4a5d661aSToomas Soome static int 389*4a5d661aSToomas Soome dos_close(struct open_file *fd) 390*4a5d661aSToomas Soome { 391*4a5d661aSToomas Soome DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 392*4a5d661aSToomas Soome DOS_FS *fs = f->fs; 393*4a5d661aSToomas Soome 394*4a5d661aSToomas Soome f->fs->links--; 395*4a5d661aSToomas Soome free(f); 396*4a5d661aSToomas Soome dos_unmount(fs); 397*4a5d661aSToomas Soome return 0; 398*4a5d661aSToomas Soome } 399*4a5d661aSToomas Soome 400*4a5d661aSToomas Soome /* 401*4a5d661aSToomas Soome * Return some stat information on a file. 402*4a5d661aSToomas Soome */ 403*4a5d661aSToomas Soome static int 404*4a5d661aSToomas Soome dos_stat(struct open_file *fd, struct stat *sb) 405*4a5d661aSToomas Soome { 406*4a5d661aSToomas Soome DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 407*4a5d661aSToomas Soome 408*4a5d661aSToomas Soome /* only important stuff */ 409*4a5d661aSToomas Soome sb->st_mode = f->de.attr & FA_DIR ? S_IFDIR | 0555 : S_IFREG | 0444; 410*4a5d661aSToomas Soome sb->st_nlink = 1; 411*4a5d661aSToomas Soome sb->st_uid = 0; 412*4a5d661aSToomas Soome sb->st_gid = 0; 413*4a5d661aSToomas Soome if ((sb->st_size = fsize(f->fs, &f->de)) == -1) 414*4a5d661aSToomas Soome return EINVAL; 415*4a5d661aSToomas Soome return (0); 416*4a5d661aSToomas Soome } 417*4a5d661aSToomas Soome 418*4a5d661aSToomas Soome static int 419*4a5d661aSToomas Soome dos_checksum(char *name, char *ext) 420*4a5d661aSToomas Soome { 421*4a5d661aSToomas Soome int x, i; 422*4a5d661aSToomas Soome char buf[11]; 423*4a5d661aSToomas Soome 424*4a5d661aSToomas Soome bcopy(name, buf, 8); 425*4a5d661aSToomas Soome bcopy(ext, buf+8, 3); 426*4a5d661aSToomas Soome x = 0; 427*4a5d661aSToomas Soome for (i = 0; i < 11; i++) { 428*4a5d661aSToomas Soome x = ((x & 1) << 7) | (x >> 1); 429*4a5d661aSToomas Soome x += buf[i]; 430*4a5d661aSToomas Soome x &= 0xff; 431*4a5d661aSToomas Soome } 432*4a5d661aSToomas Soome return (x); 433*4a5d661aSToomas Soome } 434*4a5d661aSToomas Soome 435*4a5d661aSToomas Soome static int 436*4a5d661aSToomas Soome dos_readdir(struct open_file *fd, struct dirent *d) 437*4a5d661aSToomas Soome { 438*4a5d661aSToomas Soome /* DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; */ 439*4a5d661aSToomas Soome u_char fn[261]; 440*4a5d661aSToomas Soome DOS_DIR dd; 441*4a5d661aSToomas Soome size_t res; 442*4a5d661aSToomas Soome u_int chk, x, xdn; 443*4a5d661aSToomas Soome int err; 444*4a5d661aSToomas Soome 445*4a5d661aSToomas Soome x = chk = 0; 446*4a5d661aSToomas Soome while (1) { 447*4a5d661aSToomas Soome xdn = x; 448*4a5d661aSToomas Soome x = 0; 449*4a5d661aSToomas Soome err = dos_read(fd, &dd, sizeof(dd), &res); 450*4a5d661aSToomas Soome if (err) 451*4a5d661aSToomas Soome return (err); 452*4a5d661aSToomas Soome if (res == sizeof(dd)) 453*4a5d661aSToomas Soome return (ENOENT); 454*4a5d661aSToomas Soome if (dd.de.name[0] == 0) 455*4a5d661aSToomas Soome return (ENOENT); 456*4a5d661aSToomas Soome 457*4a5d661aSToomas Soome /* Skip deleted entries */ 458*4a5d661aSToomas Soome if (dd.de.name[0] == 0xe5) 459*4a5d661aSToomas Soome continue; 460*4a5d661aSToomas Soome 461*4a5d661aSToomas Soome /* Check if directory entry is volume label */ 462*4a5d661aSToomas Soome if (dd.de.attr & FA_LABEL) { 463*4a5d661aSToomas Soome /* 464*4a5d661aSToomas Soome * If volume label set, check if the current entry is 465*4a5d661aSToomas Soome * extended entry (FA_XDE) for long file names. 466*4a5d661aSToomas Soome */ 467*4a5d661aSToomas Soome if ((dd.de.attr & FA_MASK) == FA_XDE) { 468*4a5d661aSToomas Soome /* 469*4a5d661aSToomas Soome * Read through all following extended entries 470*4a5d661aSToomas Soome * to get the long file name. 0x40 marks the 471*4a5d661aSToomas Soome * last entry containing part of long file name. 472*4a5d661aSToomas Soome */ 473*4a5d661aSToomas Soome if (dd.xde.seq & 0x40) 474*4a5d661aSToomas Soome chk = dd.xde.chk; 475*4a5d661aSToomas Soome else if (dd.xde.seq != xdn - 1 || dd.xde.chk != chk) 476*4a5d661aSToomas Soome continue; 477*4a5d661aSToomas Soome x = dd.xde.seq & ~0x40; 478*4a5d661aSToomas Soome if (x < 1 || x > 20) { 479*4a5d661aSToomas Soome x = 0; 480*4a5d661aSToomas Soome continue; 481*4a5d661aSToomas Soome } 482*4a5d661aSToomas Soome cp_xdnm(fn, &dd.xde); 483*4a5d661aSToomas Soome } else { 484*4a5d661aSToomas Soome /* skip only volume label entries */ 485*4a5d661aSToomas Soome continue; 486*4a5d661aSToomas Soome } 487*4a5d661aSToomas Soome } else { 488*4a5d661aSToomas Soome if (xdn == 1) { 489*4a5d661aSToomas Soome x = dos_checksum(dd.de.name, dd.de.ext); 490*4a5d661aSToomas Soome if (x == chk) 491*4a5d661aSToomas Soome break; 492*4a5d661aSToomas Soome } else { 493*4a5d661aSToomas Soome cp_sfn(fn, &dd.de); 494*4a5d661aSToomas Soome break; 495*4a5d661aSToomas Soome } 496*4a5d661aSToomas Soome x = 0; 497*4a5d661aSToomas Soome } 498*4a5d661aSToomas Soome } 499*4a5d661aSToomas Soome 500*4a5d661aSToomas Soome d->d_fileno = (dd.de.clus[1] << 8) + dd.de.clus[0]; 501*4a5d661aSToomas Soome d->d_reclen = sizeof(*d); 502*4a5d661aSToomas Soome d->d_type = (dd.de.attr & FA_DIR) ? DT_DIR : DT_REG; 503*4a5d661aSToomas Soome memcpy(d->d_name, fn, sizeof(d->d_name)); 504*4a5d661aSToomas Soome return(0); 505*4a5d661aSToomas Soome } 506*4a5d661aSToomas Soome 507*4a5d661aSToomas Soome /* 508*4a5d661aSToomas Soome * Parse DOS boot sector 509*4a5d661aSToomas Soome */ 510*4a5d661aSToomas Soome static int 511*4a5d661aSToomas Soome parsebs(DOS_FS *fs, DOS_BS *bs) 512*4a5d661aSToomas Soome { 513*4a5d661aSToomas Soome u_int sc; 514*4a5d661aSToomas Soome 515*4a5d661aSToomas Soome if ((bs->jmp[0] != 0x69 && 516*4a5d661aSToomas Soome bs->jmp[0] != 0xe9 && 517*4a5d661aSToomas Soome (bs->jmp[0] != 0xeb || bs->jmp[2] != 0x90)) || 518*4a5d661aSToomas Soome bs->bpb.media < 0xf0) 519*4a5d661aSToomas Soome return EINVAL; 520*4a5d661aSToomas Soome if (cv2(bs->bpb.secsiz) != SECSIZ) 521*4a5d661aSToomas Soome return EINVAL; 522*4a5d661aSToomas Soome if (!(fs->spc = bs->bpb.spc) || fs->spc & (fs->spc - 1)) 523*4a5d661aSToomas Soome return EINVAL; 524*4a5d661aSToomas Soome fs->bsize = secbyt(fs->spc); 525*4a5d661aSToomas Soome fs->bshift = ffs(fs->bsize) - 1; 526*4a5d661aSToomas Soome if ((fs->spf = cv2(bs->bpb.spf))) { 527*4a5d661aSToomas Soome if (bs->bpb.fats != 2) 528*4a5d661aSToomas Soome return EINVAL; 529*4a5d661aSToomas Soome if (!(fs->dirents = cv2(bs->bpb.dirents))) 530*4a5d661aSToomas Soome return EINVAL; 531*4a5d661aSToomas Soome } else { 532*4a5d661aSToomas Soome if (!(fs->spf = cv4(bs->bpb.lspf))) 533*4a5d661aSToomas Soome return EINVAL; 534*4a5d661aSToomas Soome if (!bs->bpb.fats || bs->bpb.fats > 16) 535*4a5d661aSToomas Soome return EINVAL; 536*4a5d661aSToomas Soome if ((fs->rdcl = cv4(bs->bpb.rdcl)) < LOCLUS) 537*4a5d661aSToomas Soome return EINVAL; 538*4a5d661aSToomas Soome } 539*4a5d661aSToomas Soome if (!(fs->lsnfat = cv2(bs->bpb.ressec))) 540*4a5d661aSToomas Soome return EINVAL; 541*4a5d661aSToomas Soome fs->lsndir = fs->lsnfat + fs->spf * bs->bpb.fats; 542*4a5d661aSToomas Soome fs->lsndta = fs->lsndir + entsec(fs->dirents); 543*4a5d661aSToomas Soome if (!(sc = cv2(bs->bpb.secs)) && !(sc = cv4(bs->bpb.lsecs))) 544*4a5d661aSToomas Soome return EINVAL; 545*4a5d661aSToomas Soome if (fs->lsndta > sc) 546*4a5d661aSToomas Soome return EINVAL; 547*4a5d661aSToomas Soome if ((fs->xclus = secblk(fs, sc - fs->lsndta) + 1) < LOCLUS) 548*4a5d661aSToomas Soome return EINVAL; 549*4a5d661aSToomas Soome fs->fatsz = fs->dirents ? fs->xclus < 0xff6 ? 12 : 16 : 32; 550*4a5d661aSToomas Soome sc = (secbyt(fs->spf) << 1) / (fs->fatsz >> 2) - 1; 551*4a5d661aSToomas Soome if (fs->xclus > sc) 552*4a5d661aSToomas Soome fs->xclus = sc; 553*4a5d661aSToomas Soome return 0; 554*4a5d661aSToomas Soome } 555*4a5d661aSToomas Soome 556*4a5d661aSToomas Soome /* 557*4a5d661aSToomas Soome * Return directory entry from path 558*4a5d661aSToomas Soome */ 559*4a5d661aSToomas Soome static int 560*4a5d661aSToomas Soome namede(DOS_FS *fs, const char *path, DOS_DE **dep) 561*4a5d661aSToomas Soome { 562*4a5d661aSToomas Soome char name[256]; 563*4a5d661aSToomas Soome DOS_DE *de; 564*4a5d661aSToomas Soome char *s; 565*4a5d661aSToomas Soome size_t n; 566*4a5d661aSToomas Soome int err; 567*4a5d661aSToomas Soome 568*4a5d661aSToomas Soome err = 0; 569*4a5d661aSToomas Soome de = &fs->root; 570*4a5d661aSToomas Soome while (*path) { 571*4a5d661aSToomas Soome while (*path == '/') 572*4a5d661aSToomas Soome path++; 573*4a5d661aSToomas Soome if (*path == '\0') 574*4a5d661aSToomas Soome break; 575*4a5d661aSToomas Soome if (!(s = strchr(path, '/'))) 576*4a5d661aSToomas Soome s = strchr(path, 0); 577*4a5d661aSToomas Soome if ((n = s - path) > 255) 578*4a5d661aSToomas Soome return ENAMETOOLONG; 579*4a5d661aSToomas Soome memcpy(name, path, n); 580*4a5d661aSToomas Soome name[n] = 0; 581*4a5d661aSToomas Soome path = s; 582*4a5d661aSToomas Soome if (!(de->attr & FA_DIR)) 583*4a5d661aSToomas Soome return ENOTDIR; 584*4a5d661aSToomas Soome if ((err = lookup(fs, stclus(fs->fatsz, de), name, &de))) 585*4a5d661aSToomas Soome return err; 586*4a5d661aSToomas Soome } 587*4a5d661aSToomas Soome *dep = de; 588*4a5d661aSToomas Soome return 0; 589*4a5d661aSToomas Soome } 590*4a5d661aSToomas Soome 591*4a5d661aSToomas Soome /* 592*4a5d661aSToomas Soome * Lookup path segment 593*4a5d661aSToomas Soome */ 594*4a5d661aSToomas Soome static int 595*4a5d661aSToomas Soome lookup(DOS_FS *fs, u_int clus, const char *name, DOS_DE **dep) 596*4a5d661aSToomas Soome { 597*4a5d661aSToomas Soome static DOS_DIR dir[DEPSEC]; 598*4a5d661aSToomas Soome u_char lfn[261]; 599*4a5d661aSToomas Soome u_char sfn[13]; 600*4a5d661aSToomas Soome u_int nsec, lsec, xdn, chk, sec, ent, x; 601*4a5d661aSToomas Soome int err, ok; 602*4a5d661aSToomas Soome 603*4a5d661aSToomas Soome if (!clus) 604*4a5d661aSToomas Soome for (ent = 0; ent < 2; ent++) 605*4a5d661aSToomas Soome if (!strcasecmp(name, dotstr[ent])) { 606*4a5d661aSToomas Soome *dep = dot + ent; 607*4a5d661aSToomas Soome return 0; 608*4a5d661aSToomas Soome } 609*4a5d661aSToomas Soome if (!clus && fs->fatsz == 32) 610*4a5d661aSToomas Soome clus = fs->rdcl; 611*4a5d661aSToomas Soome nsec = !clus ? entsec(fs->dirents) : fs->spc; 612*4a5d661aSToomas Soome lsec = 0; 613*4a5d661aSToomas Soome xdn = chk = 0; 614*4a5d661aSToomas Soome for (;;) { 615*4a5d661aSToomas Soome if (!clus && !lsec) 616*4a5d661aSToomas Soome lsec = fs->lsndir; 617*4a5d661aSToomas Soome else if (okclus(fs, clus)) 618*4a5d661aSToomas Soome lsec = blklsn(fs, clus); 619*4a5d661aSToomas Soome else 620*4a5d661aSToomas Soome return EINVAL; 621*4a5d661aSToomas Soome for (sec = 0; sec < nsec; sec++) { 622*4a5d661aSToomas Soome if ((err = ioget(fs->fd, lsec + sec, 0, dir, secbyt(1)))) 623*4a5d661aSToomas Soome return err; 624*4a5d661aSToomas Soome for (ent = 0; ent < DEPSEC; ent++) { 625*4a5d661aSToomas Soome if (!*dir[ent].de.name) 626*4a5d661aSToomas Soome return ENOENT; 627*4a5d661aSToomas Soome if (*dir[ent].de.name != 0xe5) { 628*4a5d661aSToomas Soome if ((dir[ent].de.attr & FA_MASK) == FA_XDE) { 629*4a5d661aSToomas Soome x = dir[ent].xde.seq; 630*4a5d661aSToomas Soome if (x & 0x40 || (x + 1 == xdn && 631*4a5d661aSToomas Soome dir[ent].xde.chk == chk)) { 632*4a5d661aSToomas Soome if (x & 0x40) { 633*4a5d661aSToomas Soome chk = dir[ent].xde.chk; 634*4a5d661aSToomas Soome x &= ~0x40; 635*4a5d661aSToomas Soome } 636*4a5d661aSToomas Soome if (x >= 1 && x <= 20) { 637*4a5d661aSToomas Soome cp_xdnm(lfn, &dir[ent].xde); 638*4a5d661aSToomas Soome xdn = x; 639*4a5d661aSToomas Soome continue; 640*4a5d661aSToomas Soome } 641*4a5d661aSToomas Soome } 642*4a5d661aSToomas Soome } else if (!(dir[ent].de.attr & FA_LABEL)) { 643*4a5d661aSToomas Soome if ((ok = xdn == 1)) { 644*4a5d661aSToomas Soome x = dos_checksum(dir[ent].de.name, dir[ent].de.ext); 645*4a5d661aSToomas Soome ok = chk == x && 646*4a5d661aSToomas Soome !strcasecmp(name, (const char *)lfn); 647*4a5d661aSToomas Soome } 648*4a5d661aSToomas Soome if (!ok) { 649*4a5d661aSToomas Soome cp_sfn(sfn, &dir[ent].de); 650*4a5d661aSToomas Soome ok = !strcasecmp(name, (const char *)sfn); 651*4a5d661aSToomas Soome } 652*4a5d661aSToomas Soome if (ok) { 653*4a5d661aSToomas Soome *dep = &dir[ent].de; 654*4a5d661aSToomas Soome return 0; 655*4a5d661aSToomas Soome } 656*4a5d661aSToomas Soome } 657*4a5d661aSToomas Soome } 658*4a5d661aSToomas Soome xdn = 0; 659*4a5d661aSToomas Soome } 660*4a5d661aSToomas Soome } 661*4a5d661aSToomas Soome if (!clus) 662*4a5d661aSToomas Soome break; 663*4a5d661aSToomas Soome if ((err = fatget(fs, &clus))) 664*4a5d661aSToomas Soome return err; 665*4a5d661aSToomas Soome if (fatend(fs->fatsz, clus)) 666*4a5d661aSToomas Soome break; 667*4a5d661aSToomas Soome } 668*4a5d661aSToomas Soome return ENOENT; 669*4a5d661aSToomas Soome } 670*4a5d661aSToomas Soome 671*4a5d661aSToomas Soome /* 672*4a5d661aSToomas Soome * Copy name from extended directory entry 673*4a5d661aSToomas Soome */ 674*4a5d661aSToomas Soome static void 675*4a5d661aSToomas Soome cp_xdnm(u_char *lfn, DOS_XDE *xde) 676*4a5d661aSToomas Soome { 677*4a5d661aSToomas Soome static struct { 678*4a5d661aSToomas Soome u_int off; 679*4a5d661aSToomas Soome u_int dim; 680*4a5d661aSToomas Soome } ix[3] = { 681*4a5d661aSToomas Soome {offsetof(DOS_XDE, name1), sizeof(xde->name1) / 2}, 682*4a5d661aSToomas Soome {offsetof(DOS_XDE, name2), sizeof(xde->name2) / 2}, 683*4a5d661aSToomas Soome {offsetof(DOS_XDE, name3), sizeof(xde->name3) / 2} 684*4a5d661aSToomas Soome }; 685*4a5d661aSToomas Soome u_char *p; 686*4a5d661aSToomas Soome u_int n, x, c; 687*4a5d661aSToomas Soome 688*4a5d661aSToomas Soome lfn += 13 * ((xde->seq & ~0x40) - 1); 689*4a5d661aSToomas Soome for (n = 0; n < 3; n++) 690*4a5d661aSToomas Soome for (p = (u_char *)xde + ix[n].off, x = ix[n].dim; x; 691*4a5d661aSToomas Soome p += 2, x--) { 692*4a5d661aSToomas Soome if ((c = cv2(p)) && (c < 32 || c > 127)) 693*4a5d661aSToomas Soome c = '?'; 694*4a5d661aSToomas Soome if (!(*lfn++ = c)) 695*4a5d661aSToomas Soome return; 696*4a5d661aSToomas Soome } 697*4a5d661aSToomas Soome if (xde->seq & 0x40) 698*4a5d661aSToomas Soome *lfn = 0; 699*4a5d661aSToomas Soome } 700*4a5d661aSToomas Soome 701*4a5d661aSToomas Soome /* 702*4a5d661aSToomas Soome * Copy short filename 703*4a5d661aSToomas Soome */ 704*4a5d661aSToomas Soome static void 705*4a5d661aSToomas Soome cp_sfn(u_char *sfn, DOS_DE *de) 706*4a5d661aSToomas Soome { 707*4a5d661aSToomas Soome u_char *p; 708*4a5d661aSToomas Soome int j, i; 709*4a5d661aSToomas Soome 710*4a5d661aSToomas Soome p = sfn; 711*4a5d661aSToomas Soome if (*de->name != ' ') { 712*4a5d661aSToomas Soome for (j = 7; de->name[j] == ' '; j--); 713*4a5d661aSToomas Soome for (i = 0; i <= j; i++) 714*4a5d661aSToomas Soome *p++ = de->name[i]; 715*4a5d661aSToomas Soome if (*de->ext != ' ') { 716*4a5d661aSToomas Soome *p++ = '.'; 717*4a5d661aSToomas Soome for (j = 2; de->ext[j] == ' '; j--); 718*4a5d661aSToomas Soome for (i = 0; i <= j; i++) 719*4a5d661aSToomas Soome *p++ = de->ext[i]; 720*4a5d661aSToomas Soome } 721*4a5d661aSToomas Soome } 722*4a5d661aSToomas Soome *p = 0; 723*4a5d661aSToomas Soome if (*sfn == 5) 724*4a5d661aSToomas Soome *sfn = 0xe5; 725*4a5d661aSToomas Soome } 726*4a5d661aSToomas Soome 727*4a5d661aSToomas Soome /* 728*4a5d661aSToomas Soome * Return size of file in bytes 729*4a5d661aSToomas Soome */ 730*4a5d661aSToomas Soome static off_t 731*4a5d661aSToomas Soome fsize(DOS_FS *fs, DOS_DE *de) 732*4a5d661aSToomas Soome { 733*4a5d661aSToomas Soome u_long size; 734*4a5d661aSToomas Soome u_int c; 735*4a5d661aSToomas Soome int n; 736*4a5d661aSToomas Soome 737*4a5d661aSToomas Soome if (!(size = cv4(de->size)) && de->attr & FA_DIR) { 738*4a5d661aSToomas Soome if (!(c = cv2(de->clus))) 739*4a5d661aSToomas Soome size = fs->dirents * sizeof(DOS_DE); 740*4a5d661aSToomas Soome else { 741*4a5d661aSToomas Soome if ((n = fatcnt(fs, c)) == -1) 742*4a5d661aSToomas Soome return n; 743*4a5d661aSToomas Soome size = blkbyt(fs, n); 744*4a5d661aSToomas Soome } 745*4a5d661aSToomas Soome } 746*4a5d661aSToomas Soome return size; 747*4a5d661aSToomas Soome } 748*4a5d661aSToomas Soome 749*4a5d661aSToomas Soome /* 750*4a5d661aSToomas Soome * Count number of clusters in chain 751*4a5d661aSToomas Soome */ 752*4a5d661aSToomas Soome static int 753*4a5d661aSToomas Soome fatcnt(DOS_FS *fs, u_int c) 754*4a5d661aSToomas Soome { 755*4a5d661aSToomas Soome int n; 756*4a5d661aSToomas Soome 757*4a5d661aSToomas Soome for (n = 0; okclus(fs, c); n++) 758*4a5d661aSToomas Soome if (fatget(fs, &c)) 759*4a5d661aSToomas Soome return -1; 760*4a5d661aSToomas Soome return fatend(fs->fatsz, c) ? n : -1; 761*4a5d661aSToomas Soome } 762*4a5d661aSToomas Soome 763*4a5d661aSToomas Soome /* 764*4a5d661aSToomas Soome * Get next cluster in cluster chain. Use in core fat cache unless another 765*4a5d661aSToomas Soome * device replaced it. 766*4a5d661aSToomas Soome */ 767*4a5d661aSToomas Soome static int 768*4a5d661aSToomas Soome fatget(DOS_FS *fs, u_int *c) 769*4a5d661aSToomas Soome { 770*4a5d661aSToomas Soome u_char buf[4]; 771*4a5d661aSToomas Soome u_char *s; 772*4a5d661aSToomas Soome u_int x, offset, off, n, nbyte, lsec; 773*4a5d661aSToomas Soome struct devdesc *dd = fs->fd->f_devdata; 774*4a5d661aSToomas Soome int err = 0; 775*4a5d661aSToomas Soome 776*4a5d661aSToomas Soome if (fat.unit != dd->d_unit) { 777*4a5d661aSToomas Soome /* fat cache was changed to another device, don't use it */ 778*4a5d661aSToomas Soome err = ioread(fs, secbyt(fs->lsnfat) + fatoff(fs->fatsz, *c), buf, 779*4a5d661aSToomas Soome fs->fatsz != 32 ? 2 : 4); 780*4a5d661aSToomas Soome if (err) 781*4a5d661aSToomas Soome return (err); 782*4a5d661aSToomas Soome } else { 783*4a5d661aSToomas Soome offset = fatoff(fs->fatsz, *c); 784*4a5d661aSToomas Soome nbyte = fs->fatsz != 32 ? 2 : 4; 785*4a5d661aSToomas Soome 786*4a5d661aSToomas Soome s = buf; 787*4a5d661aSToomas Soome if ((off = offset & (SECSIZ - 1))) { 788*4a5d661aSToomas Soome offset -= off; 789*4a5d661aSToomas Soome lsec = bytsec(offset); 790*4a5d661aSToomas Soome offset += SECSIZ; 791*4a5d661aSToomas Soome if ((n = SECSIZ - off) > nbyte) 792*4a5d661aSToomas Soome n = nbyte; 793*4a5d661aSToomas Soome memcpy(s, fat.buf + secbyt(lsec) + off, n); 794*4a5d661aSToomas Soome s += n; 795*4a5d661aSToomas Soome nbyte -= n; 796*4a5d661aSToomas Soome } 797*4a5d661aSToomas Soome n = nbyte & (SECSIZ - 1); 798*4a5d661aSToomas Soome if (nbyte -= n) { 799*4a5d661aSToomas Soome memcpy(s, fat.buf + secbyt(bytsec(offset)), nbyte); 800*4a5d661aSToomas Soome offset += nbyte; 801*4a5d661aSToomas Soome s += nbyte; 802*4a5d661aSToomas Soome } 803*4a5d661aSToomas Soome if (n) 804*4a5d661aSToomas Soome memcpy(s, fat.buf + secbyt(bytsec(offset)), n); 805*4a5d661aSToomas Soome } 806*4a5d661aSToomas Soome 807*4a5d661aSToomas Soome x = fs->fatsz != 32 ? cv2(buf) : cv4(buf); 808*4a5d661aSToomas Soome *c = fs->fatsz == 12 ? *c & 1 ? x >> 4 : x & 0xfff : x; 809*4a5d661aSToomas Soome return (0); 810*4a5d661aSToomas Soome } 811*4a5d661aSToomas Soome 812*4a5d661aSToomas Soome /* 813*4a5d661aSToomas Soome * Is cluster an end-of-chain marker? 814*4a5d661aSToomas Soome */ 815*4a5d661aSToomas Soome static int 816*4a5d661aSToomas Soome fatend(u_int sz, u_int c) 817*4a5d661aSToomas Soome { 818*4a5d661aSToomas Soome return c > (sz == 12 ? 0xff7U : sz == 16 ? 0xfff7U : 0xffffff7); 819*4a5d661aSToomas Soome } 820*4a5d661aSToomas Soome 821*4a5d661aSToomas Soome /* 822*4a5d661aSToomas Soome * Offset-based I/O primitive 823*4a5d661aSToomas Soome */ 824*4a5d661aSToomas Soome static int 825*4a5d661aSToomas Soome ioread(DOS_FS *fs, u_int offset, void *buf, u_int nbyte) 826*4a5d661aSToomas Soome { 827*4a5d661aSToomas Soome char *s; 828*4a5d661aSToomas Soome u_int off, n; 829*4a5d661aSToomas Soome int err; 830*4a5d661aSToomas Soome 831*4a5d661aSToomas Soome s = buf; 832*4a5d661aSToomas Soome if ((off = offset & (SECSIZ - 1))) { 833*4a5d661aSToomas Soome offset -= off; 834*4a5d661aSToomas Soome if ((n = SECSIZ - off) > nbyte) 835*4a5d661aSToomas Soome n = nbyte; 836*4a5d661aSToomas Soome if ((err = ioget(fs->fd, bytsec(offset), off, s, n))) 837*4a5d661aSToomas Soome return err; 838*4a5d661aSToomas Soome offset += SECSIZ; 839*4a5d661aSToomas Soome s += n; 840*4a5d661aSToomas Soome nbyte -= n; 841*4a5d661aSToomas Soome } 842*4a5d661aSToomas Soome n = nbyte & (SECSIZ - 1); 843*4a5d661aSToomas Soome if (nbyte -= n) { 844*4a5d661aSToomas Soome if ((err = ioget(fs->fd, bytsec(offset), 0, s, nbyte))) 845*4a5d661aSToomas Soome return err; 846*4a5d661aSToomas Soome offset += nbyte; 847*4a5d661aSToomas Soome s += nbyte; 848*4a5d661aSToomas Soome } 849*4a5d661aSToomas Soome if (n) { 850*4a5d661aSToomas Soome if ((err = ioget(fs->fd, bytsec(offset), 0, s, n))) 851*4a5d661aSToomas Soome return err; 852*4a5d661aSToomas Soome } 853*4a5d661aSToomas Soome return 0; 854*4a5d661aSToomas Soome } 855*4a5d661aSToomas Soome 856*4a5d661aSToomas Soome /* 857*4a5d661aSToomas Soome * Sector-based I/O primitive 858*4a5d661aSToomas Soome */ 859*4a5d661aSToomas Soome static int 860*4a5d661aSToomas Soome ioget(struct open_file *fd, daddr_t lsec, size_t offset, void *buf, u_int size) 861*4a5d661aSToomas Soome { 862*4a5d661aSToomas Soome return ((fd->f_dev->dv_strategy)(fd->f_devdata, F_READ, lsec, offset, 863*4a5d661aSToomas Soome size, buf, NULL)); 864*4a5d661aSToomas Soome } 865