138451Smsmith/* 238451Smsmith * Copyright (c) 1996, 1998 Robert Nordier 338451Smsmith * All rights reserved. 438451Smsmith * 538451Smsmith * Redistribution and use in source and binary forms, with or without 638451Smsmith * modification, are permitted provided that the following conditions 738451Smsmith * are met: 838451Smsmith * 1. Redistributions of source code must retain the above copyright 938451Smsmith * notice, this list of conditions and the following disclaimer. 1038451Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1138451Smsmith * notice, this list of conditions and the following disclaimer in 1238451Smsmith * the documentation and/or other materials provided with the 1338451Smsmith * distribution. 1438451Smsmith * 1538451Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS 1638451Smsmith * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 1738451Smsmith * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1838451Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY 1938451Smsmith * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2038451Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 2138451Smsmith * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2238451Smsmith * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 2338451Smsmith * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 2438451Smsmith * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 2538451Smsmith * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2638451Smsmith */ 2738451Smsmith 2884221Sdillon#include <sys/cdefs.h> 2984221Sdillon__FBSDID("$FreeBSD: stable/11/stand/libsa/dosfs.c 344408 2019-02-21 02:43:48Z kevans $"); 3084221Sdillon 3138451Smsmith/* 3238451Smsmith * Readonly filesystem for Microsoft FAT12/FAT16/FAT32 filesystems, 3338451Smsmith * also supports VFAT. 3438451Smsmith */ 3538451Smsmith 3638451Smsmith#include <sys/types.h> 3738451Smsmith#include <string.h> 3838451Smsmith#include <stddef.h> 3938451Smsmith 4038451Smsmith#include "stand.h" 4138451Smsmith 4238451Smsmith#include "dosfs.h" 4338451Smsmith 4438451Smsmith 4540005Smsmithstatic int dos_open(const char *path, struct open_file *fd); 4638451Smsmithstatic int dos_close(struct open_file *fd); 4738451Smsmithstatic int dos_read(struct open_file *fd, void *buf, size_t size, size_t *resid); 4838451Smsmithstatic off_t dos_seek(struct open_file *fd, off_t offset, int whence); 4938451Smsmithstatic int dos_stat(struct open_file *fd, struct stat *sb); 50201937Smarcelstatic int dos_readdir(struct open_file *fd, struct dirent *d); 5138451Smsmith 5238582Srnordierstruct fs_ops dosfs_fsops = { 5359766Sjlemon "dosfs", 5459766Sjlemon dos_open, 5559766Sjlemon dos_close, 5659766Sjlemon dos_read, 5759766Sjlemon null_write, 5859766Sjlemon dos_seek, 5959766Sjlemon dos_stat, 60201937Smarcel dos_readdir 6138451Smsmith}; 6238451Smsmith 6338451Smsmith#define SECSIZ 512 /* sector size */ 6438451Smsmith#define SSHIFT 9 /* SECSIZ shift */ 6538451Smsmith#define DEPSEC 16 /* directory entries per sector */ 6638451Smsmith#define DSHIFT 4 /* DEPSEC shift */ 6738451Smsmith#define LOCLUS 2 /* lowest cluster number */ 68329100Skevans#define FATBLKSZ 0x20000 /* size of block in the FAT cache buffer */ 6938451Smsmith 7038451Smsmith/* DOS "BIOS Parameter Block" */ 7138451Smsmithtypedef struct { 7238451Smsmith u_char secsiz[2]; /* sector size */ 7338451Smsmith u_char spc; /* sectors per cluster */ 7438451Smsmith u_char ressec[2]; /* reserved sectors */ 7538451Smsmith u_char fats; /* FATs */ 7638451Smsmith u_char dirents[2]; /* root directory entries */ 7738451Smsmith u_char secs[2]; /* total sectors */ 7838451Smsmith u_char media; /* media descriptor */ 7938451Smsmith u_char spf[2]; /* sectors per FAT */ 8038451Smsmith u_char spt[2]; /* sectors per track */ 8138451Smsmith u_char heads[2]; /* drive heads */ 8238451Smsmith u_char hidsec[4]; /* hidden sectors */ 8338451Smsmith u_char lsecs[4]; /* huge sectors */ 8438451Smsmith u_char lspf[4]; /* huge sectors per FAT */ 8538451Smsmith u_char xflg[2]; /* flags */ 8638451Smsmith u_char vers[2]; /* filesystem version */ 8738451Smsmith u_char rdcl[4]; /* root directory start cluster */ 8838451Smsmith u_char infs[2]; /* filesystem info sector */ 8938451Smsmith u_char bkbs[2]; /* backup boot sector */ 9038451Smsmith} DOS_BPB; 9138451Smsmith 9238451Smsmith/* Initial portion of DOS boot sector */ 9338451Smsmithtypedef struct { 9438451Smsmith u_char jmp[3]; /* usually 80x86 'jmp' opcode */ 9538451Smsmith u_char oem[8]; /* OEM name and version */ 9638451Smsmith DOS_BPB bpb; /* BPB */ 9738451Smsmith} DOS_BS; 9838451Smsmith 9938451Smsmith/* Supply missing "." and ".." root directory entries */ 10038451Smsmithstatic const char *const dotstr[2] = {".", ".."}; 10138451Smsmithstatic DOS_DE dot[2] = { 10238451Smsmith {". ", " ", FA_DIR, {0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 10338451Smsmith {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}}, 10438451Smsmith {".. ", " ", FA_DIR, {0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 10538451Smsmith {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}} 10638451Smsmith}; 10738451Smsmith 10838451Smsmith/* The usual conversion macros to avoid multiplication and division */ 10938451Smsmith#define bytsec(n) ((n) >> SSHIFT) 11038451Smsmith#define secbyt(s) ((s) << SSHIFT) 11138451Smsmith#define entsec(e) ((e) >> DSHIFT) 11238451Smsmith#define bytblk(fs, n) ((n) >> (fs)->bshift) 11338451Smsmith#define blkbyt(fs, b) ((b) << (fs)->bshift) 11438451Smsmith#define secblk(fs, s) ((s) >> ((fs)->bshift - SSHIFT)) 11538451Smsmith#define blksec(fs, b) ((b) << ((fs)->bshift - SSHIFT)) 11638451Smsmith 11738451Smsmith/* Convert cluster number to offset within filesystem */ 11838451Smsmith#define blkoff(fs, b) (secbyt((fs)->lsndta) + blkbyt(fs, (b) - LOCLUS)) 11938451Smsmith 12038451Smsmith/* Convert cluster number to logical sector number */ 12138451Smsmith#define blklsn(fs, b) ((fs)->lsndta + blksec(fs, (b) - LOCLUS)) 12238451Smsmith 12338451Smsmith/* Convert cluster number to offset within FAT */ 12438451Smsmith#define fatoff(sz, c) ((sz) == 12 ? (c) + ((c) >> 1) : \ 12538451Smsmith (sz) == 16 ? (c) << 1 : \ 12638451Smsmith (c) << 2) 12738451Smsmith 12838451Smsmith/* Does cluster number reference a valid data cluster? */ 12938451Smsmith#define okclus(fs, c) ((c) >= LOCLUS && (c) <= (fs)->xclus) 13038451Smsmith 13138451Smsmith/* Get start cluster from directory entry */ 13238451Smsmith#define stclus(sz, de) ((sz) != 32 ? cv2((de)->clus) : \ 13338451Smsmith ((u_int)cv2((de)->dex.h_clus) << 16) | \ 13438451Smsmith cv2((de)->clus)) 135298230Sallanjude 13638451Smsmithstatic int parsebs(DOS_FS *, DOS_BS *); 13738451Smsmithstatic int namede(DOS_FS *, const char *, DOS_DE **); 13838451Smsmithstatic int lookup(DOS_FS *, u_int, const char *, DOS_DE **); 13938451Smsmithstatic void cp_xdnm(u_char *, DOS_XDE *); 14038451Smsmithstatic void cp_sfn(u_char *, DOS_DE *); 14138582Srnordierstatic off_t fsize(DOS_FS *, DOS_DE *); 14238582Srnordierstatic int fatcnt(DOS_FS *, u_int); 14338451Smsmithstatic int fatget(DOS_FS *, u_int *); 14438451Smsmithstatic int fatend(u_int, u_int); 145329100Skevansstatic int ioread(DOS_FS *, u_int, void *, size_t); 146329100Skevansstatic int ioget(struct open_file *, daddr_t, void *, size_t); 14738451Smsmith 148329100Skevansstatic int 149329100Skevansdos_read_fatblk(DOS_FS *fs, struct open_file *fd, u_int blknum) 150298230Sallanjude{ 151329100Skevans int err; 152329100Skevans size_t io_size; 153329100Skevans daddr_t offset_in_fat, max_offset_in_fat; 154298230Sallanjude 155329100Skevans offset_in_fat = ((daddr_t)blknum) * FATBLKSZ; 156329100Skevans max_offset_in_fat = secbyt(fs->spf); 157329100Skevans io_size = FATBLKSZ; 158329100Skevans if (offset_in_fat > max_offset_in_fat) 159329100Skevans offset_in_fat = max_offset_in_fat; 160329100Skevans if (offset_in_fat + io_size > max_offset_in_fat) 161329100Skevans io_size = ((size_t)(max_offset_in_fat - offset_in_fat)); 162329100Skevans 163329100Skevans if (io_size != 0) { 164329100Skevans err = ioget(fd, fs->lsnfat + bytsec(offset_in_fat), 165329100Skevans fs->fatbuf, io_size); 166329100Skevans if (err != 0) { 167329100Skevans fs->fatbuf_blknum = ((u_int)(-1)); 168329100Skevans return (err); 169329100Skevans } 170298230Sallanjude } 171329100Skevans if (io_size < FATBLKSZ) 172329100Skevans memset(fs->fatbuf + io_size, 0, FATBLKSZ - io_size); 173298230Sallanjude 174329100Skevans fs->fatbuf_blknum = blknum; 175329100Skevans return (0); 176298230Sallanjude} 177298230Sallanjude 17838451Smsmith/* 17938451Smsmith * Mount DOS filesystem 18038451Smsmith */ 18138451Smsmithstatic int 18238451Smsmithdos_mount(DOS_FS *fs, struct open_file *fd) 18338451Smsmith{ 18438451Smsmith int err; 185298230Sallanjude u_char *buf; 18638451Smsmith 18738451Smsmith bzero(fs, sizeof(DOS_FS)); 18838451Smsmith fs->fd = fd; 189298230Sallanjude 190329100Skevans if ((buf = malloc(secbyt(1))) == NULL) 191329100Skevans return (errno); 192329100Skevans if ((err = ioget(fs->fd, 0, buf, secbyt(1))) || 193298230Sallanjude (err = parsebs(fs, (DOS_BS *)buf))) { 194329100Skevans free(buf); 195313355Stsoome return (err); 19638451Smsmith } 197298230Sallanjude free(buf); 198298230Sallanjude 199329100Skevans if ((fs->fatbuf = malloc(FATBLKSZ)) == NULL) 200329100Skevans return (errno); 201329100Skevans err = dos_read_fatblk(fs, fd, 0); 202329100Skevans if (err != 0) { 203329100Skevans free(fs->fatbuf); 204329100Skevans return (err); 205329100Skevans } 206298230Sallanjude 207259590Smarcel fs->root = dot[0]; 208259590Smarcel fs->root.name[0] = ' '; 209259590Smarcel if (fs->fatsz == 32) { 210259590Smarcel fs->root.clus[0] = fs->rdcl & 0xff; 211259590Smarcel fs->root.clus[1] = (fs->rdcl >> 8) & 0xff; 212259590Smarcel fs->root.dex.h_clus[0] = (fs->rdcl >> 16) & 0xff; 213259590Smarcel fs->root.dex.h_clus[1] = (fs->rdcl >> 24) & 0xff; 214259590Smarcel } 215313355Stsoome return (0); 21638451Smsmith} 21738451Smsmith 21838451Smsmith/* 21938451Smsmith * Unmount mounted filesystem 22038451Smsmith */ 22138451Smsmithstatic int 22238451Smsmithdos_unmount(DOS_FS *fs) 22338451Smsmith{ 22438451Smsmith if (fs->links) 225313355Stsoome return (EBUSY); 226329100Skevans free(fs->fatbuf); 22738582Srnordier free(fs); 228313355Stsoome return (0); 22938451Smsmith} 23038451Smsmith 23138451Smsmith/* 23238451Smsmith * Open DOS file 23338451Smsmith */ 23438451Smsmithstatic int 23540005Smsmithdos_open(const char *path, struct open_file *fd) 23638451Smsmith{ 23738451Smsmith DOS_DE *de; 23838451Smsmith DOS_FILE *f; 23938451Smsmith DOS_FS *fs; 24038451Smsmith u_int size, clus; 241329100Skevans int err; 24238451Smsmith 24338451Smsmith /* Allocate mount structure, associate with open */ 244329100Skevans if ((fs = malloc(sizeof(DOS_FS))) == NULL) 245329100Skevans return (errno); 246329100Skevans if ((err = dos_mount(fs, fd))) { 247329100Skevans free(fs); 248329100Skevans return (err); 249329100Skevans } 25038451Smsmith 251329100Skevans if ((err = namede(fs, path, &de))) { 252329100Skevans dos_unmount(fs); 253329100Skevans return (err); 254329100Skevans } 25538451Smsmith 25638451Smsmith clus = stclus(fs->fatsz, de); 25738451Smsmith size = cv4(de->size); 25838582Srnordier 25938582Srnordier if ((!(de->attr & FA_DIR) && (!clus != !size)) || 26038582Srnordier ((de->attr & FA_DIR) && size) || 26138582Srnordier (clus && !okclus(fs, clus))) { 262329100Skevans dos_unmount(fs); 263329100Skevans return (EINVAL); 26438451Smsmith } 265329100Skevans if ((f = malloc(sizeof(DOS_FILE))) == NULL) { 266329100Skevans err = errno; 267329100Skevans dos_unmount(fs); 268329100Skevans return (err); 269329100Skevans } 27038451Smsmith bzero(f, sizeof(DOS_FILE)); 27138451Smsmith f->fs = fs; 27238451Smsmith fs->links++; 27338451Smsmith f->de = *de; 27438451Smsmith fd->f_fsdata = (void *)f; 275329100Skevans return (0); 27638451Smsmith} 27738451Smsmith 27838451Smsmith/* 27938451Smsmith * Read from file 28038451Smsmith */ 28138451Smsmithstatic int 28238451Smsmithdos_read(struct open_file *fd, void *buf, size_t nbyte, size_t *resid) 28338451Smsmith{ 28438582Srnordier off_t size; 28538451Smsmith u_int nb, off, clus, c, cnt, n; 28638451Smsmith DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 28738451Smsmith int err = 0; 28838451Smsmith 289298230Sallanjude /* 290298230Sallanjude * as ioget() can be called *a lot*, use twiddle here. 291298230Sallanjude * also 4 seems to be good value not to slow loading down too much: 292298230Sallanjude * with 270MB file (~540k ioget() calls, twiddle can easily waste 4-5sec. 293298230Sallanjude */ 294298230Sallanjude twiddle(4); 29538451Smsmith nb = (u_int)nbyte; 29638582Srnordier if ((size = fsize(f->fs, &f->de)) == -1) 297313355Stsoome return (EINVAL); 29838582Srnordier if (nb > (n = size - f->offset)) 299298230Sallanjude nb = n; 30038451Smsmith off = f->offset; 30138451Smsmith if ((clus = stclus(f->fs->fatsz, &f->de))) 302298230Sallanjude off &= f->fs->bsize - 1; 30338451Smsmith c = f->c; 30438451Smsmith cnt = nb; 30538451Smsmith while (cnt) { 306298230Sallanjude n = 0; 307298230Sallanjude if (!c) { 308298230Sallanjude if ((c = clus)) 309298230Sallanjude n = bytblk(f->fs, f->offset); 310298230Sallanjude } else if (!off) 311298230Sallanjude n++; 312298230Sallanjude while (n--) { 313298230Sallanjude if ((err = fatget(f->fs, &c))) 31438451Smsmith goto out; 315298230Sallanjude if (!okclus(f->fs, c)) { 31638451Smsmith err = EINVAL; 31738451Smsmith goto out; 31838451Smsmith } 319298230Sallanjude } 320298230Sallanjude if (!clus || (n = f->fs->bsize - off) > cnt) 321298230Sallanjude n = cnt; 322298230Sallanjude if ((err = ioread(f->fs, (c ? blkoff(f->fs, c) : 323298230Sallanjude secbyt(f->fs->lsndir)) + off, buf, n))) 32438451Smsmith goto out; 325298230Sallanjude f->offset += n; 326298230Sallanjude f->c = c; 327298230Sallanjude off = 0; 328298230Sallanjude buf = (char *)buf + n; 329298230Sallanjude cnt -= n; 33038451Smsmith } 33138451Smsmith out: 33238451Smsmith if (resid) 33338582Srnordier *resid = nbyte - nb + cnt; 334313355Stsoome return (err); 33538451Smsmith} 33638451Smsmith 33738451Smsmith/* 33838451Smsmith * Reposition within file 33938451Smsmith */ 34038451Smsmithstatic off_t 34138451Smsmithdos_seek(struct open_file *fd, off_t offset, int whence) 34238451Smsmith{ 34338451Smsmith off_t off; 34438451Smsmith u_int size; 34538451Smsmith DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 34638451Smsmith 34738451Smsmith size = cv4(f->de.size); 34838451Smsmith switch (whence) { 34938451Smsmith case SEEK_SET: 35038451Smsmith off = 0; 35138451Smsmith break; 35238451Smsmith case SEEK_CUR: 35338451Smsmith off = f->offset; 35438451Smsmith break; 35538451Smsmith case SEEK_END: 35638451Smsmith off = size; 35738451Smsmith break; 35838451Smsmith default: 359124811Sjhb errno = EINVAL; 360313355Stsoome return (-1); 36138451Smsmith } 36238451Smsmith off += offset; 363124811Sjhb if (off < 0 || off > size) { 364124811Sjhb errno = EINVAL; 365313355Stsoome return (-1); 366124811Sjhb } 36738451Smsmith f->offset = (u_int)off; 36838451Smsmith f->c = 0; 369313355Stsoome return (off); 37038451Smsmith} 37138451Smsmith 37238451Smsmith/* 37338451Smsmith * Close open file 37438451Smsmith */ 37538451Smsmithstatic int 37638451Smsmithdos_close(struct open_file *fd) 37738451Smsmith{ 37838451Smsmith DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 37938451Smsmith DOS_FS *fs = f->fs; 38038451Smsmith 38138451Smsmith f->fs->links--; 38238582Srnordier free(f); 38338451Smsmith dos_unmount(fs); 384313355Stsoome return (0); 38538451Smsmith} 38638451Smsmith 38738451Smsmith/* 38838451Smsmith * Return some stat information on a file. 38938451Smsmith */ 39038451Smsmithstatic int 39138451Smsmithdos_stat(struct open_file *fd, struct stat *sb) 39238451Smsmith{ 39338451Smsmith DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; 39438451Smsmith 39538451Smsmith /* only important stuff */ 39638582Srnordier sb->st_mode = f->de.attr & FA_DIR ? S_IFDIR | 0555 : S_IFREG | 0444; 39738451Smsmith sb->st_nlink = 1; 39838451Smsmith sb->st_uid = 0; 39938451Smsmith sb->st_gid = 0; 40038582Srnordier if ((sb->st_size = fsize(f->fs, &f->de)) == -1) 401313355Stsoome return (EINVAL); 40238451Smsmith return (0); 40338451Smsmith} 40438451Smsmith 405201937Smarcelstatic int 406344408Skevansdos_checksum(unsigned char *name, unsigned char *ext) 407298230Sallanjude{ 408298230Sallanjude int x, i; 409298230Sallanjude char buf[11]; 410298230Sallanjude 411298230Sallanjude bcopy(name, buf, 8); 412298230Sallanjude bcopy(ext, buf+8, 3); 413298230Sallanjude x = 0; 414298230Sallanjude for (i = 0; i < 11; i++) { 415298230Sallanjude x = ((x & 1) << 7) | (x >> 1); 416298230Sallanjude x += buf[i]; 417298230Sallanjude x &= 0xff; 418298230Sallanjude } 419298230Sallanjude return (x); 420298230Sallanjude} 421298230Sallanjude 422298230Sallanjudestatic int 423201937Smarceldos_readdir(struct open_file *fd, struct dirent *d) 424201937Smarcel{ 425221365Srodrigc /* DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; */ 426201937Smarcel u_char fn[261]; 427201937Smarcel DOS_DIR dd; 428201937Smarcel size_t res; 429344376Skevans u_int chk, x, xdn; 430201937Smarcel int err; 431201937Smarcel 432201937Smarcel x = chk = 0; 433201937Smarcel while (1) { 434201937Smarcel xdn = x; 435201937Smarcel x = 0; 436201937Smarcel err = dos_read(fd, &dd, sizeof(dd), &res); 437201937Smarcel if (err) 438201937Smarcel return (err); 439201937Smarcel if (res == sizeof(dd)) 440201937Smarcel return (ENOENT); 441201937Smarcel if (dd.de.name[0] == 0) 442201937Smarcel return (ENOENT); 443201937Smarcel 444201937Smarcel /* Skip deleted entries */ 445201937Smarcel if (dd.de.name[0] == 0xe5) 446201937Smarcel continue; 447201937Smarcel 448259539Smarcel /* Check if directory entry is volume label */ 449259539Smarcel if (dd.de.attr & FA_LABEL) { 450259539Smarcel /* 451259539Smarcel * If volume label set, check if the current entry is 452259539Smarcel * extended entry (FA_XDE) for long file names. 453259539Smarcel */ 454259539Smarcel if ((dd.de.attr & FA_MASK) == FA_XDE) { 455259539Smarcel /* 456259539Smarcel * Read through all following extended entries 457259539Smarcel * to get the long file name. 0x40 marks the 458259539Smarcel * last entry containing part of long file name. 459259539Smarcel */ 460259539Smarcel if (dd.xde.seq & 0x40) 461259539Smarcel chk = dd.xde.chk; 462259539Smarcel else if (dd.xde.seq != xdn - 1 || dd.xde.chk != chk) 463259539Smarcel continue; 464259539Smarcel x = dd.xde.seq & ~0x40; 465259539Smarcel if (x < 1 || x > 20) { 466259539Smarcel x = 0; 467259539Smarcel continue; 468259539Smarcel } 469259539Smarcel cp_xdnm(fn, &dd.xde); 470259539Smarcel } else { 471259539Smarcel /* skip only volume label entries */ 472201937Smarcel continue; 473201937Smarcel } 474201937Smarcel } else { 475201937Smarcel if (xdn == 1) { 476298230Sallanjude x = dos_checksum(dd.de.name, dd.de.ext); 477201937Smarcel if (x == chk) 478201937Smarcel break; 479201937Smarcel } else { 480201937Smarcel cp_sfn(fn, &dd.de); 481201937Smarcel break; 482201937Smarcel } 483201937Smarcel x = 0; 484201937Smarcel } 485201937Smarcel } 486201937Smarcel 487221365Srodrigc d->d_fileno = (dd.de.clus[1] << 8) + dd.de.clus[0]; 488201937Smarcel d->d_reclen = sizeof(*d); 489201937Smarcel d->d_type = (dd.de.attr & FA_DIR) ? DT_DIR : DT_REG; 490201937Smarcel memcpy(d->d_name, fn, sizeof(d->d_name)); 491313355Stsoome return (0); 492201937Smarcel} 493201937Smarcel 49438451Smsmith/* 49538451Smsmith * Parse DOS boot sector 49638451Smsmith */ 49738451Smsmithstatic int 49838451Smsmithparsebs(DOS_FS *fs, DOS_BS *bs) 49938451Smsmith{ 50038451Smsmith u_int sc; 50138451Smsmith 50238451Smsmith if ((bs->jmp[0] != 0x69 && 50338451Smsmith bs->jmp[0] != 0xe9 && 50438451Smsmith (bs->jmp[0] != 0xeb || bs->jmp[2] != 0x90)) || 50538451Smsmith bs->bpb.media < 0xf0) 506313355Stsoome return (EINVAL); 50738451Smsmith if (cv2(bs->bpb.secsiz) != SECSIZ) 508313355Stsoome return (EINVAL); 50938451Smsmith if (!(fs->spc = bs->bpb.spc) || fs->spc & (fs->spc - 1)) 510313355Stsoome return (EINVAL); 51138451Smsmith fs->bsize = secbyt(fs->spc); 51238451Smsmith fs->bshift = ffs(fs->bsize) - 1; 51338451Smsmith if ((fs->spf = cv2(bs->bpb.spf))) { 51438451Smsmith if (bs->bpb.fats != 2) 515313355Stsoome return (EINVAL); 51638451Smsmith if (!(fs->dirents = cv2(bs->bpb.dirents))) 517313355Stsoome return (EINVAL); 51838451Smsmith } else { 51938451Smsmith if (!(fs->spf = cv4(bs->bpb.lspf))) 520313355Stsoome return (EINVAL); 52138451Smsmith if (!bs->bpb.fats || bs->bpb.fats > 16) 522313355Stsoome return (EINVAL); 52338451Smsmith if ((fs->rdcl = cv4(bs->bpb.rdcl)) < LOCLUS) 524313355Stsoome return (EINVAL); 52538451Smsmith } 52638451Smsmith if (!(fs->lsnfat = cv2(bs->bpb.ressec))) 527313355Stsoome return (EINVAL); 52838451Smsmith fs->lsndir = fs->lsnfat + fs->spf * bs->bpb.fats; 52938451Smsmith fs->lsndta = fs->lsndir + entsec(fs->dirents); 53038451Smsmith if (!(sc = cv2(bs->bpb.secs)) && !(sc = cv4(bs->bpb.lsecs))) 531313355Stsoome return (EINVAL); 53238451Smsmith if (fs->lsndta > sc) 533313355Stsoome return (EINVAL); 53438451Smsmith if ((fs->xclus = secblk(fs, sc - fs->lsndta) + 1) < LOCLUS) 535313355Stsoome return (EINVAL); 53638451Smsmith fs->fatsz = fs->dirents ? fs->xclus < 0xff6 ? 12 : 16 : 32; 53738451Smsmith sc = (secbyt(fs->spf) << 1) / (fs->fatsz >> 2) - 1; 53838451Smsmith if (fs->xclus > sc) 53938451Smsmith fs->xclus = sc; 540313355Stsoome return (0); 54138451Smsmith} 54238451Smsmith 54338451Smsmith/* 54438451Smsmith * Return directory entry from path 54538451Smsmith */ 54638451Smsmithstatic int 54738451Smsmithnamede(DOS_FS *fs, const char *path, DOS_DE **dep) 54838451Smsmith{ 54938451Smsmith char name[256]; 55038451Smsmith DOS_DE *de; 55138451Smsmith char *s; 55238451Smsmith size_t n; 55338451Smsmith int err; 55438451Smsmith 55538451Smsmith err = 0; 556259590Smarcel de = &fs->root; 55738451Smsmith while (*path) { 558259590Smarcel while (*path == '/') 559259590Smarcel path++; 560259590Smarcel if (*path == '\0') 561259590Smarcel break; 56238451Smsmith if (!(s = strchr(path, '/'))) 56338451Smsmith s = strchr(path, 0); 56438451Smsmith if ((n = s - path) > 255) 565313355Stsoome return (ENAMETOOLONG); 56638451Smsmith memcpy(name, path, n); 56738451Smsmith name[n] = 0; 56838451Smsmith path = s; 56938451Smsmith if (!(de->attr & FA_DIR)) 570313355Stsoome return (ENOTDIR); 57138451Smsmith if ((err = lookup(fs, stclus(fs->fatsz, de), name, &de))) 572313355Stsoome return (err); 57338451Smsmith } 57438451Smsmith *dep = de; 575313355Stsoome return (0); 57638451Smsmith} 57738451Smsmith 57838451Smsmith/* 57938451Smsmith * Lookup path segment 58038451Smsmith */ 58138451Smsmithstatic int 58238451Smsmithlookup(DOS_FS *fs, u_int clus, const char *name, DOS_DE **dep) 58338451Smsmith{ 58438451Smsmith static DOS_DIR dir[DEPSEC]; 58538451Smsmith u_char lfn[261]; 58638451Smsmith u_char sfn[13]; 58738451Smsmith u_int nsec, lsec, xdn, chk, sec, ent, x; 588344376Skevans int err, ok; 58938451Smsmith 59038451Smsmith if (!clus) 59138451Smsmith for (ent = 0; ent < 2; ent++) 59238451Smsmith if (!strcasecmp(name, dotstr[ent])) { 59338451Smsmith *dep = dot + ent; 594313355Stsoome return (0); 59538451Smsmith } 59638451Smsmith if (!clus && fs->fatsz == 32) 59738451Smsmith clus = fs->rdcl; 59838451Smsmith nsec = !clus ? entsec(fs->dirents) : fs->spc; 59938451Smsmith lsec = 0; 60038451Smsmith xdn = chk = 0; 60138451Smsmith for (;;) { 60238451Smsmith if (!clus && !lsec) 60338451Smsmith lsec = fs->lsndir; 60438451Smsmith else if (okclus(fs, clus)) 60538451Smsmith lsec = blklsn(fs, clus); 60638451Smsmith else 607313355Stsoome return (EINVAL); 60838451Smsmith for (sec = 0; sec < nsec; sec++) { 609313355Stsoome if ((err = ioget(fs->fd, lsec + sec, dir, secbyt(1)))) 610313355Stsoome return (err); 61138451Smsmith for (ent = 0; ent < DEPSEC; ent++) { 61238451Smsmith if (!*dir[ent].de.name) 613313355Stsoome return (ENOENT); 61446079Simp if (*dir[ent].de.name != 0xe5) { 61538451Smsmith if ((dir[ent].de.attr & FA_MASK) == FA_XDE) { 61638451Smsmith x = dir[ent].xde.seq; 61738451Smsmith if (x & 0x40 || (x + 1 == xdn && 61838451Smsmith dir[ent].xde.chk == chk)) { 61938451Smsmith if (x & 0x40) { 62038451Smsmith chk = dir[ent].xde.chk; 62138451Smsmith x &= ~0x40; 62238451Smsmith } 62338451Smsmith if (x >= 1 && x <= 20) { 62438451Smsmith cp_xdnm(lfn, &dir[ent].xde); 62538451Smsmith xdn = x; 62638451Smsmith continue; 62738451Smsmith } 62838451Smsmith } 62938451Smsmith } else if (!(dir[ent].de.attr & FA_LABEL)) { 63038451Smsmith if ((ok = xdn == 1)) { 631298230Sallanjude x = dos_checksum(dir[ent].de.name, dir[ent].de.ext); 63238451Smsmith ok = chk == x && 63338451Smsmith !strcasecmp(name, (const char *)lfn); 63438451Smsmith } 63538451Smsmith if (!ok) { 63638451Smsmith cp_sfn(sfn, &dir[ent].de); 63738451Smsmith ok = !strcasecmp(name, (const char *)sfn); 63838451Smsmith } 63938451Smsmith if (ok) { 64038451Smsmith *dep = &dir[ent].de; 641313355Stsoome return (0); 64238451Smsmith } 64338451Smsmith } 64446079Simp } 64538451Smsmith xdn = 0; 64638451Smsmith } 64738451Smsmith } 64838451Smsmith if (!clus) 64938451Smsmith break; 65038451Smsmith if ((err = fatget(fs, &clus))) 651313355Stsoome return (err); 65238451Smsmith if (fatend(fs->fatsz, clus)) 65338451Smsmith break; 65438451Smsmith } 655313355Stsoome return (ENOENT); 65638451Smsmith} 65738451Smsmith 65838451Smsmith/* 65938451Smsmith * Copy name from extended directory entry 66038451Smsmith */ 66138451Smsmithstatic void 66238451Smsmithcp_xdnm(u_char *lfn, DOS_XDE *xde) 66338451Smsmith{ 66438451Smsmith static struct { 66538451Smsmith u_int off; 66638451Smsmith u_int dim; 66738451Smsmith } ix[3] = { 66838451Smsmith {offsetof(DOS_XDE, name1), sizeof(xde->name1) / 2}, 66938451Smsmith {offsetof(DOS_XDE, name2), sizeof(xde->name2) / 2}, 67038451Smsmith {offsetof(DOS_XDE, name3), sizeof(xde->name3) / 2} 67138451Smsmith }; 67238451Smsmith u_char *p; 67338451Smsmith u_int n, x, c; 67438451Smsmith 67538451Smsmith lfn += 13 * ((xde->seq & ~0x40) - 1); 67638451Smsmith for (n = 0; n < 3; n++) 67738451Smsmith for (p = (u_char *)xde + ix[n].off, x = ix[n].dim; x; 67838451Smsmith p += 2, x--) { 67938451Smsmith if ((c = cv2(p)) && (c < 32 || c > 127)) 68038451Smsmith c = '?'; 68138451Smsmith if (!(*lfn++ = c)) 68238451Smsmith return; 68338451Smsmith } 68438451Smsmith if (xde->seq & 0x40) 68538451Smsmith *lfn = 0; 68638451Smsmith} 68738451Smsmith 68838451Smsmith/* 68938451Smsmith * Copy short filename 69038451Smsmith */ 69138451Smsmithstatic void 69238451Smsmithcp_sfn(u_char *sfn, DOS_DE *de) 69338451Smsmith{ 69438451Smsmith u_char *p; 69538451Smsmith int j, i; 69638451Smsmith 69738451Smsmith p = sfn; 69838451Smsmith if (*de->name != ' ') { 69938451Smsmith for (j = 7; de->name[j] == ' '; j--); 70038451Smsmith for (i = 0; i <= j; i++) 70138451Smsmith *p++ = de->name[i]; 70238451Smsmith if (*de->ext != ' ') { 70338451Smsmith *p++ = '.'; 70438451Smsmith for (j = 2; de->ext[j] == ' '; j--); 70538451Smsmith for (i = 0; i <= j; i++) 70638451Smsmith *p++ = de->ext[i]; 70738451Smsmith } 70838451Smsmith } 70938451Smsmith *p = 0; 71038451Smsmith if (*sfn == 5) 71138451Smsmith *sfn = 0xe5; 71238451Smsmith} 71338451Smsmith 71438451Smsmith/* 71538582Srnordier * Return size of file in bytes 71638582Srnordier */ 71738582Srnordierstatic off_t 71838582Srnordierfsize(DOS_FS *fs, DOS_DE *de) 71938582Srnordier{ 72038582Srnordier u_long size; 72138582Srnordier u_int c; 72238582Srnordier int n; 72338582Srnordier 72446079Simp if (!(size = cv4(de->size)) && de->attr & FA_DIR) { 72538582Srnordier if (!(c = cv2(de->clus))) 72638582Srnordier size = fs->dirents * sizeof(DOS_DE); 72738582Srnordier else { 72838582Srnordier if ((n = fatcnt(fs, c)) == -1) 729313355Stsoome return (n); 73038582Srnordier size = blkbyt(fs, n); 73138582Srnordier } 73246079Simp } 733313355Stsoome return (size); 73438582Srnordier} 73538582Srnordier 73638582Srnordier/* 73738582Srnordier * Count number of clusters in chain 73838582Srnordier */ 73938582Srnordierstatic int 74038582Srnordierfatcnt(DOS_FS *fs, u_int c) 74138582Srnordier{ 74238582Srnordier int n; 74338582Srnordier 74438582Srnordier for (n = 0; okclus(fs, c); n++) 74538582Srnordier if (fatget(fs, &c)) 746313355Stsoome return (-1); 747313355Stsoome return (fatend(fs->fatsz, c) ? n : -1); 74838582Srnordier} 74938582Srnordier 75038582Srnordier/* 751329100Skevans * Get next cluster in cluster chain. Use in core fat cache unless 752329100Skevans * the number of current 128K block in FAT has changed. 75338451Smsmith */ 75438451Smsmithstatic int 75538451Smsmithfatget(DOS_FS *fs, u_int *c) 75638451Smsmith{ 757329100Skevans u_int val_in, val_out, offset, blknum, nbyte; 758329100Skevans const u_char *p_entry; 759329100Skevans int err; 76038451Smsmith 761329100Skevans /* check input value to prevent overflow in fatoff() */ 762329100Skevans val_in = *c; 763329100Skevans if (val_in & 0xf0000000) 764329100Skevans return (EINVAL); 765298230Sallanjude 766329100Skevans /* ensure that current 128K FAT block is cached */ 767329100Skevans offset = fatoff(fs->fatsz, val_in); 768329100Skevans nbyte = fs->fatsz != 32 ? 2 : 4; 769329100Skevans if (offset + nbyte > secbyt(fs->spf)) 770329100Skevans return (EINVAL); 771329100Skevans blknum = offset / FATBLKSZ; 772329100Skevans offset %= FATBLKSZ; 773329100Skevans if (offset + nbyte > FATBLKSZ) 774329100Skevans return (EINVAL); 775329100Skevans if (blknum != fs->fatbuf_blknum) { 776329100Skevans err = dos_read_fatblk(fs, fs->fd, blknum); 777329100Skevans if (err != 0) 778329100Skevans return (err); 779298230Sallanjude } 780329100Skevans p_entry = fs->fatbuf + offset; 781298230Sallanjude 782329100Skevans /* extract cluster number from FAT entry */ 783329100Skevans switch (fs->fatsz) { 784329100Skevans case 32: 785329100Skevans val_out = cv4(p_entry); 786329100Skevans val_out &= 0x0fffffff; 787329100Skevans break; 788329100Skevans case 16: 789329100Skevans val_out = cv2(p_entry); 790329100Skevans break; 791329100Skevans case 12: 792329100Skevans val_out = cv2(p_entry); 793329100Skevans if (val_in & 1) 794329100Skevans val_out >>= 4; 795329100Skevans else 796329100Skevans val_out &= 0xfff; 797329100Skevans break; 798329100Skevans default: 799329100Skevans return (EINVAL); 800329100Skevans } 801329100Skevans *c = val_out; 802298230Sallanjude return (0); 80338451Smsmith} 80438451Smsmith 80538451Smsmith/* 80638451Smsmith * Is cluster an end-of-chain marker? 80738451Smsmith */ 80838451Smsmithstatic int 80938451Smsmithfatend(u_int sz, u_int c) 81038451Smsmith{ 811313355Stsoome return (c > (sz == 12 ? 0xff7U : sz == 16 ? 0xfff7U : 0xffffff7)); 81238451Smsmith} 81338451Smsmith 81438451Smsmith/* 81538451Smsmith * Offset-based I/O primitive 81638451Smsmith */ 81738451Smsmithstatic int 818329100Skevansioread(DOS_FS *fs, u_int offset, void *buf, size_t nbyte) 81938451Smsmith{ 82038451Smsmith char *s; 82138451Smsmith u_int off, n; 82238451Smsmith int err; 823313355Stsoome u_char local_buf[SECSIZ]; 82438451Smsmith 82538451Smsmith s = buf; 82638451Smsmith if ((off = offset & (SECSIZ - 1))) { 82738451Smsmith offset -= off; 828298230Sallanjude if ((n = SECSIZ - off) > nbyte) 829298230Sallanjude n = nbyte; 830313355Stsoome if ((err = ioget(fs->fd, bytsec(offset), local_buf, sizeof(local_buf)))) 831313355Stsoome return (err); 832313355Stsoome memcpy(s, local_buf + off, n); 83338451Smsmith offset += SECSIZ; 83438451Smsmith s += n; 83538451Smsmith nbyte -= n; 83638451Smsmith } 83738451Smsmith n = nbyte & (SECSIZ - 1); 83838451Smsmith if (nbyte -= n) { 839313355Stsoome if ((err = ioget(fs->fd, bytsec(offset), s, nbyte))) 840313355Stsoome return (err); 84138451Smsmith offset += nbyte; 84238451Smsmith s += nbyte; 84338451Smsmith } 84438451Smsmith if (n) { 845313355Stsoome if ((err = ioget(fs->fd, bytsec(offset), local_buf, sizeof(local_buf)))) 846313355Stsoome return (err); 847313355Stsoome memcpy(s, local_buf, n); 84838451Smsmith } 849313355Stsoome return (0); 85038451Smsmith} 85138451Smsmith 85238451Smsmith/* 85338451Smsmith * Sector-based I/O primitive 85438451Smsmith */ 85538451Smsmithstatic int 856329100Skevansioget(struct open_file *fd, daddr_t lsec, void *buf, size_t size) 85738451Smsmith{ 858329100Skevans size_t rsize; 859329100Skevans int rv; 860329100Skevans 861329100Skevans /* Make sure we get full read or error. */ 862329100Skevans rsize = 0; 863329100Skevans rv = (fd->f_dev->dv_strategy)(fd->f_devdata, F_READ, lsec, 864329100Skevans size, buf, &rsize); 865329100Skevans if ((rv == 0) && (size != rsize)) 866329100Skevans rv = EIO; 867329100Skevans return (rv); 86838451Smsmith} 869