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$");
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 */
6838451Smsmith
6938451Smsmith/* DOS "BIOS Parameter Block" */
7038451Smsmithtypedef struct {
7138451Smsmith    u_char secsiz[2];           /* sector size */
7238451Smsmith    u_char spc;                 /* sectors per cluster */
7338451Smsmith    u_char ressec[2];           /* reserved sectors */
7438451Smsmith    u_char fats;                /* FATs */
7538451Smsmith    u_char dirents[2];          /* root directory entries */
7638451Smsmith    u_char secs[2];             /* total sectors */
7738451Smsmith    u_char media;               /* media descriptor */
7838451Smsmith    u_char spf[2];              /* sectors per FAT */
7938451Smsmith    u_char spt[2];              /* sectors per track */
8038451Smsmith    u_char heads[2];            /* drive heads */
8138451Smsmith    u_char hidsec[4];           /* hidden sectors */
8238451Smsmith    u_char lsecs[4];            /* huge sectors */
8338451Smsmith    u_char lspf[4];             /* huge sectors per FAT */
8438451Smsmith    u_char xflg[2];             /* flags */
8538451Smsmith    u_char vers[2];             /* filesystem version */
8638451Smsmith    u_char rdcl[4];             /* root directory start cluster */
8738451Smsmith    u_char infs[2];             /* filesystem info sector */
8838451Smsmith    u_char bkbs[2];             /* backup boot sector */
8938451Smsmith} DOS_BPB;
9038451Smsmith
9138451Smsmith/* Initial portion of DOS boot sector */
9238451Smsmithtypedef struct {
9338451Smsmith    u_char jmp[3];              /* usually 80x86 'jmp' opcode */
9438451Smsmith    u_char oem[8];              /* OEM name and version */
9538451Smsmith    DOS_BPB bpb;                /* BPB */
9638451Smsmith} DOS_BS;
9738451Smsmith
9838451Smsmith/* Supply missing "." and ".." root directory entries */
9938451Smsmithstatic const char *const dotstr[2] = {".", ".."};
10038451Smsmithstatic DOS_DE dot[2] = {
10138451Smsmith    {".       ", "   ", FA_DIR, {0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
10238451Smsmith     {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}},
10338451Smsmith    {"..      ", "   ", FA_DIR, {0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
10438451Smsmith     {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}}
10538451Smsmith};
10638451Smsmith
10738451Smsmith/* The usual conversion macros to avoid multiplication and division */
10838451Smsmith#define bytsec(n)      ((n) >> SSHIFT)
10938451Smsmith#define secbyt(s)      ((s) << SSHIFT)
11038451Smsmith#define entsec(e)      ((e) >> DSHIFT)
11138451Smsmith#define bytblk(fs, n)  ((n) >> (fs)->bshift)
11238451Smsmith#define blkbyt(fs, b)  ((b) << (fs)->bshift)
11338451Smsmith#define secblk(fs, s)  ((s) >> ((fs)->bshift - SSHIFT))
11438451Smsmith#define blksec(fs, b)  ((b) << ((fs)->bshift - SSHIFT))
11538451Smsmith
11638451Smsmith/* Convert cluster number to offset within filesystem */
11738451Smsmith#define blkoff(fs, b) (secbyt((fs)->lsndta) + blkbyt(fs, (b) - LOCLUS))
11838451Smsmith
11938451Smsmith/* Convert cluster number to logical sector number */
12038451Smsmith#define blklsn(fs, b)  ((fs)->lsndta + blksec(fs, (b) - LOCLUS))
12138451Smsmith
12238451Smsmith/* Convert cluster number to offset within FAT */
12338451Smsmith#define fatoff(sz, c)  ((sz) == 12 ? (c) + ((c) >> 1) :  \
12438451Smsmith                        (sz) == 16 ? (c) << 1 :          \
12538451Smsmith			(c) << 2)
12638451Smsmith
12738451Smsmith/* Does cluster number reference a valid data cluster? */
12838451Smsmith#define okclus(fs, c)  ((c) >= LOCLUS && (c) <= (fs)->xclus)
12938451Smsmith
13038451Smsmith/* Get start cluster from directory entry */
13138451Smsmith#define stclus(sz, de)  ((sz) != 32 ? cv2((de)->clus) :          \
13238451Smsmith                         ((u_int)cv2((de)->dex.h_clus) << 16) |  \
13338451Smsmith			 cv2((de)->clus))
13438451Smsmith
13538451Smsmithstatic int dosunmount(DOS_FS *);
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);
14538451Smsmithstatic int ioread(DOS_FS *, u_int, void *, u_int);
14638451Smsmithstatic int iobuf(DOS_FS *, u_int);
14738451Smsmithstatic int ioget(struct open_file *, u_int, void *, u_int);
14838451Smsmith
14938451Smsmith/*
15038451Smsmith * Mount DOS filesystem
15138451Smsmith */
15238451Smsmithstatic int
15338451Smsmithdos_mount(DOS_FS *fs, struct open_file *fd)
15438451Smsmith{
15538451Smsmith    int err;
15638451Smsmith
15738451Smsmith    bzero(fs, sizeof(DOS_FS));
15838451Smsmith    fs->fd = fd;
15938582Srnordier    if ((err = !(fs->buf = malloc(SECSIZ)) ? errno : 0) ||
16038451Smsmith        (err = ioget(fs->fd, 0, fs->buf, 1)) ||
16138451Smsmith        (err = parsebs(fs, (DOS_BS *)fs->buf))) {
16238451Smsmith        (void)dosunmount(fs);
16338451Smsmith        return(err);
16438451Smsmith    }
16538451Smsmith    return 0;
16638451Smsmith}
16738451Smsmith
16838451Smsmith/*
16938451Smsmith * Unmount mounted filesystem
17038451Smsmith */
17138451Smsmithstatic int
17238451Smsmithdos_unmount(DOS_FS *fs)
17338451Smsmith{
17438451Smsmith    int err;
17538451Smsmith
17638451Smsmith    if (fs->links)
17738451Smsmith        return(EBUSY);
17838451Smsmith    if ((err = dosunmount(fs)))
17938451Smsmith        return(err);
18038451Smsmith    return 0;
18138451Smsmith}
18238451Smsmith
18338451Smsmith/*
18438451Smsmith * Common code shared by dos_mount() and dos_unmount()
18538451Smsmith */
18638451Smsmithstatic int
18738451Smsmithdosunmount(DOS_FS *fs)
18838451Smsmith{
18938451Smsmith    if (fs->buf)
19038582Srnordier        free(fs->buf);
19138582Srnordier    free(fs);
19238451Smsmith    return(0);
19338451Smsmith}
19438451Smsmith
19538451Smsmith/*
19638451Smsmith * Open DOS file
19738451Smsmith */
19838451Smsmithstatic int
19940005Smsmithdos_open(const char *path, struct open_file *fd)
20038451Smsmith{
20138451Smsmith    DOS_DE *de;
20238451Smsmith    DOS_FILE *f;
20338451Smsmith    DOS_FS *fs;
20438451Smsmith    u_int size, clus;
20538451Smsmith    int err = 0;
20638451Smsmith
20738451Smsmith    /* Allocate mount structure, associate with open */
20838582Srnordier    fs = malloc(sizeof(DOS_FS));
20938451Smsmith
21038451Smsmith    if ((err = dos_mount(fs, fd)))
21138451Smsmith	goto out;
21238451Smsmith
21338451Smsmith    if ((err = namede(fs, path, &de)))
21438451Smsmith	goto out;
21538451Smsmith
21638451Smsmith    clus = stclus(fs->fatsz, de);
21738451Smsmith    size = cv4(de->size);
21838582Srnordier
21938582Srnordier    if ((!(de->attr & FA_DIR) && (!clus != !size)) ||
22038582Srnordier	((de->attr & FA_DIR) && size) ||
22138582Srnordier	(clus && !okclus(fs, clus))) {
22238451Smsmith        err = EINVAL;
22338451Smsmith	goto out;
22438451Smsmith    }
22538582Srnordier    f = malloc(sizeof(DOS_FILE));
22638451Smsmith    bzero(f, sizeof(DOS_FILE));
22738451Smsmith    f->fs = fs;
22838451Smsmith    fs->links++;
22938451Smsmith    f->de = *de;
23038451Smsmith    fd->f_fsdata = (void *)f;
23138451Smsmith
23238451Smsmith out:
23338451Smsmith    return(err);
23438451Smsmith}
23538451Smsmith
23638451Smsmith/*
23738451Smsmith * Read from file
23838451Smsmith */
23938451Smsmithstatic int
24038451Smsmithdos_read(struct open_file *fd, void *buf, size_t nbyte, size_t *resid)
24138451Smsmith{
24238582Srnordier    off_t size;
24338451Smsmith    u_int nb, off, clus, c, cnt, n;
24438451Smsmith    DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
24538451Smsmith    int err = 0;
24638451Smsmith
24738451Smsmith    nb = (u_int)nbyte;
24838582Srnordier    if ((size = fsize(f->fs, &f->de)) == -1)
24938582Srnordier	return EINVAL;
25038582Srnordier    if (nb > (n = size - f->offset))
25138451Smsmith        nb = n;
25238451Smsmith    off = f->offset;
25338451Smsmith    if ((clus = stclus(f->fs->fatsz, &f->de)))
25438451Smsmith        off &= f->fs->bsize - 1;
25538451Smsmith    c = f->c;
25638451Smsmith    cnt = nb;
25738451Smsmith    while (cnt) {
25838451Smsmith        n = 0;
25938451Smsmith        if (!c) {
26038451Smsmith            if ((c = clus))
26138451Smsmith                n = bytblk(f->fs, f->offset);
26238451Smsmith        } else if (!off)
26338451Smsmith            n++;
26438451Smsmith        while (n--) {
26538451Smsmith            if ((err = fatget(f->fs, &c)))
26638451Smsmith		goto out;
26738451Smsmith            if (!okclus(f->fs, c)) {
26838451Smsmith		err = EINVAL;
26938451Smsmith		goto out;
27038451Smsmith	    }
27138451Smsmith        }
27238451Smsmith        if (!clus || (n = f->fs->bsize - off) > cnt)
27338451Smsmith            n = cnt;
27438582Srnordier        if ((err = ioread(f->fs, (c ? blkoff(f->fs, c) :
27538582Srnordier				      secbyt(f->fs->lsndir)) + off,
27638582Srnordier			  buf, n)))
27738451Smsmith	    goto out;
27838451Smsmith        f->offset += n;
27938451Smsmith        f->c = c;
28038451Smsmith        off = 0;
281136093Sstefanf        buf = (char *)buf + n;
28238451Smsmith        cnt -= n;
28338451Smsmith    }
28438451Smsmith out:
28538451Smsmith    if (resid)
28638582Srnordier	*resid = nbyte - nb + cnt;
28738451Smsmith    return(err);
28838451Smsmith}
28938451Smsmith
29038451Smsmith/*
29138451Smsmith * Reposition within file
29238451Smsmith */
29338451Smsmithstatic off_t
29438451Smsmithdos_seek(struct open_file *fd, off_t offset, int whence)
29538451Smsmith{
29638451Smsmith    off_t off;
29738451Smsmith    u_int size;
29838451Smsmith    DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
29938451Smsmith
30038451Smsmith    size = cv4(f->de.size);
30138451Smsmith    switch (whence) {
30238451Smsmith    case SEEK_SET:
30338451Smsmith        off = 0;
30438451Smsmith        break;
30538451Smsmith    case SEEK_CUR:
30638451Smsmith        off = f->offset;
30738451Smsmith        break;
30838451Smsmith    case SEEK_END:
30938451Smsmith        off = size;
31038451Smsmith        break;
31138451Smsmith    default:
312124811Sjhb	errno = EINVAL;
31338451Smsmith	return(-1);
31438451Smsmith    }
31538451Smsmith    off += offset;
316124811Sjhb    if (off < 0 || off > size) {
317124811Sjhb	errno = EINVAL;
31838451Smsmith        return(-1);
319124811Sjhb    }
32038451Smsmith    f->offset = (u_int)off;
32138451Smsmith    f->c = 0;
32238451Smsmith    return(off);
32338451Smsmith}
32438451Smsmith
32538451Smsmith/*
32638451Smsmith * Close open file
32738451Smsmith */
32838451Smsmithstatic int
32938451Smsmithdos_close(struct open_file *fd)
33038451Smsmith{
33138451Smsmith    DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
33238451Smsmith    DOS_FS *fs = f->fs;
33338451Smsmith
33438451Smsmith    f->fs->links--;
33538582Srnordier    free(f);
33638451Smsmith    dos_unmount(fs);
33738451Smsmith    return 0;
33838451Smsmith}
33938451Smsmith
34038451Smsmith/*
34138451Smsmith * Return some stat information on a file.
34238451Smsmith */
34338451Smsmithstatic int
34438451Smsmithdos_stat(struct open_file *fd, struct stat *sb)
34538451Smsmith{
34638451Smsmith    DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
34738451Smsmith
34838451Smsmith    /* only important stuff */
34938582Srnordier    sb->st_mode = f->de.attr & FA_DIR ? S_IFDIR | 0555 : S_IFREG | 0444;
35038451Smsmith    sb->st_nlink = 1;
35138451Smsmith    sb->st_uid = 0;
35238451Smsmith    sb->st_gid = 0;
35338582Srnordier    if ((sb->st_size = fsize(f->fs, &f->de)) == -1)
35438582Srnordier	return EINVAL;
35538451Smsmith    return (0);
35638451Smsmith}
35738451Smsmith
358201937Smarcelstatic int
359201937Smarceldos_readdir(struct open_file *fd, struct dirent *d)
360201937Smarcel{
361221365Srodrigc    /* DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; */
362201937Smarcel    u_char fn[261];
363201937Smarcel    DOS_DIR dd;
364201937Smarcel    size_t res;
365201937Smarcel    u_int chk, i, x, xdn;
366201937Smarcel    int err;
367201937Smarcel
368201937Smarcel    x = chk = 0;
369201937Smarcel    while (1) {
370201937Smarcel	xdn = x;
371201937Smarcel	x = 0;
372201937Smarcel	err = dos_read(fd, &dd, sizeof(dd), &res);
373201937Smarcel	if (err)
374201937Smarcel	    return (err);
375201937Smarcel	if (res == sizeof(dd))
376201937Smarcel	    return (ENOENT);
377201937Smarcel	if (dd.de.name[0] == 0)
378201937Smarcel	    return (ENOENT);
379201937Smarcel
380201937Smarcel	/* Skip deleted entries */
381201937Smarcel	if (dd.de.name[0] == 0xe5)
382201937Smarcel	    continue;
383201937Smarcel
384201937Smarcel	/* Skip volume labels */
385201937Smarcel	if (dd.de.attr & FA_LABEL)
386201937Smarcel	    continue;
387201937Smarcel
388201937Smarcel	if ((dd.de.attr & FA_MASK) == FA_XDE) {
389201937Smarcel	    if (dd.xde.seq & 0x40)
390201937Smarcel		chk = dd.xde.chk;
391201937Smarcel	    else if (dd.xde.seq != xdn - 1 || dd.xde.chk != chk)
392201937Smarcel		continue;
393201937Smarcel	    x = dd.xde.seq & ~0x40;
394201937Smarcel	    if (x < 1 || x > 20) {
395201937Smarcel		x = 0;
396201937Smarcel		continue;
397201937Smarcel	    }
398201937Smarcel	    cp_xdnm(fn, &dd.xde);
399201937Smarcel	} else {
400201937Smarcel	    if (xdn == 1) {
401201937Smarcel		x = 0;
402201937Smarcel		for (i = 0; i < 11; i++) {
403201937Smarcel		    x = ((x & 1) << 7) | (x >> 1);
404201937Smarcel		    x += dd.de.name[i];
405201937Smarcel		    x &= 0xff;
406201937Smarcel		}
407201937Smarcel		if (x == chk)
408201937Smarcel		    break;
409201937Smarcel	    } else {
410201937Smarcel		cp_sfn(fn, &dd.de);
411201937Smarcel		break;
412201937Smarcel	    }
413201937Smarcel	    x = 0;
414201937Smarcel	}
415201937Smarcel    }
416201937Smarcel
417221365Srodrigc    d->d_fileno = (dd.de.clus[1] << 8) + dd.de.clus[0];
418201937Smarcel    d->d_reclen = sizeof(*d);
419201937Smarcel    d->d_type = (dd.de.attr & FA_DIR) ? DT_DIR : DT_REG;
420201937Smarcel    memcpy(d->d_name, fn, sizeof(d->d_name));
421201937Smarcel    return(0);
422201937Smarcel}
423201937Smarcel
42438451Smsmith/*
42538451Smsmith * Parse DOS boot sector
42638451Smsmith */
42738451Smsmithstatic int
42838451Smsmithparsebs(DOS_FS *fs, DOS_BS *bs)
42938451Smsmith{
43038451Smsmith    u_int sc;
43138451Smsmith
43238451Smsmith    if ((bs->jmp[0] != 0x69 &&
43338451Smsmith         bs->jmp[0] != 0xe9 &&
43438451Smsmith         (bs->jmp[0] != 0xeb || bs->jmp[2] != 0x90)) ||
43538451Smsmith        bs->bpb.media < 0xf0)
43638451Smsmith        return EINVAL;
43738451Smsmith    if (cv2(bs->bpb.secsiz) != SECSIZ)
43838451Smsmith        return EINVAL;
43938451Smsmith    if (!(fs->spc = bs->bpb.spc) || fs->spc & (fs->spc - 1))
44038451Smsmith        return EINVAL;
44138451Smsmith    fs->bsize = secbyt(fs->spc);
44238451Smsmith    fs->bshift = ffs(fs->bsize) - 1;
44338451Smsmith    if ((fs->spf = cv2(bs->bpb.spf))) {
44438451Smsmith        if (bs->bpb.fats != 2)
44538451Smsmith            return EINVAL;
44638451Smsmith        if (!(fs->dirents = cv2(bs->bpb.dirents)))
44738451Smsmith            return EINVAL;
44838451Smsmith    } else {
44938451Smsmith        if (!(fs->spf = cv4(bs->bpb.lspf)))
45038451Smsmith            return EINVAL;
45138451Smsmith        if (!bs->bpb.fats || bs->bpb.fats > 16)
45238451Smsmith            return EINVAL;
45338451Smsmith        if ((fs->rdcl = cv4(bs->bpb.rdcl)) < LOCLUS)
45438451Smsmith            return EINVAL;
45538451Smsmith    }
45638451Smsmith    if (!(fs->lsnfat = cv2(bs->bpb.ressec)))
45738451Smsmith        return EINVAL;
45838451Smsmith    fs->lsndir = fs->lsnfat + fs->spf * bs->bpb.fats;
45938451Smsmith    fs->lsndta = fs->lsndir + entsec(fs->dirents);
46038451Smsmith    if (!(sc = cv2(bs->bpb.secs)) && !(sc = cv4(bs->bpb.lsecs)))
46138451Smsmith        return EINVAL;
46238451Smsmith    if (fs->lsndta > sc)
46338451Smsmith        return EINVAL;
46438451Smsmith    if ((fs->xclus = secblk(fs, sc - fs->lsndta) + 1) < LOCLUS)
46538451Smsmith        return EINVAL;
46638451Smsmith    fs->fatsz = fs->dirents ? fs->xclus < 0xff6 ? 12 : 16 : 32;
46738451Smsmith    sc = (secbyt(fs->spf) << 1) / (fs->fatsz >> 2) - 1;
46838451Smsmith    if (fs->xclus > sc)
46938451Smsmith        fs->xclus = sc;
47038451Smsmith    return 0;
47138451Smsmith}
47238451Smsmith
47338451Smsmith/*
47438451Smsmith * Return directory entry from path
47538451Smsmith */
47638451Smsmithstatic int
47738451Smsmithnamede(DOS_FS *fs, const char *path, DOS_DE **dep)
47838451Smsmith{
47938451Smsmith    char name[256];
48038451Smsmith    DOS_DE *de;
48138451Smsmith    char *s;
48238451Smsmith    size_t n;
48338451Smsmith    int err;
48438451Smsmith
48538451Smsmith    err = 0;
48638451Smsmith    de = dot;
48738451Smsmith    if (*path == '/')
48838451Smsmith        path++;
48938451Smsmith    while (*path) {
49038451Smsmith        if (!(s = strchr(path, '/')))
49138451Smsmith            s = strchr(path, 0);
49238451Smsmith        if ((n = s - path) > 255)
49338451Smsmith            return ENAMETOOLONG;
49438451Smsmith        memcpy(name, path, n);
49538451Smsmith        name[n] = 0;
49638451Smsmith        path = s;
49738451Smsmith        if (!(de->attr & FA_DIR))
49838451Smsmith            return ENOTDIR;
49938451Smsmith        if ((err = lookup(fs, stclus(fs->fatsz, de), name, &de)))
50038451Smsmith            return err;
50138451Smsmith        if (*path == '/')
50238451Smsmith            path++;
50338451Smsmith    }
50438451Smsmith    *dep = de;
50538451Smsmith    return 0;
50638451Smsmith}
50738451Smsmith
50838451Smsmith/*
50938451Smsmith * Lookup path segment
51038451Smsmith */
51138451Smsmithstatic int
51238451Smsmithlookup(DOS_FS *fs, u_int clus, const char *name, DOS_DE **dep)
51338451Smsmith{
51438451Smsmith    static DOS_DIR dir[DEPSEC];
51538451Smsmith    u_char lfn[261];
51638451Smsmith    u_char sfn[13];
51738451Smsmith    u_int nsec, lsec, xdn, chk, sec, ent, x;
51838451Smsmith    int err, ok, i;
51938451Smsmith
52038451Smsmith    if (!clus)
52138451Smsmith        for (ent = 0; ent < 2; ent++)
52238451Smsmith            if (!strcasecmp(name, dotstr[ent])) {
52338451Smsmith                *dep = dot + ent;
52438451Smsmith                return 0;
52538451Smsmith            }
52638451Smsmith    if (!clus && fs->fatsz == 32)
52738451Smsmith        clus = fs->rdcl;
52838451Smsmith    nsec = !clus ? entsec(fs->dirents) : fs->spc;
52938451Smsmith    lsec = 0;
53038451Smsmith    xdn = chk = 0;
53138451Smsmith    for (;;) {
53238451Smsmith        if (!clus && !lsec)
53338451Smsmith            lsec = fs->lsndir;
53438451Smsmith        else if (okclus(fs, clus))
53538451Smsmith            lsec = blklsn(fs, clus);
53638451Smsmith        else
53738451Smsmith            return EINVAL;
53838451Smsmith        for (sec = 0; sec < nsec; sec++) {
53938451Smsmith            if ((err = ioget(fs->fd, lsec + sec, dir, 1)))
54038451Smsmith                return err;
54138451Smsmith            for (ent = 0; ent < DEPSEC; ent++) {
54238451Smsmith                if (!*dir[ent].de.name)
54338451Smsmith                    return ENOENT;
54446079Simp                if (*dir[ent].de.name != 0xe5) {
54538451Smsmith                    if ((dir[ent].de.attr & FA_MASK) == FA_XDE) {
54638451Smsmith                        x = dir[ent].xde.seq;
54738451Smsmith                        if (x & 0x40 || (x + 1 == xdn &&
54838451Smsmith                                         dir[ent].xde.chk == chk)) {
54938451Smsmith                            if (x & 0x40) {
55038451Smsmith                                chk = dir[ent].xde.chk;
55138451Smsmith                                x &= ~0x40;
55238451Smsmith                            }
55338451Smsmith                            if (x >= 1 && x <= 20) {
55438451Smsmith                                cp_xdnm(lfn, &dir[ent].xde);
55538451Smsmith                                xdn = x;
55638451Smsmith                                continue;
55738451Smsmith                            }
55838451Smsmith                        }
55938451Smsmith                    } else if (!(dir[ent].de.attr & FA_LABEL)) {
56038451Smsmith                        if ((ok = xdn == 1)) {
56138451Smsmith                            for (x = 0, i = 0; i < 11; i++)
56238451Smsmith                                x = ((((x & 1) << 7) | (x >> 1)) +
56338451Smsmith                                     dir[ent].de.name[i]) & 0xff;
56438451Smsmith                            ok = chk == x &&
56538451Smsmith                                !strcasecmp(name, (const char *)lfn);
56638451Smsmith                        }
56738451Smsmith                        if (!ok) {
56838451Smsmith                            cp_sfn(sfn, &dir[ent].de);
56938451Smsmith                            ok = !strcasecmp(name, (const char *)sfn);
57038451Smsmith                        }
57138451Smsmith                        if (ok) {
57238451Smsmith                            *dep = &dir[ent].de;
57338451Smsmith                            return 0;
57438451Smsmith                        }
57538451Smsmith                    }
57646079Simp		}
57738451Smsmith                xdn = 0;
57838451Smsmith            }
57938451Smsmith        }
58038451Smsmith        if (!clus)
58138451Smsmith            break;
58238451Smsmith        if ((err = fatget(fs, &clus)))
58338451Smsmith            return err;
58438451Smsmith        if (fatend(fs->fatsz, clus))
58538451Smsmith            break;
58638451Smsmith    }
58738451Smsmith    return ENOENT;
58838451Smsmith}
58938451Smsmith
59038451Smsmith/*
59138451Smsmith * Copy name from extended directory entry
59238451Smsmith */
59338451Smsmithstatic void
59438451Smsmithcp_xdnm(u_char *lfn, DOS_XDE *xde)
59538451Smsmith{
59638451Smsmith    static struct {
59738451Smsmith        u_int off;
59838451Smsmith        u_int dim;
59938451Smsmith    } ix[3] = {
60038451Smsmith        {offsetof(DOS_XDE, name1), sizeof(xde->name1) / 2},
60138451Smsmith        {offsetof(DOS_XDE, name2), sizeof(xde->name2) / 2},
60238451Smsmith        {offsetof(DOS_XDE, name3), sizeof(xde->name3) / 2}
60338451Smsmith    };
60438451Smsmith    u_char *p;
60538451Smsmith    u_int n, x, c;
60638451Smsmith
60738451Smsmith    lfn += 13 * ((xde->seq & ~0x40) - 1);
60838451Smsmith    for (n = 0; n < 3; n++)
60938451Smsmith        for (p = (u_char *)xde + ix[n].off, x = ix[n].dim; x;
61038451Smsmith	     p += 2, x--) {
61138451Smsmith            if ((c = cv2(p)) && (c < 32 || c > 127))
61238451Smsmith                c = '?';
61338451Smsmith            if (!(*lfn++ = c))
61438451Smsmith                return;
61538451Smsmith        }
61638451Smsmith    if (xde->seq & 0x40)
61738451Smsmith        *lfn = 0;
61838451Smsmith}
61938451Smsmith
62038451Smsmith/*
62138451Smsmith * Copy short filename
62238451Smsmith */
62338451Smsmithstatic void
62438451Smsmithcp_sfn(u_char *sfn, DOS_DE *de)
62538451Smsmith{
62638451Smsmith    u_char *p;
62738451Smsmith    int j, i;
62838451Smsmith
62938451Smsmith    p = sfn;
63038451Smsmith    if (*de->name != ' ') {
63138451Smsmith        for (j = 7; de->name[j] == ' '; j--);
63238451Smsmith        for (i = 0; i <= j; i++)
63338451Smsmith            *p++ = de->name[i];
63438451Smsmith        if (*de->ext != ' ') {
63538451Smsmith            *p++ = '.';
63638451Smsmith            for (j = 2; de->ext[j] == ' '; j--);
63738451Smsmith            for (i = 0; i <= j; i++)
63838451Smsmith                *p++ = de->ext[i];
63938451Smsmith        }
64038451Smsmith    }
64138451Smsmith    *p = 0;
64238451Smsmith    if (*sfn == 5)
64338451Smsmith        *sfn = 0xe5;
64438451Smsmith}
64538451Smsmith
64638451Smsmith/*
64738582Srnordier * Return size of file in bytes
64838582Srnordier */
64938582Srnordierstatic off_t
65038582Srnordierfsize(DOS_FS *fs, DOS_DE *de)
65138582Srnordier{
65238582Srnordier   u_long size;
65338582Srnordier   u_int c;
65438582Srnordier   int n;
65538582Srnordier
65646079Simp   if (!(size = cv4(de->size)) && de->attr & FA_DIR) {
65738582Srnordier      if (!(c = cv2(de->clus)))
65838582Srnordier         size = fs->dirents * sizeof(DOS_DE);
65938582Srnordier      else {
66038582Srnordier         if ((n = fatcnt(fs, c)) == -1)
66138582Srnordier            return n;
66238582Srnordier         size = blkbyt(fs, n);
66338582Srnordier      }
66446079Simp   }
66538582Srnordier   return size;
66638582Srnordier}
66738582Srnordier
66838582Srnordier/*
66938582Srnordier * Count number of clusters in chain
67038582Srnordier */
67138582Srnordierstatic int
67238582Srnordierfatcnt(DOS_FS *fs, u_int c)
67338582Srnordier{
67438582Srnordier   int n;
67538582Srnordier
67638582Srnordier   for (n = 0; okclus(fs, c); n++)
67738582Srnordier      if (fatget(fs, &c))
67838582Srnordier	  return -1;
67938582Srnordier   return fatend(fs->fatsz, c) ? n : -1;
68038582Srnordier}
68138582Srnordier
68238582Srnordier/*
68338451Smsmith * Get next cluster in cluster chain
68438451Smsmith */
68538451Smsmithstatic int
68638451Smsmithfatget(DOS_FS *fs, u_int *c)
68738451Smsmith{
68838451Smsmith    u_char buf[4];
68938451Smsmith    u_int x;
69038451Smsmith    int err;
69138451Smsmith
69238451Smsmith    err = ioread(fs, secbyt(fs->lsnfat) + fatoff(fs->fatsz, *c), buf,
69338451Smsmith                 fs->fatsz != 32 ? 2 : 4);
69438451Smsmith    if (err)
69538451Smsmith        return err;
69638451Smsmith    x = fs->fatsz != 32 ? cv2(buf) : cv4(buf);
69738451Smsmith    *c = fs->fatsz == 12 ? *c & 1 ? x >> 4 : x & 0xfff : x;
69838451Smsmith    return 0;
69938451Smsmith}
70038451Smsmith
70138451Smsmith/*
70238451Smsmith * Is cluster an end-of-chain marker?
70338451Smsmith */
70438451Smsmithstatic int
70538451Smsmithfatend(u_int sz, u_int c)
70638451Smsmith{
70738451Smsmith    return c > (sz == 12 ? 0xff7U : sz == 16 ? 0xfff7U : 0xffffff7);
70838451Smsmith}
70938451Smsmith
71038451Smsmith/*
71138451Smsmith * Offset-based I/O primitive
71238451Smsmith */
71338451Smsmithstatic int
71438451Smsmithioread(DOS_FS *fs, u_int offset, void *buf, u_int nbyte)
71538451Smsmith{
71638451Smsmith    char *s;
71738451Smsmith    u_int off, n;
71838451Smsmith    int err;
71938451Smsmith
72038451Smsmith    s = buf;
72138451Smsmith    if ((off = offset & (SECSIZ - 1))) {
72238451Smsmith        offset -= off;
72338451Smsmith        if ((err = iobuf(fs, bytsec(offset))))
72438451Smsmith            return err;
72538451Smsmith        offset += SECSIZ;
72638451Smsmith        if ((n = SECSIZ - off) > nbyte)
72738451Smsmith            n = nbyte;
72838451Smsmith        memcpy(s, fs->buf + off, n);
72938451Smsmith        s += n;
73038451Smsmith        nbyte -= n;
73138451Smsmith    }
73238451Smsmith    n = nbyte & (SECSIZ - 1);
73338451Smsmith    if (nbyte -= n) {
73438451Smsmith        if ((err = ioget(fs->fd, bytsec(offset), s, bytsec(nbyte))))
73538451Smsmith            return err;
73638451Smsmith        offset += nbyte;
73738451Smsmith        s += nbyte;
73838451Smsmith    }
73938451Smsmith    if (n) {
74038451Smsmith        if ((err = iobuf(fs, bytsec(offset))))
74138451Smsmith            return err;
74238451Smsmith        memcpy(s, fs->buf, n);
74338451Smsmith    }
74438451Smsmith    return 0;
74538451Smsmith}
74638451Smsmith
74738451Smsmith/*
74838451Smsmith * Buffered sector-based I/O primitive
74938451Smsmith */
75038451Smsmithstatic int
75138451Smsmithiobuf(DOS_FS *fs, u_int lsec)
75238451Smsmith{
75338451Smsmith    int err;
75438451Smsmith
75538451Smsmith    if (fs->bufsec != lsec) {
75638451Smsmith        if ((err = ioget(fs->fd, lsec, fs->buf, 1)))
75738451Smsmith            return err;
75838451Smsmith        fs->bufsec = lsec;
75938451Smsmith    }
76038451Smsmith    return 0;
76138451Smsmith}
76238451Smsmith
76338451Smsmith/*
76438451Smsmith * Sector-based I/O primitive
76538451Smsmith */
76638451Smsmithstatic int
76738451Smsmithioget(struct open_file *fd, u_int lsec, void *buf, u_int nsec)
76838451Smsmith{
76938451Smsmith    int	err;
77038451Smsmith
77138451Smsmith    if ((err = (fd->f_dev->dv_strategy)(fd->f_devdata, F_READ, lsec,
77238451Smsmith					secbyt(nsec), buf, NULL)))
77338451Smsmith	return(err);
77438451Smsmith    return(0);
77538451Smsmith}
776