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