dosfs.c revision 40005
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
2838451Smsmith/*
2938451Smsmith * Readonly filesystem for Microsoft FAT12/FAT16/FAT32 filesystems,
3038451Smsmith * also supports VFAT.
3138451Smsmith */
3238451Smsmith
3338451Smsmith#include <sys/types.h>
3438451Smsmith#include <string.h>
3538451Smsmith#include <stddef.h>
3638451Smsmith
3738451Smsmith#include "stand.h"
3838451Smsmith
3938451Smsmith#include "dosfs.h"
4038451Smsmith
4138451Smsmith
4240005Smsmithstatic int	dos_open(const char *path, struct open_file *fd);
4338451Smsmithstatic int	dos_close(struct open_file *fd);
4438451Smsmithstatic int	dos_read(struct open_file *fd, void *buf, size_t size, size_t *resid);
4538451Smsmithstatic off_t	dos_seek(struct open_file *fd, off_t offset, int whence);
4638451Smsmithstatic int	dos_stat(struct open_file *fd, struct stat *sb);
4738451Smsmith
4838582Srnordierstruct fs_ops dosfs_fsops = {
4938451Smsmith    "dosfs", dos_open, dos_close, dos_read, null_write, dos_seek, dos_stat
5038451Smsmith};
5138451Smsmith
5238451Smsmith#define SECSIZ  512             /* sector size */
5338451Smsmith#define SSHIFT    9             /* SECSIZ shift */
5438451Smsmith#define DEPSEC   16             /* directory entries per sector */
5538451Smsmith#define DSHIFT    4             /* DEPSEC shift */
5638451Smsmith#define LOCLUS    2             /* lowest cluster number */
5738451Smsmith
5838451Smsmith/* DOS "BIOS Parameter Block" */
5938451Smsmithtypedef struct {
6038451Smsmith    u_char secsiz[2];           /* sector size */
6138451Smsmith    u_char spc;                 /* sectors per cluster */
6238451Smsmith    u_char ressec[2];           /* reserved sectors */
6338451Smsmith    u_char fats;                /* FATs */
6438451Smsmith    u_char dirents[2];          /* root directory entries */
6538451Smsmith    u_char secs[2];             /* total sectors */
6638451Smsmith    u_char media;               /* media descriptor */
6738451Smsmith    u_char spf[2];              /* sectors per FAT */
6838451Smsmith    u_char spt[2];              /* sectors per track */
6938451Smsmith    u_char heads[2];            /* drive heads */
7038451Smsmith    u_char hidsec[4];           /* hidden sectors */
7138451Smsmith    u_char lsecs[4];            /* huge sectors */
7238451Smsmith    u_char lspf[4];             /* huge sectors per FAT */
7338451Smsmith    u_char xflg[2];             /* flags */
7438451Smsmith    u_char vers[2];             /* filesystem version */
7538451Smsmith    u_char rdcl[4];             /* root directory start cluster */
7638451Smsmith    u_char infs[2];             /* filesystem info sector */
7738451Smsmith    u_char bkbs[2];             /* backup boot sector */
7838451Smsmith} DOS_BPB;
7938451Smsmith
8038451Smsmith/* Initial portion of DOS boot sector */
8138451Smsmithtypedef struct {
8238451Smsmith    u_char jmp[3];              /* usually 80x86 'jmp' opcode */
8338451Smsmith    u_char oem[8];              /* OEM name and version */
8438451Smsmith    DOS_BPB bpb;                /* BPB */
8538451Smsmith} DOS_BS;
8638451Smsmith
8738451Smsmith/* Supply missing "." and ".." root directory entries */
8838451Smsmithstatic const char *const dotstr[2] = {".", ".."};
8938451Smsmithstatic DOS_DE dot[2] = {
9038451Smsmith    {".       ", "   ", FA_DIR, {0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
9138451Smsmith     {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}},
9238451Smsmith    {"..      ", "   ", FA_DIR, {0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
9338451Smsmith     {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}}
9438451Smsmith};
9538451Smsmith
9638451Smsmith/* The usual conversion macros to avoid multiplication and division */
9738451Smsmith#define bytsec(n)      ((n) >> SSHIFT)
9838451Smsmith#define secbyt(s)      ((s) << SSHIFT)
9938451Smsmith#define entsec(e)      ((e) >> DSHIFT)
10038451Smsmith#define bytblk(fs, n)  ((n) >> (fs)->bshift)
10138451Smsmith#define blkbyt(fs, b)  ((b) << (fs)->bshift)
10238451Smsmith#define secblk(fs, s)  ((s) >> ((fs)->bshift - SSHIFT))
10338451Smsmith#define blksec(fs, b)  ((b) << ((fs)->bshift - SSHIFT))
10438451Smsmith
10538451Smsmith/* Convert cluster number to offset within filesystem */
10638451Smsmith#define blkoff(fs, b) (secbyt((fs)->lsndta) + blkbyt(fs, (b) - LOCLUS))
10738451Smsmith
10838451Smsmith/* Convert cluster number to logical sector number */
10938451Smsmith#define blklsn(fs, b)  ((fs)->lsndta + blksec(fs, (b) - LOCLUS))
11038451Smsmith
11138451Smsmith/* Convert cluster number to offset within FAT */
11238451Smsmith#define fatoff(sz, c)  ((sz) == 12 ? (c) + ((c) >> 1) :  \
11338451Smsmith                        (sz) == 16 ? (c) << 1 :          \
11438451Smsmith			(c) << 2)
11538451Smsmith
11638451Smsmith/* Does cluster number reference a valid data cluster? */
11738451Smsmith#define okclus(fs, c)  ((c) >= LOCLUS && (c) <= (fs)->xclus)
11838451Smsmith
11938451Smsmith/* Get start cluster from directory entry */
12038451Smsmith#define stclus(sz, de)  ((sz) != 32 ? cv2((de)->clus) :          \
12138451Smsmith                         ((u_int)cv2((de)->dex.h_clus) << 16) |  \
12238451Smsmith			 cv2((de)->clus))
12338451Smsmith
12438451Smsmithstatic int dosunmount(DOS_FS *);
12538451Smsmithstatic int parsebs(DOS_FS *, DOS_BS *);
12638451Smsmithstatic int namede(DOS_FS *, const char *, DOS_DE **);
12738451Smsmithstatic int lookup(DOS_FS *, u_int, const char *, DOS_DE **);
12838451Smsmithstatic void cp_xdnm(u_char *, DOS_XDE *);
12938451Smsmithstatic void cp_sfn(u_char *, DOS_DE *);
13038582Srnordierstatic off_t fsize(DOS_FS *, DOS_DE *);
13138582Srnordierstatic int fatcnt(DOS_FS *, u_int);
13238451Smsmithstatic int fatget(DOS_FS *, u_int *);
13338451Smsmithstatic int fatend(u_int, u_int);
13438451Smsmithstatic int ioread(DOS_FS *, u_int, void *, u_int);
13538451Smsmithstatic int iobuf(DOS_FS *, u_int);
13638451Smsmithstatic int ioget(struct open_file *, u_int, void *, u_int);
13738451Smsmith
13838451Smsmith/*
13938451Smsmith * Mount DOS filesystem
14038451Smsmith */
14138451Smsmithstatic int
14238451Smsmithdos_mount(DOS_FS *fs, struct open_file *fd)
14338451Smsmith{
14438451Smsmith    int err;
14538451Smsmith
14638451Smsmith    bzero(fs, sizeof(DOS_FS));
14738451Smsmith    fs->fd = fd;
14838582Srnordier    if ((err = !(fs->buf = malloc(SECSIZ)) ? errno : 0) ||
14938451Smsmith        (err = ioget(fs->fd, 0, fs->buf, 1)) ||
15038451Smsmith        (err = parsebs(fs, (DOS_BS *)fs->buf))) {
15138451Smsmith        (void)dosunmount(fs);
15238451Smsmith        return(err);
15338451Smsmith    }
15438451Smsmith    return 0;
15538451Smsmith}
15638451Smsmith
15738451Smsmith/*
15838451Smsmith * Unmount mounted filesystem
15938451Smsmith */
16038451Smsmithstatic int
16138451Smsmithdos_unmount(DOS_FS *fs)
16238451Smsmith{
16338451Smsmith    int err;
16438451Smsmith
16538451Smsmith    if (fs->links)
16638451Smsmith        return(EBUSY);
16738451Smsmith    if ((err = dosunmount(fs)))
16838451Smsmith        return(err);
16938451Smsmith    return 0;
17038451Smsmith}
17138451Smsmith
17238451Smsmith/*
17338451Smsmith * Common code shared by dos_mount() and dos_unmount()
17438451Smsmith */
17538451Smsmithstatic int
17638451Smsmithdosunmount(DOS_FS *fs)
17738451Smsmith{
17838451Smsmith    if (fs->buf)
17938582Srnordier        free(fs->buf);
18038582Srnordier    free(fs);
18138451Smsmith    return(0);
18238451Smsmith}
18338451Smsmith
18438451Smsmith/*
18538451Smsmith * Open DOS file
18638451Smsmith */
18738451Smsmithstatic int
18840005Smsmithdos_open(const char *path, struct open_file *fd)
18938451Smsmith{
19038451Smsmith    DOS_DE *de;
19138451Smsmith    DOS_FILE *f;
19238451Smsmith    DOS_FS *fs;
19338451Smsmith    u_int size, clus;
19438451Smsmith    int err = 0;
19538451Smsmith
19638451Smsmith    /* Allocate mount structure, associate with open */
19738582Srnordier    fs = malloc(sizeof(DOS_FS));
19838451Smsmith
19938451Smsmith    if ((err = dos_mount(fs, fd)))
20038451Smsmith	goto out;
20138451Smsmith
20238451Smsmith    if ((err = namede(fs, path, &de)))
20338451Smsmith	goto out;
20438451Smsmith
20538451Smsmith    clus = stclus(fs->fatsz, de);
20638451Smsmith    size = cv4(de->size);
20738582Srnordier
20838582Srnordier    if ((!(de->attr & FA_DIR) && (!clus != !size)) ||
20938582Srnordier	((de->attr & FA_DIR) && size) ||
21038582Srnordier	(clus && !okclus(fs, clus))) {
21138451Smsmith        err = EINVAL;
21238451Smsmith	goto out;
21338451Smsmith    }
21438582Srnordier    f = malloc(sizeof(DOS_FILE));
21538451Smsmith    bzero(f, sizeof(DOS_FILE));
21638451Smsmith    f->fs = fs;
21738451Smsmith    fs->links++;
21838451Smsmith    f->de = *de;
21938451Smsmith    fd->f_fsdata = (void *)f;
22038451Smsmith
22138451Smsmith out:
22238451Smsmith    return(err);
22338451Smsmith}
22438451Smsmith
22538451Smsmith/*
22638451Smsmith * Read from file
22738451Smsmith */
22838451Smsmithstatic int
22938451Smsmithdos_read(struct open_file *fd, void *buf, size_t nbyte, size_t *resid)
23038451Smsmith{
23138582Srnordier    off_t size;
23238451Smsmith    u_int nb, off, clus, c, cnt, n;
23338451Smsmith    DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
23438451Smsmith    int err = 0;
23538451Smsmith
23638451Smsmith    nb = (u_int)nbyte;
23738582Srnordier    if ((size = fsize(f->fs, &f->de)) == -1)
23838582Srnordier	return EINVAL;
23938582Srnordier    if (nb > (n = size - f->offset))
24038451Smsmith        nb = n;
24138451Smsmith    off = f->offset;
24238451Smsmith    if ((clus = stclus(f->fs->fatsz, &f->de)))
24338451Smsmith        off &= f->fs->bsize - 1;
24438451Smsmith    c = f->c;
24538451Smsmith    cnt = nb;
24638451Smsmith    while (cnt) {
24738451Smsmith        n = 0;
24838451Smsmith        if (!c) {
24938451Smsmith            if ((c = clus))
25038451Smsmith                n = bytblk(f->fs, f->offset);
25138451Smsmith        } else if (!off)
25238451Smsmith            n++;
25338451Smsmith        while (n--) {
25438451Smsmith            if ((err = fatget(f->fs, &c)))
25538451Smsmith		goto out;
25638451Smsmith            if (!okclus(f->fs, c)) {
25738451Smsmith		err = EINVAL;
25838451Smsmith		goto out;
25938451Smsmith	    }
26038451Smsmith        }
26138451Smsmith        if (!clus || (n = f->fs->bsize - off) > cnt)
26238451Smsmith            n = cnt;
26338582Srnordier        if ((err = ioread(f->fs, (c ? blkoff(f->fs, c) :
26438582Srnordier				      secbyt(f->fs->lsndir)) + off,
26538582Srnordier			  buf, n)))
26638451Smsmith	    goto out;
26738451Smsmith        f->offset += n;
26838451Smsmith        f->c = c;
26938451Smsmith        off = 0;
27038451Smsmith        buf += n;
27138451Smsmith        cnt -= n;
27238451Smsmith    }
27338451Smsmith out:
27438451Smsmith    if (resid)
27538582Srnordier	*resid = nbyte - nb + cnt;
27638451Smsmith    return(err);
27738451Smsmith}
27838451Smsmith
27938451Smsmith/*
28038451Smsmith * Reposition within file
28138451Smsmith */
28238451Smsmithstatic off_t
28338451Smsmithdos_seek(struct open_file *fd, off_t offset, int whence)
28438451Smsmith{
28538451Smsmith    off_t off;
28638451Smsmith    u_int size;
28738451Smsmith    DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
28838451Smsmith
28938451Smsmith    size = cv4(f->de.size);
29038451Smsmith    switch (whence) {
29138451Smsmith    case SEEK_SET:
29238451Smsmith        off = 0;
29338451Smsmith        break;
29438451Smsmith    case SEEK_CUR:
29538451Smsmith        off = f->offset;
29638451Smsmith        break;
29738451Smsmith    case SEEK_END:
29838451Smsmith        off = size;
29938451Smsmith        break;
30038451Smsmith    default:
30138451Smsmith	return(-1);
30238451Smsmith    }
30338451Smsmith    off += offset;
30438451Smsmith    if (off < 0 || off > size)
30538451Smsmith        return(-1);
30638451Smsmith    f->offset = (u_int)off;
30738451Smsmith    f->c = 0;
30838451Smsmith    return(off);
30938451Smsmith}
31038451Smsmith
31138451Smsmith/*
31238451Smsmith * Close open file
31338451Smsmith */
31438451Smsmithstatic int
31538451Smsmithdos_close(struct open_file *fd)
31638451Smsmith{
31738451Smsmith    DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
31838451Smsmith    DOS_FS *fs = f->fs;
31938451Smsmith
32038451Smsmith    f->fs->links--;
32138582Srnordier    free(f);
32238451Smsmith    dos_unmount(fs);
32338451Smsmith    return 0;
32438451Smsmith}
32538451Smsmith
32638451Smsmith/*
32738451Smsmith * Return some stat information on a file.
32838451Smsmith */
32938451Smsmithstatic int
33038451Smsmithdos_stat(struct open_file *fd, struct stat *sb)
33138451Smsmith{
33238451Smsmith    DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
33338451Smsmith
33438451Smsmith    /* only important stuff */
33538582Srnordier    sb->st_mode = f->de.attr & FA_DIR ? S_IFDIR | 0555 : S_IFREG | 0444;
33638451Smsmith    sb->st_nlink = 1;
33738451Smsmith    sb->st_uid = 0;
33838451Smsmith    sb->st_gid = 0;
33938582Srnordier    if ((sb->st_size = fsize(f->fs, &f->de)) == -1)
34038582Srnordier	return EINVAL;
34138451Smsmith    return (0);
34238451Smsmith}
34338451Smsmith
34438451Smsmith/*
34538451Smsmith * Parse DOS boot sector
34638451Smsmith */
34738451Smsmithstatic int
34838451Smsmithparsebs(DOS_FS *fs, DOS_BS *bs)
34938451Smsmith{
35038451Smsmith    u_int sc;
35138451Smsmith
35238451Smsmith    if ((bs->jmp[0] != 0x69 &&
35338451Smsmith         bs->jmp[0] != 0xe9 &&
35438451Smsmith         (bs->jmp[0] != 0xeb || bs->jmp[2] != 0x90)) ||
35538451Smsmith        bs->bpb.media < 0xf0)
35638451Smsmith        return EINVAL;
35738451Smsmith    if (cv2(bs->bpb.secsiz) != SECSIZ)
35838451Smsmith        return EINVAL;
35938451Smsmith    if (!(fs->spc = bs->bpb.spc) || fs->spc & (fs->spc - 1))
36038451Smsmith        return EINVAL;
36138451Smsmith    fs->bsize = secbyt(fs->spc);
36238451Smsmith    fs->bshift = ffs(fs->bsize) - 1;
36338451Smsmith    if ((fs->spf = cv2(bs->bpb.spf))) {
36438451Smsmith        if (bs->bpb.fats != 2)
36538451Smsmith            return EINVAL;
36638451Smsmith        if (!(fs->dirents = cv2(bs->bpb.dirents)))
36738451Smsmith            return EINVAL;
36838451Smsmith    } else {
36938451Smsmith        if (!(fs->spf = cv4(bs->bpb.lspf)))
37038451Smsmith            return EINVAL;
37138451Smsmith        if (!bs->bpb.fats || bs->bpb.fats > 16)
37238451Smsmith            return EINVAL;
37338451Smsmith        if ((fs->rdcl = cv4(bs->bpb.rdcl)) < LOCLUS)
37438451Smsmith            return EINVAL;
37538451Smsmith    }
37638451Smsmith    if (!(fs->lsnfat = cv2(bs->bpb.ressec)))
37738451Smsmith        return EINVAL;
37838451Smsmith    fs->lsndir = fs->lsnfat + fs->spf * bs->bpb.fats;
37938451Smsmith    fs->lsndta = fs->lsndir + entsec(fs->dirents);
38038451Smsmith    if (!(sc = cv2(bs->bpb.secs)) && !(sc = cv4(bs->bpb.lsecs)))
38138451Smsmith        return EINVAL;
38238451Smsmith    if (fs->lsndta > sc)
38338451Smsmith        return EINVAL;
38438451Smsmith    if ((fs->xclus = secblk(fs, sc - fs->lsndta) + 1) < LOCLUS)
38538451Smsmith        return EINVAL;
38638451Smsmith    fs->fatsz = fs->dirents ? fs->xclus < 0xff6 ? 12 : 16 : 32;
38738451Smsmith    sc = (secbyt(fs->spf) << 1) / (fs->fatsz >> 2) - 1;
38838451Smsmith    if (fs->xclus > sc)
38938451Smsmith        fs->xclus = sc;
39038451Smsmith    return 0;
39138451Smsmith}
39238451Smsmith
39338451Smsmith/*
39438451Smsmith * Return directory entry from path
39538451Smsmith */
39638451Smsmithstatic int
39738451Smsmithnamede(DOS_FS *fs, const char *path, DOS_DE **dep)
39838451Smsmith{
39938451Smsmith    char name[256];
40038451Smsmith    DOS_DE *de;
40138451Smsmith    char *s;
40238451Smsmith    size_t n;
40338451Smsmith    int err;
40438451Smsmith
40538451Smsmith    err = 0;
40638451Smsmith    de = dot;
40738451Smsmith    if (*path == '/')
40838451Smsmith        path++;
40938451Smsmith    while (*path) {
41038451Smsmith        if (!(s = strchr(path, '/')))
41138451Smsmith            s = strchr(path, 0);
41238451Smsmith        if ((n = s - path) > 255)
41338451Smsmith            return ENAMETOOLONG;
41438451Smsmith        memcpy(name, path, n);
41538451Smsmith        name[n] = 0;
41638451Smsmith        path = s;
41738451Smsmith        if (!(de->attr & FA_DIR))
41838451Smsmith            return ENOTDIR;
41938451Smsmith        if ((err = lookup(fs, stclus(fs->fatsz, de), name, &de)))
42038451Smsmith            return err;
42138451Smsmith        if (*path == '/')
42238451Smsmith            path++;
42338451Smsmith    }
42438451Smsmith    *dep = de;
42538451Smsmith    return 0;
42638451Smsmith}
42738451Smsmith
42838451Smsmith/*
42938451Smsmith * Lookup path segment
43038451Smsmith */
43138451Smsmithstatic int
43238451Smsmithlookup(DOS_FS *fs, u_int clus, const char *name, DOS_DE **dep)
43338451Smsmith{
43438451Smsmith    static DOS_DIR dir[DEPSEC];
43538451Smsmith    u_char lfn[261];
43638451Smsmith    u_char sfn[13];
43738451Smsmith    u_int nsec, lsec, xdn, chk, sec, ent, x;
43838451Smsmith    int err, ok, i;
43938451Smsmith
44038451Smsmith    if (!clus)
44138451Smsmith        for (ent = 0; ent < 2; ent++)
44238451Smsmith            if (!strcasecmp(name, dotstr[ent])) {
44338451Smsmith                *dep = dot + ent;
44438451Smsmith                return 0;
44538451Smsmith            }
44638451Smsmith    if (!clus && fs->fatsz == 32)
44738451Smsmith        clus = fs->rdcl;
44838451Smsmith    nsec = !clus ? entsec(fs->dirents) : fs->spc;
44938451Smsmith    lsec = 0;
45038451Smsmith    xdn = chk = 0;
45138451Smsmith    for (;;) {
45238451Smsmith        if (!clus && !lsec)
45338451Smsmith            lsec = fs->lsndir;
45438451Smsmith        else if (okclus(fs, clus))
45538451Smsmith            lsec = blklsn(fs, clus);
45638451Smsmith        else
45738451Smsmith            return EINVAL;
45838451Smsmith        for (sec = 0; sec < nsec; sec++) {
45938451Smsmith            if ((err = ioget(fs->fd, lsec + sec, dir, 1)))
46038451Smsmith                return err;
46138451Smsmith            for (ent = 0; ent < DEPSEC; ent++) {
46238451Smsmith                if (!*dir[ent].de.name)
46338451Smsmith                    return ENOENT;
46438451Smsmith                if (*dir[ent].de.name != 0xe5)
46538451Smsmith                    if ((dir[ent].de.attr & FA_MASK) == FA_XDE) {
46638451Smsmith                        x = dir[ent].xde.seq;
46738451Smsmith                        if (x & 0x40 || (x + 1 == xdn &&
46838451Smsmith                                         dir[ent].xde.chk == chk)) {
46938451Smsmith                            if (x & 0x40) {
47038451Smsmith                                chk = dir[ent].xde.chk;
47138451Smsmith                                x &= ~0x40;
47238451Smsmith                            }
47338451Smsmith                            if (x >= 1 && x <= 20) {
47438451Smsmith                                cp_xdnm(lfn, &dir[ent].xde);
47538451Smsmith                                xdn = x;
47638451Smsmith                                continue;
47738451Smsmith                            }
47838451Smsmith                        }
47938451Smsmith                    } else if (!(dir[ent].de.attr & FA_LABEL)) {
48038451Smsmith                        if ((ok = xdn == 1)) {
48138451Smsmith                            for (x = 0, i = 0; i < 11; i++)
48238451Smsmith                                x = ((((x & 1) << 7) | (x >> 1)) +
48338451Smsmith                                     dir[ent].de.name[i]) & 0xff;
48438451Smsmith                            ok = chk == x &&
48538451Smsmith                                !strcasecmp(name, (const char *)lfn);
48638451Smsmith                        }
48738451Smsmith                        if (!ok) {
48838451Smsmith                            cp_sfn(sfn, &dir[ent].de);
48938451Smsmith                            ok = !strcasecmp(name, (const char *)sfn);
49038451Smsmith                        }
49138451Smsmith                        if (ok) {
49238451Smsmith                            *dep = &dir[ent].de;
49338451Smsmith                            return 0;
49438451Smsmith                        }
49538451Smsmith                    }
49638451Smsmith                xdn = 0;
49738451Smsmith            }
49838451Smsmith        }
49938451Smsmith        if (!clus)
50038451Smsmith            break;
50138451Smsmith        if ((err = fatget(fs, &clus)))
50238451Smsmith            return err;
50338451Smsmith        if (fatend(fs->fatsz, clus))
50438451Smsmith            break;
50538451Smsmith    }
50638451Smsmith    return ENOENT;
50738451Smsmith}
50838451Smsmith
50938451Smsmith/*
51038451Smsmith * Copy name from extended directory entry
51138451Smsmith */
51238451Smsmithstatic void
51338451Smsmithcp_xdnm(u_char *lfn, DOS_XDE *xde)
51438451Smsmith{
51538451Smsmith    static struct {
51638451Smsmith        u_int off;
51738451Smsmith        u_int dim;
51838451Smsmith    } ix[3] = {
51938451Smsmith        {offsetof(DOS_XDE, name1), sizeof(xde->name1) / 2},
52038451Smsmith        {offsetof(DOS_XDE, name2), sizeof(xde->name2) / 2},
52138451Smsmith        {offsetof(DOS_XDE, name3), sizeof(xde->name3) / 2}
52238451Smsmith    };
52338451Smsmith    u_char *p;
52438451Smsmith    u_int n, x, c;
52538451Smsmith
52638451Smsmith    lfn += 13 * ((xde->seq & ~0x40) - 1);
52738451Smsmith    for (n = 0; n < 3; n++)
52838451Smsmith        for (p = (u_char *)xde + ix[n].off, x = ix[n].dim; x;
52938451Smsmith	     p += 2, x--) {
53038451Smsmith            if ((c = cv2(p)) && (c < 32 || c > 127))
53138451Smsmith                c = '?';
53238451Smsmith            if (!(*lfn++ = c))
53338451Smsmith                return;
53438451Smsmith        }
53538451Smsmith    if (xde->seq & 0x40)
53638451Smsmith        *lfn = 0;
53738451Smsmith}
53838451Smsmith
53938451Smsmith/*
54038451Smsmith * Copy short filename
54138451Smsmith */
54238451Smsmithstatic void
54338451Smsmithcp_sfn(u_char *sfn, DOS_DE *de)
54438451Smsmith{
54538451Smsmith    u_char *p;
54638451Smsmith    int j, i;
54738451Smsmith
54838451Smsmith    p = sfn;
54938451Smsmith    if (*de->name != ' ') {
55038451Smsmith        for (j = 7; de->name[j] == ' '; j--);
55138451Smsmith        for (i = 0; i <= j; i++)
55238451Smsmith            *p++ = de->name[i];
55338451Smsmith        if (*de->ext != ' ') {
55438451Smsmith            *p++ = '.';
55538451Smsmith            for (j = 2; de->ext[j] == ' '; j--);
55638451Smsmith            for (i = 0; i <= j; i++)
55738451Smsmith                *p++ = de->ext[i];
55838451Smsmith        }
55938451Smsmith    }
56038451Smsmith    *p = 0;
56138451Smsmith    if (*sfn == 5)
56238451Smsmith        *sfn = 0xe5;
56338451Smsmith}
56438451Smsmith
56538451Smsmith/*
56638582Srnordier * Return size of file in bytes
56738582Srnordier */
56838582Srnordierstatic off_t
56938582Srnordierfsize(DOS_FS *fs, DOS_DE *de)
57038582Srnordier{
57138582Srnordier   u_long size;
57238582Srnordier   u_int c;
57338582Srnordier   int n;
57438582Srnordier
57538582Srnordier   if (!(size = cv4(de->size)) && de->attr & FA_DIR)
57638582Srnordier      if (!(c = cv2(de->clus)))
57738582Srnordier         size = fs->dirents * sizeof(DOS_DE);
57838582Srnordier      else {
57938582Srnordier         if ((n = fatcnt(fs, c)) == -1)
58038582Srnordier            return n;
58138582Srnordier         size = blkbyt(fs, n);
58238582Srnordier      }
58338582Srnordier   return size;
58438582Srnordier}
58538582Srnordier
58638582Srnordier/*
58738582Srnordier * Count number of clusters in chain
58838582Srnordier */
58938582Srnordierstatic int
59038582Srnordierfatcnt(DOS_FS *fs, u_int c)
59138582Srnordier{
59238582Srnordier   int n;
59338582Srnordier
59438582Srnordier   for (n = 0; okclus(fs, c); n++)
59538582Srnordier      if (fatget(fs, &c))
59638582Srnordier	  return -1;
59738582Srnordier   return fatend(fs->fatsz, c) ? n : -1;
59838582Srnordier}
59938582Srnordier
60038582Srnordier/*
60138451Smsmith * Get next cluster in cluster chain
60238451Smsmith */
60338451Smsmithstatic int
60438451Smsmithfatget(DOS_FS *fs, u_int *c)
60538451Smsmith{
60638451Smsmith    u_char buf[4];
60738451Smsmith    u_int x;
60838451Smsmith    int err;
60938451Smsmith
61038451Smsmith    err = ioread(fs, secbyt(fs->lsnfat) + fatoff(fs->fatsz, *c), buf,
61138451Smsmith                 fs->fatsz != 32 ? 2 : 4);
61238451Smsmith    if (err)
61338451Smsmith        return err;
61438451Smsmith    x = fs->fatsz != 32 ? cv2(buf) : cv4(buf);
61538451Smsmith    *c = fs->fatsz == 12 ? *c & 1 ? x >> 4 : x & 0xfff : x;
61638451Smsmith    return 0;
61738451Smsmith}
61838451Smsmith
61938451Smsmith/*
62038451Smsmith * Is cluster an end-of-chain marker?
62138451Smsmith */
62238451Smsmithstatic int
62338451Smsmithfatend(u_int sz, u_int c)
62438451Smsmith{
62538451Smsmith    return c > (sz == 12 ? 0xff7U : sz == 16 ? 0xfff7U : 0xffffff7);
62638451Smsmith}
62738451Smsmith
62838451Smsmith/*
62938451Smsmith * Offset-based I/O primitive
63038451Smsmith */
63138451Smsmithstatic int
63238451Smsmithioread(DOS_FS *fs, u_int offset, void *buf, u_int nbyte)
63338451Smsmith{
63438451Smsmith    char *s;
63538451Smsmith    u_int off, n;
63638451Smsmith    int err;
63738451Smsmith
63838451Smsmith    s = buf;
63938451Smsmith    if ((off = offset & (SECSIZ - 1))) {
64038451Smsmith        offset -= off;
64138451Smsmith        if ((err = iobuf(fs, bytsec(offset))))
64238451Smsmith            return err;
64338451Smsmith        offset += SECSIZ;
64438451Smsmith        if ((n = SECSIZ - off) > nbyte)
64538451Smsmith            n = nbyte;
64638451Smsmith        memcpy(s, fs->buf + off, n);
64738451Smsmith        s += n;
64838451Smsmith        nbyte -= n;
64938451Smsmith    }
65038451Smsmith    n = nbyte & (SECSIZ - 1);
65138451Smsmith    if (nbyte -= n) {
65238451Smsmith        if ((err = ioget(fs->fd, bytsec(offset), s, bytsec(nbyte))))
65338451Smsmith            return err;
65438451Smsmith        offset += nbyte;
65538451Smsmith        s += nbyte;
65638451Smsmith    }
65738451Smsmith    if (n) {
65838451Smsmith        if ((err = iobuf(fs, bytsec(offset))))
65938451Smsmith            return err;
66038451Smsmith        memcpy(s, fs->buf, n);
66138451Smsmith    }
66238451Smsmith    return 0;
66338451Smsmith}
66438451Smsmith
66538451Smsmith/*
66638451Smsmith * Buffered sector-based I/O primitive
66738451Smsmith */
66838451Smsmithstatic int
66938451Smsmithiobuf(DOS_FS *fs, u_int lsec)
67038451Smsmith{
67138451Smsmith    int err;
67238451Smsmith
67338451Smsmith    if (fs->bufsec != lsec) {
67438451Smsmith        if ((err = ioget(fs->fd, lsec, fs->buf, 1)))
67538451Smsmith            return err;
67638451Smsmith        fs->bufsec = lsec;
67738451Smsmith    }
67838451Smsmith    return 0;
67938451Smsmith}
68038451Smsmith
68138451Smsmith/*
68238451Smsmith * Sector-based I/O primitive
68338451Smsmith */
68438451Smsmithstatic int
68538451Smsmithioget(struct open_file *fd, u_int lsec, void *buf, u_int nsec)
68638451Smsmith{
68738451Smsmith    int	err;
68838451Smsmith
68938451Smsmith    if ((err = (fd->f_dev->dv_strategy)(fd->f_devdata, F_READ, lsec,
69038451Smsmith					secbyt(nsec), buf, NULL)))
69138451Smsmith	return(err);
69238451Smsmith    return(0);
69338451Smsmith}
694