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