diff options
author | jkh <jkh@FreeBSD.org> | 1998-03-03 04:29:01 +0000 |
---|---|---|
committer | jkh <jkh@FreeBSD.org> | 1998-03-03 04:29:01 +0000 |
commit | d1ab9135f210e56e4a1b5104d638e7f9976b7930 (patch) | |
tree | 31c478b6a0196ca78ec4c1f8e5cc48b4d9008998 /release/sysinstall/dosio.c | |
parent | 8acfb4f2c51b23fb6afd9a5460c3e5747c9f4a82 (diff) | |
download | FreeBSD-src-d1ab9135f210e56e4a1b5104d638e7f9976b7930.zip FreeBSD-src-d1ab9135f210e56e4a1b5104d638e7f9976b7930.tar.gz |
Update stand-alone DOS I/O routines to be able to read from FAT32/VFAT
partitions. This should allow sysinstall to function properly in all
Win95 installation based scenarios now.
Submitted by: Robert Nordier <rnordier@iafrica.com>
Diffstat (limited to 'release/sysinstall/dosio.c')
-rw-r--r-- | release/sysinstall/dosio.c | 949 |
1 files changed, 445 insertions, 504 deletions
diff --git a/release/sysinstall/dosio.c b/release/sysinstall/dosio.c index eb94214..68a7336 100644 --- a/release/sysinstall/dosio.c +++ b/release/sysinstall/dosio.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996 Robert Nordier + * Copyright (c) 1996, 1998 Robert Nordier * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -8,30 +8,30 @@ * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <sys/types.h> -#include <sys/stat.h> #include <unistd.h> #include <fcntl.h> +#include <stddef.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> -#include <ctype.h> #include <string.h> #include "dosio.h" @@ -39,71 +39,73 @@ #define SSHIFT 9 /* SECSIZ shift */ #define DEPSEC 16 /* directory entries per sector */ #define DSHIFT 4 /* DEPSEC shift */ -#define NFATS 2 /* number of FATs */ -#define DENMSZ 8 /* DE name size */ -#define DEXTSZ 3 /* DE extension size */ -#define DENXSZ 11 /* DE name + extension size */ #define LOCLUS 2 /* lowest cluster number */ /* DOS "BIOS Parameter Block" */ typedef struct { - u_char secsiz[2]; /* sector size */ - u_char spc; /* sectors per cluster */ - u_char ressec[2]; /* reserved sectors */ - u_char fats; /* FATs */ - u_char dirents[2]; /* root directory entries */ - u_char secs[2]; /* total sectors */ - u_char media; /* media descriptor */ - u_char spf[2]; /* sectors per FAT */ - u_char spt[2]; /* sectors per track */ - u_char heads[2]; /* drive heads */ - u_char hidsec[4]; /* hidden sectors */ - u_char lsecs[4]; /* huge sectors */ + u_char secsiz[2]; /* sector size */ + u_char spc; /* sectors per cluster */ + u_char ressec[2]; /* reserved sectors */ + u_char fats; /* FATs */ + u_char dirents[2]; /* root directory entries */ + u_char secs[2]; /* total sectors */ + u_char media; /* media descriptor */ + u_char spf[2]; /* sectors per FAT */ + u_char spt[2]; /* sectors per track */ + u_char heads[2]; /* drive heads */ + u_char hidsec[4]; /* hidden sectors */ + u_char lsecs[4]; /* huge sectors */ + u_char lspf[4]; /* huge sectors per FAT */ + u_char xflg[2]; /* flags */ + u_char vers[2]; /* filesystem version */ + u_char rdcl[4]; /* root directory start cluster */ + u_char infs[2]; /* filesystem info sector */ + u_char bkbs[2]; /* backup boot sector */ } DOS_BPB; -/* Fixed portion of DOS boot sector */ +/* Initial portion of DOS boot sector */ typedef struct { - u_char jmp[3]; /* usually 80x86 'jmp' opcode */ - u_char oem[8]; /* OEM name and version */ - DOS_BPB bpb; /* BPB */ - u_char drive; /* drive number */ - u_char reserved; /* reserved */ - u_char extsig; /* extended boot signature */ - u_char volid[4]; /* volume ID */ - u_char label[11]; /* volume label */ - u_char fstype[8]; /* file system type */ + u_char jmp[3]; /* usually 80x86 'jmp' opcode */ + u_char oem[8]; /* OEM name and version */ + DOS_BPB bpb; /* BPB */ } DOS_BS; /* Supply missing "." and ".." root directory entries */ +static const char *const dotstr[2] = {".", ".."}; static DOS_DE dot[2] = { - {". ", " ", FA_DIR, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0} }, - {".. ", " ", FA_DIR, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0} } + {". ", " ", FA_DIR, {0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, + {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}}, + {".. ", " ", FA_DIR, {0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, + {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}} }; -/* I/O error handler address */ -int (*dos_ioerr)(int op) = NULL; - /* The usual conversion macros to avoid multiplication and division */ -#define bytsec(n) ((n) >> (SSHIFT)) -#define secbyt(s) ((u_long)(s) << (SSHIFT)) -#define entsec(e) ((e) >> (DSHIFT)) +#define bytsec(n) ((n) >> SSHIFT) +#define secbyt(s) ((s) << SSHIFT) +#define entsec(e) ((e) >> DSHIFT) #define bytblk(fs, n) ((n) >> (fs)->bshift) -#define blkbyt(fs, b) ((u_long)(b) << (fs)->bshift) -#define blksec(fs, b) ((u_long)(b) << ((fs)->bshift - (SSHIFT))) +#define blkbyt(fs, b) ((b) << (fs)->bshift) +#define secblk(fs, s) ((s) >> ((fs)->bshift - SSHIFT)) +#define blksec(fs, b) ((b) << ((fs)->bshift - SSHIFT)) /* Convert cluster number to offset within filesystem */ -#define blkoff(fs, b) secbyt((fs)->lsndta) + blkbyt(fs, (b) - (LOCLUS)) +#define blkoff(fs, b) (secbyt((fs)->lsndta) + blkbyt(fs, (b) - LOCLUS)) /* Convert cluster number to logical sector number */ -#define blklsn(fs, b) ((fs)->lsndta + blksec(fs, (b) - (LOCLUS))) +#define blklsn(fs, b) ((fs)->lsndta + blksec(fs, (b) - LOCLUS)) /* Convert cluster number to offset within FAT */ -#define fatoff(fat12, c) ((u_long)(c) + ((fat12) ? (c) >> 1 : (c))) +#define fatoff(sz, c) ((sz) == 12 ? (c) + ((c) >> 1) : \ + (sz) == 16 ? (c) << 1 : \ + (c) << 2) /* Does cluster number reference a valid data cluster? */ -#define okclus(fs, c) ((c) >= (LOCLUS) && (c) <= (fs)->xclus) +#define okclus(fs, c) ((c) >= LOCLUS && (c) <= (fs)->xclus) + +/* Get start cluster from directory entry */ +#define stclus(sz, de) ((sz) != 32 ? cv2((de)->clus) : \ + ((u_int)cv2((de)->dex.h_clus) << 16) | \ + cv2((de)->clus)) /* Return on error */ #define RETERR(err) { \ @@ -111,47 +113,39 @@ int (*dos_ioerr)(int op) = NULL; return -1; \ } -static int dosunmount(DOS_FS *fs); -static int dosstat(DOS_FS *fs, DOS_DE *de, struct stat *sb); -static int parsebs(DOS_FS *fs, DOS_BS *bs); -static int namede(DOS_FS *fs, const char *path, DOS_DE **dep); -static DOS_DE *lookup(DOS_FS *fs, unsigned c, const u_char *nx, int *err); -static off_t fsize(DOS_FS *fs, DOS_DE *de); -static u_short fatget(DOS_FS *fs, u_short c); -static int fatend(int fat12, u_short c); -static int fatcnt(DOS_FS *fs, u_short c); -static int ioread(int fd, off_t offset, void *buf, size_t nbytes); -static int ioget(int fd, u_long lsec, void *buf, u_int nsec); -static u_char *nxname(const char *name, char **endptr); -static int sepchar(int c); -static int wildchar(int c); -static int doschar(int c); +static int dosunmount(DOS_FS *); +static int dosread(void *, char *, int); +static fpos_t dosseek(void *, fpos_t, int); +static int dosclose(void *); +static int parsebs(DOS_FS *, DOS_BS *); +static int namede(DOS_FS *, const char *, DOS_DE **); +static int lookup(DOS_FS *, u_int, const char *, DOS_DE **); +static void cp_xdnm(u_char *, DOS_XDE *); +static void cp_sfn(u_char *, DOS_DE *); +static int fatget(DOS_FS *, u_int *); +static int fatend(u_int, u_int); +static int ioread(DOS_FS *, u_int, void *, u_int); +static int iobuf(DOS_FS *, u_int); +static int ioget(int, u_int, void *, u_int); /* * Mount DOS filesystem */ int -dos_mount(DOS_FS *fs, const char *devname) +dos_mount(DOS_FS *fs, const char *dname) { - char buf[SECSIZ]; - int err; - - memset(fs, 0, sizeof(DOS_FS)); - if ((fs->fd = open(devname, O_RDONLY)) == -1) - RETERR(errno); - if (!(err = ioget(fs->fd, 0, buf, 1)) && - !(err = parsebs(fs, (DOS_BS *)buf))) - if (!(fs->fat = malloc(secbyt(fs->spf)))) - err = errno; - else - err = ioget(fs->fd, fs->lsnfat, fs->fat, fs->spf); - if (err) { - dosunmount(fs); - RETERR(err); - } - fs->bsize = secbyt(fs->spc); - fs->bshift = ffs(fs->bsize) - 1; - return 0; + int err; + + memset(fs, 0, sizeof(DOS_FS)); + if ((fs->fd = open(dname, O_RDONLY)) == -1) + return -1; + if ((err = !(fs->buf = malloc(SECSIZ)) ? errno : 0) || + (err = ioget(fs->fd, 0, fs->buf, 1)) || + (err = parsebs(fs, (DOS_BS *)fs->buf))) { + (void)dosunmount(fs); + RETERR(err); + } + return 0; } /* @@ -160,233 +154,146 @@ dos_mount(DOS_FS *fs, const char *devname) int dos_unmount(DOS_FS *fs) { - int err; + int err; - if (fs->links) - RETERR(EBUSY); - if ((err = dosunmount(fs))) - RETERR(err); - return 0; + if (fs->links) + RETERR(EBUSY); + if ((err = dosunmount(fs))) + RETERR(err); + return 0; } /* * Common code shared by dos_mount() and dos_unmount() */ -static -int dosunmount(DOS_FS *fs) +static int +dosunmount(DOS_FS *fs) { - if (fs->fat) - free(fs->fat); - return close(fs->fd) ? errno : 0; + if (fs->buf) + free(fs->buf); + return close(fs->fd) ? errno : 0; } /* - * Determine free data space in filesystem (in bytes) + * Open DOS file */ -u_long -dos_free(DOS_FS *fs) +FILE * +dos_open(DOS_FS *fs, const char *path) { - unsigned n, c; - - n = 0; - for (c = LOCLUS; c <= fs->xclus; c++) - if (!fatget(fs, c)) - n++; - return blkbyt(fs, n); + DOS_DE *de; + DOS_FILE *f; + u_int size, clus; + int err; + + if ((err = namede(fs, path, &de))) { + errno = err; + return NULL; + } + if (de->attr & FA_DIR) { + errno = EISDIR; + return NULL; + } + clus = stclus(fs->fatsz, de); + size = cv4(de->size); + if (!clus ^ !size || (clus && !okclus(fs, clus))) { + errno = EINVAL; + return NULL; + } + f = malloc(sizeof(DOS_FILE)); + memset(f, 0, sizeof(DOS_FILE)); + f->fs = fs; + fs->links++; + f->de = *de; + return funopen(f, dosread, NULL, dosseek, dosclose); } /* - * Close open file + * Read from file */ -int -dos_close(void *v) +static int +dosread(void *v, char *buf, int nbyte) { + u_int nb, off, clus, c, cnt, n; + int err; DOS_FILE *f = v; - f->fs->links--; - free(f); - return 0; + nb = (u_int)nbyte; + if (nb > (n = cv4(f->de.size) - f->offset)) + nb = n; + off = f->offset; + if ((clus = stclus(f->fs->fatsz, &f->de))) + off &= f->fs->bsize - 1; + c = f->c; + cnt = nb; + while (cnt) { + n = 0; + if (!c) { + if ((c = clus)) + n = bytblk(f->fs, f->offset); + } else if (!off) + n++; + while (n--) { + if ((err = fatget(f->fs, &c))) + RETERR(err); + if (!okclus(f->fs, c)) + RETERR(EINVAL); + } + if (!clus || (n = f->fs->bsize - off) > cnt) + n = cnt; + if ((err = ioread(f->fs, blkoff(f->fs, c) + off, buf, n))) + RETERR(err); + f->offset += n; + f->c = c; + off = 0; + buf += n; + cnt -= n; + } + return (int)nb; } /* * Reposition with file */ -fpos_t -dos_seek(void *v, fpos_t offset, int whence) +static fpos_t +dosseek(void *v, fpos_t offset, int whence) { - off_t off; - u_long size; - DOS_FILE *f = v; - - size = cv4(f->de.size); - switch (whence) { - case SEEK_SET: - off = 0; - break; - case SEEK_CUR: - off = f->offset; - break; - case SEEK_END: - off = size; - break; - default: - RETERR(EINVAL); - } - off += offset; - if (off < 0 || off > size) - RETERR(EINVAL); - f->offset = off; - f->c = 0; - return 0; -} - -/* - * Read from file - */ -int -dos_read(void *v, char *buf, int nbytes) -{ - off_t size; - u_long off, cnt, n; - unsigned clus, c; - int err; - DOS_FILE *f = v; - - if ((size = fsize(f->fs, &f->de)) == -1) - RETERR(EBADFS); - if (nbytes > (n = size - f->offset)) - nbytes = n; - off = f->offset; - if ((clus = cv2(f->de.clus))) - off &= f->fs->bsize - 1; - c = f->c; - cnt = nbytes; - while (cnt) { - n = 0; - if (!c) { - if ((c = clus)) - n = bytblk(f->fs, f->offset); - } else if (!off) - n++; - while (n--) { - c = fatget(f->fs, c); - if (!okclus(f->fs, c)) - RETERR(EBADFS); - } - if (!clus || (n = f->fs->bsize - off) > cnt) - n = cnt; - if ((err = ioread(f->fs->fd, (c ? blkoff(f->fs, c) : - secbyt(f->fs->lsndir)) + off, - buf, n))) - RETERR(err); - f->offset += n; - f->c = c; - off = 0; - buf += n; - cnt -= n; - } - return nbytes; -} - -/* - * Get file status - */ -int -dos_stat(DOS_FS *fs, const char *path, struct stat *sb) -{ - DOS_DE *de; - int err; - - if ((err = namede(fs, path, &de)) || (err = dosstat(fs, de, sb))) - RETERR(err); - return 0; -} - -/* - * Get file status of open file - */ -int -dos_fstat(DOS_FILE *f, struct stat *sb) -{ - int err; + off_t off; + u_int size; + DOS_FILE *f = v; - if ((err = dosstat(f->fs, &f->de, sb))) - RETERR(err); - return 0; + size = cv4(f->de.size); + switch (whence) { + case SEEK_SET: + off = 0; + break; + case SEEK_CUR: + off = f->offset; + break; + case SEEK_END: + off = size; + break; + default: + RETERR(EINVAL); + } + off += offset; + if (off < 0 || off > size) + RETERR(EINVAL); + f->offset = (u_int)off; + f->c = 0; + return off; } /* - * File status primitive + * Close open file */ static int -dosstat(DOS_FS *fs, DOS_DE *de, struct stat *sb) +dosclose(void *v) { + DOS_FILE *f = v; - memset(sb, 0, sizeof(struct stat)); - sb->st_mode = (de->attr & FA_DIR) ? S_IFDIR | 0777 : S_IFREG | 0666; - if (de->attr & FA_RDONLY) - sb->st_mode &= ~0222; - if (de->attr & FA_HIDDEN) - sb->st_mode &= ~0007; - if (de->attr & FA_SYSTEM) - sb->st_mode &= ~0077; - sb->st_nlink = 1; - dos_cvtime(&sb->st_atime, cv2(de->date), cv2(de->time)); - sb->st_mtime = sb->st_atime; - sb->st_ctime = sb->st_atime; - if ((sb->st_size = fsize(fs, de)) == -1) - return EBADFS; - if (!(de->attr & FA_DIR) || cv2(de->clus)) - sb->st_blocks = bytblk(fs, sb->st_size + fs->bsize - 1); - sb->st_blksize = fs->bsize; - return 0; -} - -/* - * Convert from DOS date and time - */ -void -dos_cvtime(time_t *timer, u_short ddate, u_short dtime) -{ - struct tm tm; - - memset(&tm, 0, sizeof(tm)); - tm.tm_sec = (dtime & 0x1f) << 1; - tm.tm_min = dtime >> 5 & 0x3f; - tm.tm_hour = dtime >> 11; - tm.tm_mday = ddate & 0x1f; - tm.tm_mon = (ddate >> 5 & 0xf) - 1; - tm.tm_year = 80 + (ddate >> 9); - *timer = mktime(&tm); -} - -/* - * Open DOS file - */ -FILE * -dos_open(DOS_FS *fs, const char *path) -{ - DOS_DE *de; - DOS_FILE *f; - u_long size; - u_int clus; - int err; - FILE *fp; - - if ((err = namede(fs, path, &de))) - return NULL; - clus = cv2(de->clus); - size = cv4(de->size); - if ((clus && (!okclus(fs, clus) || (!(de->attr & FA_DIR) && !size))) || - (!clus && !(de->attr & FA_DIR) && size)) - return NULL; - f = (DOS_FILE *)malloc(sizeof(DOS_FILE)); - memset(f, 0, sizeof(DOS_FILE)); - f->fs = fs; - fs->links++; - f->de = *de; - fp = funopen(f, dos_read, NULL, dos_seek, dos_close); - return fp; + f->fs->links--; + free(f); + return 0; } /* @@ -395,35 +302,47 @@ dos_open(DOS_FS *fs, const char *path) static int parsebs(DOS_FS *fs, DOS_BS *bs) { - u_long sc; - - if ((bs->jmp[0] != 0xe9 && (bs->jmp[0] != 0xeb || bs->jmp[2] != 0x90)) || - bs->bpb.media < 0xf0 || - cv2(bs->bpb.secsiz) != SECSIZ || - !bs->bpb.spc || (bs->bpb.spc ^ (bs->bpb.spc - 1)) < bs->bpb.spc) - return EINVAL; - fs->spf = cv2(bs->bpb.spf); - fs->dirents = cv2(bs->bpb.dirents); - fs->spc = bs->bpb.spc; - sc = cv2(bs->bpb.secs); - if (!sc && bs->extsig == 0x29) - sc = cv4(bs->bpb.lsecs); - if (!sc || bs->bpb.fats != NFATS || bs->bpb.spc > 64) - return EINVAL; - if (!fs->dirents || fs->dirents & (DEPSEC - 1)) - return EINVAL; - fs->lsnfat = cv2(bs->bpb.ressec); - fs->lsndir = fs->lsnfat + (u_long)fs->spf * NFATS; - fs->lsndta = fs->lsndir + entsec(fs->dirents); - if (fs->lsndta > sc || !(sc = (sc - fs->lsndta) / fs->spc) || sc >= 0xfff6) - return EINVAL; - fs->fat12 = sc < 0xff6; - fs->xclus = sc + 1; - if (fs->spf < bytsec(fatoff(fs->fat12, fs->xclus) + SECSIZ)) - return EINVAL; - if (bs->extsig == 0x29) - fs->volid = cv4(bs->volid); - return 0; + u_int sc; + + if ((bs->jmp[0] != 0x69 && + bs->jmp[0] != 0xe9 && + (bs->jmp[0] != 0xeb || bs->jmp[2] != 0x90)) || + bs->bpb.media < 0xf0) + return EINVAL; + if (cv2(bs->bpb.secsiz) != SECSIZ) + return EINVAL; + if (!(fs->spc = bs->bpb.spc) || fs->spc & (fs->spc - 1)) + return EINVAL; + fs->bsize = secbyt(fs->spc); + fs->bshift = ffs(fs->bsize) - 1; + if ((fs->spf = cv2(bs->bpb.spf))) { + if (bs->bpb.fats != 2) + return EINVAL; + if (!(fs->dirents = cv2(bs->bpb.dirents))) + return EINVAL; + } else { + if (!(fs->spf = cv4(bs->bpb.lspf))) + return EINVAL; + if (!bs->bpb.fats || bs->bpb.fats > 16) + return EINVAL; + if ((fs->rdcl = cv4(bs->bpb.rdcl)) < LOCLUS) + return EINVAL; + } + if (!(fs->lsnfat = cv2(bs->bpb.ressec))) + return EINVAL; + fs->lsndir = fs->lsnfat + fs->spf * bs->bpb.fats; + fs->lsndta = fs->lsndir + entsec(fs->dirents); + if (!(sc = cv2(bs->bpb.secs)) && !(sc = cv4(bs->bpb.lsecs))) + return EINVAL; + if (fs->lsndta > sc) + return EINVAL; + if ((fs->xclus = secblk(fs, sc - fs->lsndta) + 1) < LOCLUS) + return EINVAL; + fs->fatsz = fs->dirents ? fs->xclus < 0xff6 ? 12 : 16 : 32; + sc = (secbyt(fs->spf) << 1) / (fs->fatsz >> 2) - 1; + if (fs->xclus > sc) + fs->xclus = sc; + return 0; } /* @@ -432,243 +351,265 @@ parsebs(DOS_FS *fs, DOS_BS *bs) static int namede(DOS_FS *fs, const char *path, DOS_DE **dep) { - DOS_DE *de; - u_char *nx; - int err; - - err = 0; - de = dot; - if (*path == '/') - path++; - while (*path) { - if (!(nx = nxname(path, (char **)&path))) - return EINVAL; - if (!(de->attr & FA_DIR)) - return ENOTDIR; - if (!(de = lookup(fs, cv2(de->clus), nx, &err))) - return err ? err : ENOENT; - if (*path == '/') - path++; - } - *dep = de; - return 0; + char name[256]; + DOS_DE *de; + char *s; + size_t n; + int err; + + err = 0; + de = dot; + if (*path == '/') + path++; + while (*path) { + if (!(s = strchr(path, '/'))) + s = strchr(path, 0); + if ((n = s - path) > 255) + return ENAMETOOLONG; + memcpy(name, path, n); + name[n] = 0; + path = s; + if (!(de->attr & FA_DIR)) + return ENOTDIR; + if ((err = lookup(fs, stclus(fs->fatsz, de), name, &de))) + return err; + if (*path == '/') + path++; + } + *dep = de; + return 0; } /* * Lookup path segment */ -static DOS_DE * -lookup(DOS_FS *fs, unsigned c, const u_char *nx, int *err) +static int +lookup(DOS_FS *fs, u_int clus, const char *name, DOS_DE **dep) { - static DOS_DE dir[DEPSEC]; - u_long lsec; - u_int nsec; - int s, e; - - if (!c) - for (e = 0; e < 2; e++) - if (!memcmp(dot + e, nx, DENXSZ)) - return dot + e; - nsec = !c ? entsec(fs->dirents) : fs->spc; - lsec = 0; - do { - if (!c && !lsec) - lsec = fs->lsndir; - else if okclus(fs, c) - lsec = blklsn(fs, c); - else { - *err = EBADFS; - return NULL; - } - for (s = 0; s < nsec; s++) { - if ((e = ioget(fs->fd, lsec + s, dir, 1))) { - *err = e; - return NULL; - } - for (e = 0; e < DEPSEC; e++) { - if (!*dir[e].name) - return NULL; - if (*dir[e].name == 0xe5 || dir[e].attr & FA_LABEL) - continue; - if (!memcmp(dir + e, nx, DENXSZ)) - return dir + e; - } - } - } while (c && !fatend(fs->fat12, c = fatget(fs, c))); - return NULL; + static DOS_DIR dir[DEPSEC]; + u_char lfn[261]; + u_char sfn[13]; + u_int nsec, lsec, xdn, chk, sec, ent, x; + int err, ok, i; + + if (!clus) + for (ent = 0; ent < 2; ent++) + if (!strcasecmp(name, dotstr[ent])) { + *dep = dot + ent; + return 0; + } + if (!clus && fs->fatsz == 32) + clus = fs->rdcl; + nsec = !clus ? entsec(fs->dirents) : fs->spc; + lsec = 0; + xdn = chk = 0; + for (;;) { + if (!clus && !lsec) + lsec = fs->lsndir; + else if (okclus(fs, clus)) + lsec = blklsn(fs, clus); + else + return EINVAL; + for (sec = 0; sec < nsec; sec++) { + if ((err = ioget(fs->fd, lsec + sec, dir, 1))) + return err; + for (ent = 0; ent < DEPSEC; ent++) { + if (!*dir[ent].de.name) + return ENOENT; + if (*dir[ent].de.name != 0xe5) + if ((dir[ent].de.attr & FA_MASK) == FA_XDE) { + x = dir[ent].xde.seq; + if (x & 0x40 || (x + 1 == xdn && + dir[ent].xde.chk == chk)) { + if (x & 0x40) { + chk = dir[ent].xde.chk; + x &= ~0x40; + } + if (x >= 1 && x <= 20) { + cp_xdnm(lfn, &dir[ent].xde); + xdn = x; + continue; + } + } + } else if (!(dir[ent].de.attr & FA_LABEL)) { + if ((ok = xdn == 1)) { + for (x = 0, i = 0; i < 11; i++) + x = ((((x & 1) << 7) | (x >> 1)) + + dir[ent].de.name[i]) & 0xff; + ok = chk == x && + !strcasecmp(name, (const char *)lfn); + } + if (!ok) { + cp_sfn(sfn, &dir[ent].de); + ok = !strcasecmp(name, (const char *)sfn); + } + if (ok) { + *dep = &dir[ent].de; + return 0; + } + } + xdn = 0; + } + } + if (!clus) + break; + if ((err = fatget(fs, &clus))) + return err; + if (fatend(fs->fatsz, clus)) + break; + } + return ENOENT; } /* - * Return size of file in bytes + * Copy name from extended directory entry */ -static off_t -fsize(DOS_FS *fs, DOS_DE *de) +static void +cp_xdnm(u_char *lfn, DOS_XDE *xde) { - u_long size; - u_int c; - int n; - - if (!(size = cv4(de->size)) && de->attr & FA_DIR) - if (!(c = cv2(de->clus))) - size = fs->dirents * sizeof(DOS_DE); - else { - if ((n = fatcnt(fs, c)) == -1) - return n; - size = blkbyt(fs, n); - } - return size; + static struct { + u_int off; + u_int dim; + } ix[3] = { + {offsetof(DOS_XDE, name1), sizeof(xde->name1) / 2}, + {offsetof(DOS_XDE, name2), sizeof(xde->name2) / 2}, + {offsetof(DOS_XDE, name3), sizeof(xde->name3) / 2} + }; + u_char *p; + u_int n, x, c; + + lfn += 13 * ((xde->seq & ~0x40) - 1); + for (n = 0; n < 3; n++) + for (p = (u_char *)xde + ix[n].off, x = ix[n].dim; x; + p += 2, x--) { + if ((c = cv2(p)) && (c < 32 || c > 127)) + c = '?'; + if (!(*lfn++ = c)) + return; + } + if (xde->seq & 0x40) + *lfn = 0; } /* - * Return next cluster in cluster chain + * Copy short filename */ -static u_short -fatget(DOS_FS *fs, u_short c) +static void +cp_sfn(u_char *sfn, DOS_DE *de) { - u_short x; - - x = cv2(fs->fat + fatoff(fs->fat12, c)); - return fs->fat12 ? c & 1 ? x >> 4 : x & 0xfff : x; + u_char *p; + int j, i; + + p = sfn; + if (*de->name != ' ') { + for (j = 7; de->name[j] == ' '; j--); + for (i = 0; i <= j; i++) + *p++ = de->name[i]; + if (*de->ext != ' ') { + *p++ = '.'; + for (j = 2; de->ext[j] == ' '; j--); + for (i = 0; i <= j; i++) + *p++ = de->ext[i]; + } + } + *p = 0; + if (*sfn == 5) + *sfn = 0xe5; } /* - * Count number of clusters in chain + * Get next cluster in cluster chain */ static int -fatcnt(DOS_FS *fs, u_short c) +fatget(DOS_FS *fs, u_int *c) { - int n; - - for (n = 0; okclus(fs, c); n++) - c = fatget(fs, c); - return fatend(fs->fat12, c) ? n : -1; + u_char buf[4]; + u_int x; + int err; + + err = ioread(fs, secbyt(fs->lsnfat) + fatoff(fs->fatsz, *c), buf, + fs->fatsz != 32 ? 2 : 4); + if (err) + return err; + x = fs->fatsz != 32 ? cv2(buf) : cv4(buf); + *c = fs->fatsz == 12 ? *c & 1 ? x >> 4 : x & 0xfff : x; + return 0; } /* * Is cluster an end-of-chain marker? */ static int -fatend(int fat12, u_short c) +fatend(u_int sz, u_int c) { - return c > (fat12 ? 0xff7 : 0xfff7) || c == 0xfff0; + return c > (sz == 12 ? 0xff7U : sz == 16 ? 0xfff7U : 0xffffff7); } /* * Offset-based I/O primitive */ static int -ioread(int fd, off_t offset, void *buf, size_t nbytes) +ioread(DOS_FS *fs, u_int offset, void *buf, u_int nbyte) { - char tmp[SECSIZ]; - u_int off, n; - int err; - - if ((off = offset & (SECSIZ - 1))) { - offset -= off; - if ((err = ioget(fd, bytsec(offset), tmp, 1))) - return err; - offset += SECSIZ; - if ((n = SECSIZ - off) > nbytes) - n = nbytes; - memcpy(buf, tmp + off, n); - buf += n; - nbytes -= n; - } - n = nbytes & (SECSIZ - 1); - if (nbytes -= n) { - if ((err = ioget(fd, bytsec(offset), buf, bytsec(nbytes)))) - return err; - offset += nbytes; - buf += nbytes; - } - if (n) { - if ((err = ioget(fd, bytsec(offset), tmp, 1))) - return err; - memcpy(buf, tmp, n); - } - return 0; -} - -/* - * Sector-based I/O primitive - */ -static int -ioget(int fd, u_long lsec, void *buf, u_int nsec) -{ - size_t nbytes; - ssize_t n; - - nbytes = secbyt(nsec); - do { - if (lseek(fd, secbyt(lsec), SEEK_SET) == -1) - return errno; - n = read(fd, buf, nbytes); - } while (n == -1 && errno == EIO && dos_ioerr && dos_ioerr(0)); - if (n != nbytes) - return n == -1 ? errno : EIO; - return 0; -} - -/* - * Convert name to DOS directory (name + extension) format - */ -static u_char * -nxname(const char *name, char **endptr) -{ - static u_char nx[DENXSZ]; - int i; - - memset(nx, ' ', sizeof(nx)); - for (i = 0; i < DENMSZ && doschar(*name); i++) - nx[i] = toupper(*name++); - if (i) { - if (i == DENMSZ) - while (!sepchar(*name)) - name++; - if (*name == '.') { - name++; - for (i = 0; i < DEXTSZ && doschar(*name); i++) - nx[DENMSZ + i] = toupper(*name++); - if (i == DEXTSZ) - while(!sepchar(*name)) - name++; - } - } else if (*name == '.') { - nx[0] = *name++; - if (*name == '.') - nx[1] = *name++; - } - if ((*name && *name != '/') || *nx == ' ') - return NULL; - if (*nx == 0xe5) - *nx = 5; - *endptr = (char *)name; - return nx; + char *s; + u_int off, n; + int err; + + s = buf; + if ((off = offset & (SECSIZ - 1))) { + offset -= off; + if ((err = iobuf(fs, bytsec(offset)))) + return err; + offset += SECSIZ; + if ((n = SECSIZ - off) > nbyte) + n = nbyte; + memcpy(s, fs->buf + off, n); + s += n; + nbyte -= n; + } + n = nbyte & (SECSIZ - 1); + if (nbyte -= n) { + if ((err = ioget(fs->fd, bytsec(offset), s, bytsec(nbyte)))) + return err; + offset += nbyte; + s += nbyte; + } + if (n) { + if ((err = iobuf(fs, bytsec(offset)))) + return err; + memcpy(s, fs->buf, n); + } + return 0; } /* - * Is character a path-separator? + * Buffered sector-based I/O primitive */ static int -sepchar(int c) +iobuf(DOS_FS *fs, u_int lsec) { - return !wildchar(c) && !doschar(c); -} + int err; -/* - * Is character a wildcard? - */ -static int -wildchar(int c) -{ - return c == '*' || c == '?'; + if (fs->bufsec != lsec) { + if ((err = ioget(fs->fd, lsec, fs->buf, 1))) + return err; + fs->bufsec = lsec; + } + return 0; } /* - * Is character valid in a DOS name? + * Sector-based I/O primitive */ static int -doschar(int c) +ioget(int fd, u_int lsec, void *buf, u_int nsec) { - return c & 0x80 || (c >= ' ' && !strchr("\"*+,./:;<=>?[\\]|", c)); + size_t nbyte; + ssize_t n; + + if (lseek(fd, secbyt(lsec), SEEK_SET) == -1) + return errno; + nbyte = secbyt(nsec); + n = read(fd, buf, nbyte); + return n == -1 ? errno : (size_t)n != nbyte ? EIO : 0; } |