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