xref: /minix3/sys/lib/libsa/dosfs.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc /*	$NetBSD: dosfs.c,v 1.20 2014/03/20 03:13:18 christos Exp $	*/
258a2b000SEvgeniy Ivanov 
358a2b000SEvgeniy Ivanov /*
458a2b000SEvgeniy Ivanov  * Copyright (c) 1996, 1998 Robert Nordier
558a2b000SEvgeniy Ivanov  * All rights reserved.
658a2b000SEvgeniy Ivanov  *
758a2b000SEvgeniy Ivanov  * Redistribution and use in source and binary forms, with or without
858a2b000SEvgeniy Ivanov  * modification, are permitted provided that the following conditions
958a2b000SEvgeniy Ivanov  * are met:
1058a2b000SEvgeniy Ivanov  * 1. Redistributions of source code must retain the above copyright
1158a2b000SEvgeniy Ivanov  *    notice, this list of conditions and the following disclaimer.
1258a2b000SEvgeniy Ivanov  * 2. Redistributions in binary form must reproduce the above copyright
1358a2b000SEvgeniy Ivanov  *    notice, this list of conditions and the following disclaimer in
1458a2b000SEvgeniy Ivanov  *    the documentation and/or other materials provided with the
1558a2b000SEvgeniy Ivanov  *    distribution.
1658a2b000SEvgeniy Ivanov  *
1758a2b000SEvgeniy Ivanov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
1858a2b000SEvgeniy Ivanov  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1958a2b000SEvgeniy Ivanov  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2058a2b000SEvgeniy Ivanov  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
2158a2b000SEvgeniy Ivanov  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2258a2b000SEvgeniy Ivanov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
2358a2b000SEvgeniy Ivanov  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2458a2b000SEvgeniy Ivanov  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
2558a2b000SEvgeniy Ivanov  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
2658a2b000SEvgeniy Ivanov  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
2758a2b000SEvgeniy Ivanov  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2858a2b000SEvgeniy Ivanov  */
2958a2b000SEvgeniy Ivanov 
3058a2b000SEvgeniy Ivanov /*
3158a2b000SEvgeniy Ivanov  * Readonly filesystem for Microsoft FAT12/FAT16/FAT32 filesystems,
3258a2b000SEvgeniy Ivanov  * also supports VFAT.
3358a2b000SEvgeniy Ivanov  */
3458a2b000SEvgeniy Ivanov 
3558a2b000SEvgeniy Ivanov /*
3658a2b000SEvgeniy Ivanov  * XXX DOES NOT SUPPORT:
3758a2b000SEvgeniy Ivanov  *
3858a2b000SEvgeniy Ivanov  *	LIBSA_FS_SINGLECOMPONENT
3958a2b000SEvgeniy Ivanov  */
4058a2b000SEvgeniy Ivanov 
4158a2b000SEvgeniy Ivanov #include <sys/param.h>
4258a2b000SEvgeniy Ivanov 
4358a2b000SEvgeniy Ivanov #include <fs/msdosfs/bpb.h>
4458a2b000SEvgeniy Ivanov #include <fs/msdosfs/direntry.h>
4558a2b000SEvgeniy Ivanov 
4658a2b000SEvgeniy Ivanov #ifdef _STANDALONE
4758a2b000SEvgeniy Ivanov #include <lib/libkern/libkern.h>
4858a2b000SEvgeniy Ivanov #else
4958a2b000SEvgeniy Ivanov #include <string.h>
5058a2b000SEvgeniy Ivanov #include <stddef.h>
5158a2b000SEvgeniy Ivanov #endif
5258a2b000SEvgeniy Ivanov 
5358a2b000SEvgeniy Ivanov #include "stand.h"
5458a2b000SEvgeniy Ivanov #include "dosfs.h"
5558a2b000SEvgeniy Ivanov 
5658a2b000SEvgeniy Ivanov #define SECSIZ  512		/* sector size */
5758a2b000SEvgeniy Ivanov #define SSHIFT    9		/* SECSIZ shift */
5858a2b000SEvgeniy Ivanov #define DEPSEC   16		/* directory entries per sector */
5958a2b000SEvgeniy Ivanov #define DSHIFT    4		/* DEPSEC shift */
6058a2b000SEvgeniy Ivanov #define LOCLUS    2		/* lowest cluster number */
6158a2b000SEvgeniy Ivanov 
6258a2b000SEvgeniy Ivanov typedef union {
6358a2b000SEvgeniy Ivanov 	struct direntry de;	/* standard directory entry */
6458a2b000SEvgeniy Ivanov 	struct winentry xde;	/* extended directory entry */
6558a2b000SEvgeniy Ivanov } DOS_DIR;
6658a2b000SEvgeniy Ivanov 
6758a2b000SEvgeniy Ivanov typedef struct {
6858a2b000SEvgeniy Ivanov 	struct open_file *fd;	/* file descriptor */
6958a2b000SEvgeniy Ivanov 	u_char *buf;		/* buffer */
7058a2b000SEvgeniy Ivanov 	u_int   bufsec;		/* buffered sector */
7158a2b000SEvgeniy Ivanov 	u_int   links;		/* active links to structure */
7258a2b000SEvgeniy Ivanov 	u_int   spc;		/* sectors per cluster */
7358a2b000SEvgeniy Ivanov 	u_int   bsize;		/* cluster size in bytes */
7458a2b000SEvgeniy Ivanov 	u_int   bshift;		/* cluster conversion shift */
7558a2b000SEvgeniy Ivanov 	u_int   dirents;	/* root directory entries */
7658a2b000SEvgeniy Ivanov 	u_int   spf;		/* sectors per fat */
7758a2b000SEvgeniy Ivanov 	u_int   rdcl;		/* root directory start cluster */
7858a2b000SEvgeniy Ivanov 	u_int   lsnfat;		/* start of fat */
7958a2b000SEvgeniy Ivanov 	u_int   lsndir;		/* start of root dir */
8058a2b000SEvgeniy Ivanov 	u_int   lsndta;		/* start of data area */
8158a2b000SEvgeniy Ivanov 	u_int   fatsz;		/* FAT entry size */
8258a2b000SEvgeniy Ivanov 	u_int   xclus;		/* maximum cluster number */
8358a2b000SEvgeniy Ivanov } DOS_FS;
8458a2b000SEvgeniy Ivanov 
8558a2b000SEvgeniy Ivanov typedef struct {
8658a2b000SEvgeniy Ivanov 	DOS_FS *fs;		/* associated filesystem */
8758a2b000SEvgeniy Ivanov 	struct direntry de;	/* directory entry */
8858a2b000SEvgeniy Ivanov 	u_int   offset;		/* current offset */
8958a2b000SEvgeniy Ivanov 	u_int   c;		/* last cluster read */
9058a2b000SEvgeniy Ivanov } DOS_FILE;
9158a2b000SEvgeniy Ivanov 
9258a2b000SEvgeniy Ivanov /* Initial portion of DOS boot sector */
9358a2b000SEvgeniy Ivanov typedef struct {
9458a2b000SEvgeniy Ivanov 	u_char  jmp[3];		/* usually 80x86 'jmp' opcode */
9558a2b000SEvgeniy Ivanov 	u_char  oem[8];		/* OEM name and version */
9658a2b000SEvgeniy Ivanov 	struct byte_bpb710 bpb;	/* BPB */
9758a2b000SEvgeniy Ivanov } DOS_BS;
9858a2b000SEvgeniy Ivanov 
9958a2b000SEvgeniy Ivanov /* Supply missing "." and ".." root directory entries */
10058a2b000SEvgeniy Ivanov static const char *const dotstr[2] = {".", ".."};
10158a2b000SEvgeniy Ivanov static const struct direntry dot[2] = {
10258a2b000SEvgeniy Ivanov 	{".       ", "   ", ATTR_DIRECTORY,
10358a2b000SEvgeniy Ivanov 		0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0},
10458a2b000SEvgeniy Ivanov 		{0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}},
10558a2b000SEvgeniy Ivanov 
10658a2b000SEvgeniy Ivanov 	{"..      ", "   ", ATTR_DIRECTORY,
10758a2b000SEvgeniy Ivanov 		0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0},
10858a2b000SEvgeniy Ivanov 		{0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}}
10958a2b000SEvgeniy Ivanov };
11058a2b000SEvgeniy Ivanov 
11158a2b000SEvgeniy Ivanov /* The usual conversion macros to avoid multiplication and division */
11258a2b000SEvgeniy Ivanov #define bytsec(n)      ((n) >> SSHIFT)
11358a2b000SEvgeniy Ivanov #define secbyt(s)      ((s) << SSHIFT)
11458a2b000SEvgeniy Ivanov #define entsec(e)      ((e) >> DSHIFT)
11558a2b000SEvgeniy Ivanov #define bytblk(fs, n)  ((n) >> (fs)->bshift)
11658a2b000SEvgeniy Ivanov #define blkbyt(fs, b)  ((b) << (fs)->bshift)
11758a2b000SEvgeniy Ivanov #define secblk(fs, s)  ((s) >> ((fs)->bshift - SSHIFT))
11858a2b000SEvgeniy Ivanov #define blksec(fs, b)  ((b) << ((fs)->bshift - SSHIFT))
11958a2b000SEvgeniy Ivanov 
12058a2b000SEvgeniy Ivanov /* Convert cluster number to offset within filesystem */
12158a2b000SEvgeniy Ivanov #define blkoff(fs, b) (secbyt((fs)->lsndta) + blkbyt(fs, (b) - LOCLUS))
12258a2b000SEvgeniy Ivanov 
12358a2b000SEvgeniy Ivanov /* Convert cluster number to logical sector number */
12458a2b000SEvgeniy Ivanov #define blklsn(fs, b)  ((fs)->lsndta + blksec(fs, (b) - LOCLUS))
12558a2b000SEvgeniy Ivanov 
12658a2b000SEvgeniy Ivanov /* Convert cluster number to offset within FAT */
12758a2b000SEvgeniy Ivanov #define fatoff(sz, c)  ((sz) == 12 ? (c) + ((c) >> 1) :  \
12858a2b000SEvgeniy Ivanov                         (sz) == 16 ? (c) << 1 :          \
12958a2b000SEvgeniy Ivanov                         (c) << 2)
13058a2b000SEvgeniy Ivanov 
13158a2b000SEvgeniy Ivanov /* Does cluster number reference a valid data cluster? */
13258a2b000SEvgeniy Ivanov #define okclus(fs, c)  ((c) >= LOCLUS && (c) <= (fs)->xclus)
13358a2b000SEvgeniy Ivanov 
13458a2b000SEvgeniy Ivanov /* Get start cluster from directory entry */
13558a2b000SEvgeniy Ivanov #define stclus(sz, de)  ((sz) != 32 ? (u_int)getushort((de)->deStartCluster) : \
13658a2b000SEvgeniy Ivanov                          ((u_int)getushort((de)->deHighClust) << 16) |  \
13758a2b000SEvgeniy Ivanov                          (u_int)getushort((de)->deStartCluster))
13858a2b000SEvgeniy Ivanov 
13958a2b000SEvgeniy Ivanov static int dosunmount(DOS_FS *);
14058a2b000SEvgeniy Ivanov static int parsebs(DOS_FS *, DOS_BS *);
14158a2b000SEvgeniy Ivanov static int namede(DOS_FS *, const char *, const struct direntry **);
14258a2b000SEvgeniy Ivanov static int lookup(DOS_FS *, u_int, const char *, const struct direntry **);
14358a2b000SEvgeniy Ivanov static void cp_xdnm(u_char *, struct winentry *);
14458a2b000SEvgeniy Ivanov static void cp_sfn(u_char *, struct direntry *);
14558a2b000SEvgeniy Ivanov static off_t fsize(DOS_FS *, struct direntry *);
14658a2b000SEvgeniy Ivanov static int fatcnt(DOS_FS *, u_int);
14758a2b000SEvgeniy Ivanov static int fatget(DOS_FS *, u_int *);
14858a2b000SEvgeniy Ivanov static int fatend(u_int, u_int);
14958a2b000SEvgeniy Ivanov static int ioread(DOS_FS *, u_int, void *, u_int);
15058a2b000SEvgeniy Ivanov static int iobuf(DOS_FS *, u_int);
15158a2b000SEvgeniy Ivanov static int ioget(struct open_file *, u_int, void *, u_int);
15258a2b000SEvgeniy Ivanov 
15358a2b000SEvgeniy Ivanov #define strcasecmp(s1, s2) dos_strcasecmp(s1, s2)
15458a2b000SEvgeniy Ivanov static int
strcasecmp(const char * s1,const char * s2)15558a2b000SEvgeniy Ivanov strcasecmp(const char *s1, const char *s2)
15658a2b000SEvgeniy Ivanov {
15758a2b000SEvgeniy Ivanov 	char c1, c2;
15858a2b000SEvgeniy Ivanov 	#define TO_UPPER(c) ((c) >= 'a' && (c) <= 'z' ? (c) - ('a' - 'A') : (c))
15958a2b000SEvgeniy Ivanov 	for (;;) {
16058a2b000SEvgeniy Ivanov 		c1 = *s1++;
16158a2b000SEvgeniy Ivanov 		c2 = *s2++;
16258a2b000SEvgeniy Ivanov 		if (TO_UPPER(c1) != TO_UPPER(c2))
16358a2b000SEvgeniy Ivanov 			return 1;
16458a2b000SEvgeniy Ivanov 		if (c1 == 0)
16558a2b000SEvgeniy Ivanov 			return 0;
16658a2b000SEvgeniy Ivanov 	}
16758a2b000SEvgeniy Ivanov 	#undef TO_UPPER
16858a2b000SEvgeniy Ivanov }
16958a2b000SEvgeniy Ivanov 
17058a2b000SEvgeniy Ivanov /*
17158a2b000SEvgeniy Ivanov  * Mount DOS filesystem
17258a2b000SEvgeniy Ivanov  */
17358a2b000SEvgeniy Ivanov static int
dos_mount(DOS_FS * fs,struct open_file * fd)17458a2b000SEvgeniy Ivanov dos_mount(DOS_FS *fs, struct open_file *fd)
17558a2b000SEvgeniy Ivanov {
17658a2b000SEvgeniy Ivanov 	int     err;
17758a2b000SEvgeniy Ivanov 
17858a2b000SEvgeniy Ivanov 	(void)memset(fs, 0, sizeof(DOS_FS));
17958a2b000SEvgeniy Ivanov 	fs->fd = fd;
18058a2b000SEvgeniy Ivanov 	if ((err = !(fs->buf = alloc(SECSIZ)) ? errno : 0) ||
18158a2b000SEvgeniy Ivanov 	    (err = ioget(fs->fd, 0, fs->buf, 1)) ||
18258a2b000SEvgeniy Ivanov 	    (err = parsebs(fs, (DOS_BS *)fs->buf))) {
18358a2b000SEvgeniy Ivanov 		(void) dosunmount(fs);
18458a2b000SEvgeniy Ivanov 		return err;
18558a2b000SEvgeniy Ivanov 	}
18658a2b000SEvgeniy Ivanov 	return 0;
18758a2b000SEvgeniy Ivanov }
18858a2b000SEvgeniy Ivanov 
18958a2b000SEvgeniy Ivanov #ifndef LIBSA_NO_FS_CLOSE
19058a2b000SEvgeniy Ivanov /*
19158a2b000SEvgeniy Ivanov  * Unmount mounted filesystem
19258a2b000SEvgeniy Ivanov  */
19358a2b000SEvgeniy Ivanov static int
dos_unmount(DOS_FS * fs)19458a2b000SEvgeniy Ivanov dos_unmount(DOS_FS *fs)
19558a2b000SEvgeniy Ivanov {
19658a2b000SEvgeniy Ivanov 	int     err;
19758a2b000SEvgeniy Ivanov 
19858a2b000SEvgeniy Ivanov 	if (fs->links)
19958a2b000SEvgeniy Ivanov 		return EBUSY;
20058a2b000SEvgeniy Ivanov 	if ((err = dosunmount(fs)))
20158a2b000SEvgeniy Ivanov 		return err;
20258a2b000SEvgeniy Ivanov 	return 0;
20358a2b000SEvgeniy Ivanov }
20458a2b000SEvgeniy Ivanov #endif
20558a2b000SEvgeniy Ivanov 
20658a2b000SEvgeniy Ivanov /*
20758a2b000SEvgeniy Ivanov  * Common code shared by dos_mount() and dos_unmount()
20858a2b000SEvgeniy Ivanov  */
20958a2b000SEvgeniy Ivanov static int
dosunmount(DOS_FS * fs)21058a2b000SEvgeniy Ivanov dosunmount(DOS_FS *fs)
21158a2b000SEvgeniy Ivanov {
21258a2b000SEvgeniy Ivanov 	if (fs->buf)
21358a2b000SEvgeniy Ivanov 		dealloc(fs->buf, SECSIZ);
21458a2b000SEvgeniy Ivanov 	dealloc(fs, sizeof(DOS_FS));
21558a2b000SEvgeniy Ivanov 	return 0;
21658a2b000SEvgeniy Ivanov }
21758a2b000SEvgeniy Ivanov 
21858a2b000SEvgeniy Ivanov /*
21958a2b000SEvgeniy Ivanov  * Open DOS file
22058a2b000SEvgeniy Ivanov  */
22158a2b000SEvgeniy Ivanov __compactcall int
dosfs_open(const char * path,struct open_file * fd)22258a2b000SEvgeniy Ivanov dosfs_open(const char *path, struct open_file *fd)
22358a2b000SEvgeniy Ivanov {
22458a2b000SEvgeniy Ivanov 	const struct direntry *de;
22558a2b000SEvgeniy Ivanov 	DOS_FILE *f;
22658a2b000SEvgeniy Ivanov 	DOS_FS *fs;
22758a2b000SEvgeniy Ivanov 	u_int   size, clus;
22858a2b000SEvgeniy Ivanov 	int     err = 0;
22958a2b000SEvgeniy Ivanov 
23058a2b000SEvgeniy Ivanov 	/* Allocate mount structure, associate with open */
23158a2b000SEvgeniy Ivanov 	fs = alloc(sizeof(DOS_FS));
23258a2b000SEvgeniy Ivanov 
23358a2b000SEvgeniy Ivanov 	if ((err = dos_mount(fs, fd)))
23458a2b000SEvgeniy Ivanov 		goto out;
23558a2b000SEvgeniy Ivanov 
23658a2b000SEvgeniy Ivanov 	if ((err = namede(fs, path, &de)))
23758a2b000SEvgeniy Ivanov 		goto out;
23858a2b000SEvgeniy Ivanov 
23958a2b000SEvgeniy Ivanov 	clus = stclus(fs->fatsz, de);
24058a2b000SEvgeniy Ivanov 	size = getulong(de->deFileSize);
24158a2b000SEvgeniy Ivanov 
24258a2b000SEvgeniy Ivanov 	if ((!(de->deAttributes & ATTR_DIRECTORY) && (!clus != !size)) ||
24358a2b000SEvgeniy Ivanov 	    ((de->deAttributes & ATTR_DIRECTORY) && size) ||
24458a2b000SEvgeniy Ivanov 	    (clus && !okclus(fs, clus))) {
24558a2b000SEvgeniy Ivanov 		err = EINVAL;
24658a2b000SEvgeniy Ivanov 		goto out;
24758a2b000SEvgeniy Ivanov 	}
24858a2b000SEvgeniy Ivanov 
24958a2b000SEvgeniy Ivanov 	f = alloc(sizeof(DOS_FILE));
25058a2b000SEvgeniy Ivanov #ifdef BOOTXX
25158a2b000SEvgeniy Ivanov 	/* due to __internal_memset_ causing all sorts of register spillage
25258a2b000SEvgeniy Ivanov 	   (and being completely unoptimized for zeroing small amounts of
25358a2b000SEvgeniy Ivanov 	   memory), if we hand-initialize the remaining members of f to zero,
25458a2b000SEvgeniy Ivanov 	   the code size drops 68 bytes. This makes no sense, admittedly. */
25558a2b000SEvgeniy Ivanov 	f->offset = 0;
25658a2b000SEvgeniy Ivanov 	f->c = 0;
25758a2b000SEvgeniy Ivanov #else
25858a2b000SEvgeniy Ivanov 	(void)memset(f, 0, sizeof(DOS_FILE));
25958a2b000SEvgeniy Ivanov #endif
26058a2b000SEvgeniy Ivanov 	f->fs = fs;
26158a2b000SEvgeniy Ivanov 	fs->links++;
26258a2b000SEvgeniy Ivanov 	f->de = *de;
26358a2b000SEvgeniy Ivanov 	fd->f_fsdata = (void *)f;
26458a2b000SEvgeniy Ivanov 	fsmod = "msdos";
26558a2b000SEvgeniy Ivanov 
26658a2b000SEvgeniy Ivanov out:
26758a2b000SEvgeniy Ivanov 	return err;
26858a2b000SEvgeniy Ivanov }
26958a2b000SEvgeniy Ivanov 
27058a2b000SEvgeniy Ivanov /*
27158a2b000SEvgeniy Ivanov  * Read from file
27258a2b000SEvgeniy Ivanov  */
27358a2b000SEvgeniy Ivanov __compactcall int
dosfs_read(struct open_file * fd,void * vbuf,size_t nbyte,size_t * resid)27458a2b000SEvgeniy Ivanov dosfs_read(struct open_file *fd, void *vbuf, size_t nbyte, size_t *resid)
27558a2b000SEvgeniy Ivanov {
27658a2b000SEvgeniy Ivanov 	off_t   size;
27758a2b000SEvgeniy Ivanov 	u_int8_t *buf = vbuf;
27858a2b000SEvgeniy Ivanov 	u_int   nb, off, clus, c, cnt, n;
27958a2b000SEvgeniy Ivanov 	DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
28058a2b000SEvgeniy Ivanov 	int     err = 0;
28158a2b000SEvgeniy Ivanov 
28258a2b000SEvgeniy Ivanov 	nb = (u_int) nbyte;
28358a2b000SEvgeniy Ivanov 	if ((size = fsize(f->fs, &f->de)) == -1)
28458a2b000SEvgeniy Ivanov 		return EINVAL;
28558a2b000SEvgeniy Ivanov 	if (nb > (n = size - f->offset))
28658a2b000SEvgeniy Ivanov 		nb = n;
28758a2b000SEvgeniy Ivanov 	off = f->offset;
28858a2b000SEvgeniy Ivanov 	if ((clus = stclus(f->fs->fatsz, &f->de)))
28958a2b000SEvgeniy Ivanov 		off &= f->fs->bsize - 1;
29058a2b000SEvgeniy Ivanov 	c = f->c;
29158a2b000SEvgeniy Ivanov 	cnt = nb;
29258a2b000SEvgeniy Ivanov 	while (cnt) {
29358a2b000SEvgeniy Ivanov 		n = 0;
29458a2b000SEvgeniy Ivanov 		if (!c) {
29558a2b000SEvgeniy Ivanov 			if ((c = clus))
29658a2b000SEvgeniy Ivanov 				n = bytblk(f->fs, f->offset);
29758a2b000SEvgeniy Ivanov 		} else if (!off) {
29858a2b000SEvgeniy Ivanov 			n++;
29958a2b000SEvgeniy Ivanov 		}
30058a2b000SEvgeniy Ivanov 		while (n--) {
30158a2b000SEvgeniy Ivanov 			if ((err = fatget(f->fs, &c)))
30258a2b000SEvgeniy Ivanov 				goto out;
30358a2b000SEvgeniy Ivanov 			if (!okclus(f->fs, c)) {
30458a2b000SEvgeniy Ivanov 				err = EINVAL;
30558a2b000SEvgeniy Ivanov 				goto out;
30658a2b000SEvgeniy Ivanov 			}
30758a2b000SEvgeniy Ivanov 		}
30858a2b000SEvgeniy Ivanov 		if (!clus || (n = f->fs->bsize - off) > cnt)
30958a2b000SEvgeniy Ivanov 			n = cnt;
31058a2b000SEvgeniy Ivanov 		if ((err = ioread(f->fs, (c ? blkoff(f->fs, c) :
31158a2b000SEvgeniy Ivanov 				secbyt(f->fs->lsndir)) + off,
31258a2b000SEvgeniy Ivanov 			    buf, n)))
31358a2b000SEvgeniy Ivanov 			goto out;
31458a2b000SEvgeniy Ivanov 		f->offset += n;
31558a2b000SEvgeniy Ivanov 		f->c = c;
31658a2b000SEvgeniy Ivanov 		off = 0;
31758a2b000SEvgeniy Ivanov 		buf += n;
31858a2b000SEvgeniy Ivanov 		cnt -= n;
31958a2b000SEvgeniy Ivanov 	}
32058a2b000SEvgeniy Ivanov out:
32158a2b000SEvgeniy Ivanov 	if (resid)
32258a2b000SEvgeniy Ivanov 		*resid = nbyte - nb + cnt;
32358a2b000SEvgeniy Ivanov 	return err;
32458a2b000SEvgeniy Ivanov }
32558a2b000SEvgeniy Ivanov 
32658a2b000SEvgeniy Ivanov #ifndef LIBSA_NO_FS_WRITE
32758a2b000SEvgeniy Ivanov /*
32858a2b000SEvgeniy Ivanov  * Not implemented.
32958a2b000SEvgeniy Ivanov  */
33058a2b000SEvgeniy Ivanov __compactcall int
dosfs_write(struct open_file * fd,void * start,size_t size,size_t * resid)33158a2b000SEvgeniy Ivanov dosfs_write(struct open_file *fd, void *start, size_t size, size_t *resid)
33258a2b000SEvgeniy Ivanov {
33358a2b000SEvgeniy Ivanov 
33458a2b000SEvgeniy Ivanov 	return EROFS;
33558a2b000SEvgeniy Ivanov }
33658a2b000SEvgeniy Ivanov #endif /* !LIBSA_NO_FS_WRITE */
33758a2b000SEvgeniy Ivanov 
33858a2b000SEvgeniy Ivanov #ifndef LIBSA_NO_FS_SEEK
33958a2b000SEvgeniy Ivanov /*
34058a2b000SEvgeniy Ivanov  * Reposition within file
34158a2b000SEvgeniy Ivanov  */
34258a2b000SEvgeniy Ivanov __compactcall off_t
dosfs_seek(struct open_file * fd,off_t offset,int whence)34358a2b000SEvgeniy Ivanov dosfs_seek(struct open_file *fd, off_t offset, int whence)
34458a2b000SEvgeniy Ivanov {
34558a2b000SEvgeniy Ivanov 	off_t   off;
34658a2b000SEvgeniy Ivanov 	u_int   size;
34758a2b000SEvgeniy Ivanov 	DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
34858a2b000SEvgeniy Ivanov 
34958a2b000SEvgeniy Ivanov 	size = getulong(f->de.deFileSize);
35058a2b000SEvgeniy Ivanov 	switch (whence) {
35158a2b000SEvgeniy Ivanov 	case SEEK_SET:
35258a2b000SEvgeniy Ivanov 		off = 0;
35358a2b000SEvgeniy Ivanov 		break;
35458a2b000SEvgeniy Ivanov 	case SEEK_CUR:
35558a2b000SEvgeniy Ivanov 		off = f->offset;
35658a2b000SEvgeniy Ivanov 		break;
35758a2b000SEvgeniy Ivanov 	case SEEK_END:
35858a2b000SEvgeniy Ivanov 		off = size;
35958a2b000SEvgeniy Ivanov 		break;
36058a2b000SEvgeniy Ivanov 	default:
36158a2b000SEvgeniy Ivanov 		return -1;
36258a2b000SEvgeniy Ivanov 	}
36358a2b000SEvgeniy Ivanov 	off += offset;
36458a2b000SEvgeniy Ivanov 	if (off < 0 || off > size)
36558a2b000SEvgeniy Ivanov 		return -1;
36658a2b000SEvgeniy Ivanov 	f->offset = (u_int) off;
36758a2b000SEvgeniy Ivanov 	f->c = 0;
36858a2b000SEvgeniy Ivanov 	return off;
36958a2b000SEvgeniy Ivanov }
37058a2b000SEvgeniy Ivanov #endif /* !LIBSA_NO_FS_SEEK */
37158a2b000SEvgeniy Ivanov 
37258a2b000SEvgeniy Ivanov #ifndef LIBSA_NO_FS_CLOSE
37358a2b000SEvgeniy Ivanov /*
37458a2b000SEvgeniy Ivanov  * Close open file
37558a2b000SEvgeniy Ivanov  */
37658a2b000SEvgeniy Ivanov __compactcall int
dosfs_close(struct open_file * fd)37758a2b000SEvgeniy Ivanov dosfs_close(struct open_file *fd)
37858a2b000SEvgeniy Ivanov {
37958a2b000SEvgeniy Ivanov 	DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
38058a2b000SEvgeniy Ivanov 	DOS_FS *fs = f->fs;
38158a2b000SEvgeniy Ivanov 
38258a2b000SEvgeniy Ivanov 	f->fs->links--;
38358a2b000SEvgeniy Ivanov 	dealloc(f, sizeof(DOS_FILE));
38458a2b000SEvgeniy Ivanov 	dos_unmount(fs);
38558a2b000SEvgeniy Ivanov 	return 0;
38658a2b000SEvgeniy Ivanov }
38758a2b000SEvgeniy Ivanov #endif /* !LIBSA_NO_FS_CLOSE */
38858a2b000SEvgeniy Ivanov 
38958a2b000SEvgeniy Ivanov /*
39058a2b000SEvgeniy Ivanov  * Return some stat information on a file.
39158a2b000SEvgeniy Ivanov  */
39258a2b000SEvgeniy Ivanov __compactcall int
dosfs_stat(struct open_file * fd,struct stat * sb)39358a2b000SEvgeniy Ivanov dosfs_stat(struct open_file *fd, struct stat *sb)
39458a2b000SEvgeniy Ivanov {
39558a2b000SEvgeniy Ivanov 	DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
39658a2b000SEvgeniy Ivanov 
39758a2b000SEvgeniy Ivanov 	/* only important stuff */
39858a2b000SEvgeniy Ivanov 	sb->st_mode = (f->de.deAttributes & ATTR_DIRECTORY) ?
39958a2b000SEvgeniy Ivanov 	    (S_IFDIR | 0555) : (S_IFREG | 0444);
40058a2b000SEvgeniy Ivanov 	sb->st_nlink = 1;
40158a2b000SEvgeniy Ivanov 	sb->st_uid = 0;
40258a2b000SEvgeniy Ivanov 	sb->st_gid = 0;
40358a2b000SEvgeniy Ivanov 	if ((sb->st_size = fsize(f->fs, &f->de)) == -1)
40458a2b000SEvgeniy Ivanov 		return EINVAL;
40558a2b000SEvgeniy Ivanov 	return 0;
40658a2b000SEvgeniy Ivanov }
40758a2b000SEvgeniy Ivanov 
40858a2b000SEvgeniy Ivanov #if defined(LIBSA_ENABLE_LS_OP)
409*0a6a1f1dSLionel Sambuc #include "ls.h"
41058a2b000SEvgeniy Ivanov __compactcall void
dosfs_ls(struct open_file * f,const char * pattern)411*0a6a1f1dSLionel Sambuc dosfs_ls(struct open_file *f, const char *pattern)
41258a2b000SEvgeniy Ivanov {
413*0a6a1f1dSLionel Sambuc 	lsunsup("dosfs");
41458a2b000SEvgeniy Ivanov }
415*0a6a1f1dSLionel Sambuc 
416*0a6a1f1dSLionel Sambuc #if defined(__minix) && defined(LIBSA_ENABLE_LOAD_MODS_OP)
417*0a6a1f1dSLionel Sambuc __compactcall void
dosfs_load_mods(struct open_file * f,const char * pattern,void (* funcp)(char *),char * path)418*0a6a1f1dSLionel Sambuc dosfs_load_mods(struct open_file *f, const char *pattern,
419*0a6a1f1dSLionel Sambuc 	void (*funcp)(char *), char *path)
420*0a6a1f1dSLionel Sambuc {
421*0a6a1f1dSLionel Sambuc 	load_modsunsup("dosfs");
422*0a6a1f1dSLionel Sambuc }
423*0a6a1f1dSLionel Sambuc #endif /* defined(__minix) && defined(LIBSA_ENABLE_LOAD_MODS_OP) */
42458a2b000SEvgeniy Ivanov #endif
42558a2b000SEvgeniy Ivanov 
42658a2b000SEvgeniy Ivanov /*
42758a2b000SEvgeniy Ivanov  * Parse DOS boot sector
42858a2b000SEvgeniy Ivanov  */
42958a2b000SEvgeniy Ivanov static int
parsebs(DOS_FS * fs,DOS_BS * bs)43058a2b000SEvgeniy Ivanov parsebs(DOS_FS *fs, DOS_BS *bs)
43158a2b000SEvgeniy Ivanov {
43258a2b000SEvgeniy Ivanov 	u_int   sc;
43358a2b000SEvgeniy Ivanov 
43458a2b000SEvgeniy Ivanov 	if ((bs->jmp[0] != 0x69 &&
43558a2b000SEvgeniy Ivanov 		bs->jmp[0] != 0xe9 &&
43658a2b000SEvgeniy Ivanov 		(bs->jmp[0] != 0xeb || bs->jmp[2] != 0x90)) ||
43758a2b000SEvgeniy Ivanov 	    bs->bpb.bpbMedia < 0xf0)
43858a2b000SEvgeniy Ivanov 		return EINVAL;
43958a2b000SEvgeniy Ivanov 	if (getushort(bs->bpb.bpbBytesPerSec) != SECSIZ)
44058a2b000SEvgeniy Ivanov 		return EINVAL;
44158a2b000SEvgeniy Ivanov 	if (!(fs->spc = bs->bpb.bpbSecPerClust) || fs->spc & (fs->spc - 1))
44258a2b000SEvgeniy Ivanov 		return EINVAL;
44358a2b000SEvgeniy Ivanov 	fs->bsize = secbyt(fs->spc);
44458a2b000SEvgeniy Ivanov 	fs->bshift = ffs(fs->bsize) - 1;
44558a2b000SEvgeniy Ivanov 	if ((fs->spf = getushort(bs->bpb.bpbFATsecs))) {
44658a2b000SEvgeniy Ivanov 		if (bs->bpb.bpbFATs != 2)
44758a2b000SEvgeniy Ivanov 			return EINVAL;
44858a2b000SEvgeniy Ivanov 		if (!(fs->dirents = getushort(bs->bpb.bpbRootDirEnts)))
44958a2b000SEvgeniy Ivanov 			return EINVAL;
45058a2b000SEvgeniy Ivanov 	} else {
45158a2b000SEvgeniy Ivanov 		if (!(fs->spf = getulong(bs->bpb.bpbBigFATsecs)))
45258a2b000SEvgeniy Ivanov 			return EINVAL;
45358a2b000SEvgeniy Ivanov 		if (!bs->bpb.bpbFATs || bs->bpb.bpbFATs > 16)
45458a2b000SEvgeniy Ivanov 			return EINVAL;
45558a2b000SEvgeniy Ivanov 		if ((fs->rdcl = getulong(bs->bpb.bpbRootClust)) < LOCLUS)
45658a2b000SEvgeniy Ivanov 			return EINVAL;
45758a2b000SEvgeniy Ivanov 	}
45858a2b000SEvgeniy Ivanov 	if (!(fs->lsnfat = getushort(bs->bpb.bpbResSectors)))
45958a2b000SEvgeniy Ivanov 		return EINVAL;
46058a2b000SEvgeniy Ivanov 	fs->lsndir = fs->lsnfat + fs->spf * bs->bpb.bpbFATs;
46158a2b000SEvgeniy Ivanov 	fs->lsndta = fs->lsndir + entsec(fs->dirents);
46258a2b000SEvgeniy Ivanov 	if (!(sc = getushort(bs->bpb.bpbSectors)) &&
46358a2b000SEvgeniy Ivanov 	    !(sc = getulong(bs->bpb.bpbHugeSectors)))
46458a2b000SEvgeniy Ivanov 		return EINVAL;
46558a2b000SEvgeniy Ivanov 	if (fs->lsndta > sc)
46658a2b000SEvgeniy Ivanov 		return EINVAL;
46758a2b000SEvgeniy Ivanov 	if ((fs->xclus = secblk(fs, sc - fs->lsndta) + 1) < LOCLUS)
46858a2b000SEvgeniy Ivanov 		return EINVAL;
46958a2b000SEvgeniy Ivanov 	fs->fatsz = fs->dirents ? fs->xclus < 0xff6 ? 12 : 16 : 32;
47058a2b000SEvgeniy Ivanov 	sc = (secbyt(fs->spf) << 1) / (fs->fatsz >> 2) - 1;
47158a2b000SEvgeniy Ivanov 	if (fs->xclus > sc)
47258a2b000SEvgeniy Ivanov 		fs->xclus = sc;
47358a2b000SEvgeniy Ivanov 	return 0;
47458a2b000SEvgeniy Ivanov }
47558a2b000SEvgeniy Ivanov 
47658a2b000SEvgeniy Ivanov /*
47758a2b000SEvgeniy Ivanov  * Return directory entry from path
47858a2b000SEvgeniy Ivanov  */
47958a2b000SEvgeniy Ivanov static int
namede(DOS_FS * fs,const char * path,const struct direntry ** dep)48058a2b000SEvgeniy Ivanov namede(DOS_FS *fs, const char *path, const struct direntry **dep)
48158a2b000SEvgeniy Ivanov {
48258a2b000SEvgeniy Ivanov 	char    name[256];
48358a2b000SEvgeniy Ivanov 	const struct direntry *de;
48458a2b000SEvgeniy Ivanov 	char   *s;
48558a2b000SEvgeniy Ivanov 	size_t  n;
48658a2b000SEvgeniy Ivanov 	int     err;
48758a2b000SEvgeniy Ivanov 
48858a2b000SEvgeniy Ivanov 	err = 0;
48958a2b000SEvgeniy Ivanov 	de = dot;
49058a2b000SEvgeniy Ivanov 	if (*path == '/')
49158a2b000SEvgeniy Ivanov 		path++;
49258a2b000SEvgeniy Ivanov 	while (*path) {
49358a2b000SEvgeniy Ivanov 		if (!(s = strchr(path, '/')))
49458a2b000SEvgeniy Ivanov 			s = strchr(path, 0);
49558a2b000SEvgeniy Ivanov 		if ((n = s - path) > 255)
49658a2b000SEvgeniy Ivanov 			return ENAMETOOLONG;
49758a2b000SEvgeniy Ivanov 		memcpy(name, path, n);
49858a2b000SEvgeniy Ivanov 		name[n] = 0;
49958a2b000SEvgeniy Ivanov 		path = s;
50058a2b000SEvgeniy Ivanov 		if (!(de->deAttributes & ATTR_DIRECTORY))
50158a2b000SEvgeniy Ivanov 			return ENOTDIR;
50258a2b000SEvgeniy Ivanov 		if ((err = lookup(fs, stclus(fs->fatsz, de), name, &de)))
50358a2b000SEvgeniy Ivanov 			return err;
50458a2b000SEvgeniy Ivanov 		if (*path == '/')
50558a2b000SEvgeniy Ivanov 			path++;
50658a2b000SEvgeniy Ivanov 	}
50758a2b000SEvgeniy Ivanov 	*dep = de;
50858a2b000SEvgeniy Ivanov 	return 0;
50958a2b000SEvgeniy Ivanov }
51058a2b000SEvgeniy Ivanov 
51158a2b000SEvgeniy Ivanov /*
51258a2b000SEvgeniy Ivanov  * Lookup path segment
51358a2b000SEvgeniy Ivanov  */
51458a2b000SEvgeniy Ivanov static int
lookup(DOS_FS * fs,u_int clus,const char * name,const struct direntry ** dep)51558a2b000SEvgeniy Ivanov lookup(DOS_FS *fs, u_int clus, const char *name, const struct direntry **dep)
51658a2b000SEvgeniy Ivanov {
51758a2b000SEvgeniy Ivanov 	static DOS_DIR *dir = NULL;
51858a2b000SEvgeniy Ivanov 	u_char  lfn[261];
51958a2b000SEvgeniy Ivanov 	u_char  sfn[13];
52058a2b000SEvgeniy Ivanov 	u_int   nsec, lsec, xdn, chk, sec, ent, x;
52158a2b000SEvgeniy Ivanov 	int     err = 0, ok, i;
52258a2b000SEvgeniy Ivanov 
52358a2b000SEvgeniy Ivanov 	if (!clus)
52458a2b000SEvgeniy Ivanov 		for (ent = 0; ent < 2; ent++)
52558a2b000SEvgeniy Ivanov 			if (!strcasecmp(name, dotstr[ent])) {
52658a2b000SEvgeniy Ivanov 				*dep = dot + ent;
52758a2b000SEvgeniy Ivanov 				return 0;
52858a2b000SEvgeniy Ivanov 			}
52958a2b000SEvgeniy Ivanov 
53058a2b000SEvgeniy Ivanov 	if (dir == NULL) {
53158a2b000SEvgeniy Ivanov 		dir = alloc(sizeof(DOS_DIR) * DEPSEC);
53258a2b000SEvgeniy Ivanov 		if (dir == NULL)
53358a2b000SEvgeniy Ivanov 			return ENOMEM;
53458a2b000SEvgeniy Ivanov 	}
53558a2b000SEvgeniy Ivanov 
53658a2b000SEvgeniy Ivanov 	if (!clus && fs->fatsz == 32)
53758a2b000SEvgeniy Ivanov 		clus = fs->rdcl;
53858a2b000SEvgeniy Ivanov 	nsec = !clus ? entsec(fs->dirents) : fs->spc;
53958a2b000SEvgeniy Ivanov 	lsec = 0;
54058a2b000SEvgeniy Ivanov 	xdn = chk = 0;
54158a2b000SEvgeniy Ivanov 	for (;;) {
54258a2b000SEvgeniy Ivanov 		if (!clus && !lsec)
54358a2b000SEvgeniy Ivanov 			lsec = fs->lsndir;
54458a2b000SEvgeniy Ivanov 		else if (okclus(fs, clus))
54558a2b000SEvgeniy Ivanov 			lsec = blklsn(fs, clus);
54658a2b000SEvgeniy Ivanov 		else {
54758a2b000SEvgeniy Ivanov 			err = EINVAL;
54858a2b000SEvgeniy Ivanov 			goto out;
54958a2b000SEvgeniy Ivanov 		}
55058a2b000SEvgeniy Ivanov 		for (sec = 0; sec < nsec; sec++) {
55158a2b000SEvgeniy Ivanov 			if ((err = ioget(fs->fd, lsec + sec, dir, 1)))
55258a2b000SEvgeniy Ivanov 				goto out;
55358a2b000SEvgeniy Ivanov 			for (ent = 0; ent < DEPSEC; ent++) {
55458a2b000SEvgeniy Ivanov 				if (!*dir[ent].de.deName) {
55558a2b000SEvgeniy Ivanov 					err = ENOENT;
55658a2b000SEvgeniy Ivanov 					goto out;
55758a2b000SEvgeniy Ivanov 				}
55858a2b000SEvgeniy Ivanov 				if (*dir[ent].de.deName != 0xe5) {
55958a2b000SEvgeniy Ivanov 					if (dir[ent].de.deAttributes ==
56058a2b000SEvgeniy Ivanov 					    ATTR_WIN95) {
56158a2b000SEvgeniy Ivanov 						x = dir[ent].xde.weCnt;
56258a2b000SEvgeniy Ivanov 						if (x & WIN_LAST ||
56358a2b000SEvgeniy Ivanov 						    (x + 1 == xdn &&
56458a2b000SEvgeniy Ivanov 						     dir[ent].xde.weChksum ==
56558a2b000SEvgeniy Ivanov 						     chk)) {
56658a2b000SEvgeniy Ivanov 							if (x & WIN_LAST) {
56758a2b000SEvgeniy Ivanov 								chk = dir[ent].xde.weChksum;
56858a2b000SEvgeniy Ivanov 								x &= WIN_CNT;
56958a2b000SEvgeniy Ivanov 							}
57058a2b000SEvgeniy Ivanov 							if (x >= 1 && x <= 20) {
57158a2b000SEvgeniy Ivanov 								cp_xdnm(lfn, &dir[ent].xde);
57258a2b000SEvgeniy Ivanov 								xdn = x;
57358a2b000SEvgeniy Ivanov 								continue;
57458a2b000SEvgeniy Ivanov 							}
57558a2b000SEvgeniy Ivanov 						}
57658a2b000SEvgeniy Ivanov 					} else if (!(dir[ent].de.deAttributes &
57758a2b000SEvgeniy Ivanov 						     ATTR_VOLUME)) {
57858a2b000SEvgeniy Ivanov 						if ((ok = xdn == 1)) {
57958a2b000SEvgeniy Ivanov 							for (x = 0, i = 0;
58058a2b000SEvgeniy Ivanov 							     i < 11; i++)
58158a2b000SEvgeniy Ivanov 								x = ((((x & 1) << 7) | (x >> 1)) +
58284d9c625SLionel Sambuc 								    msdos_dirchar(&dir[ent].de,i)) & 0xff;
58358a2b000SEvgeniy Ivanov 							ok = chk == x &&
58458a2b000SEvgeniy Ivanov 							    !strcasecmp(name, (const char *)lfn);
58558a2b000SEvgeniy Ivanov 						}
58658a2b000SEvgeniy Ivanov 						if (!ok) {
58758a2b000SEvgeniy Ivanov 							cp_sfn(sfn, &dir[ent].de);
58858a2b000SEvgeniy Ivanov 							ok = !strcasecmp(name, (const char *)sfn);
58958a2b000SEvgeniy Ivanov 						}
59058a2b000SEvgeniy Ivanov 						if (ok) {
59158a2b000SEvgeniy Ivanov 							*dep = &dir[ent].de;
59258a2b000SEvgeniy Ivanov 							goto out2;
59358a2b000SEvgeniy Ivanov 						}
59458a2b000SEvgeniy Ivanov 					}
59558a2b000SEvgeniy Ivanov 				}
59658a2b000SEvgeniy Ivanov 				xdn = 0;
59758a2b000SEvgeniy Ivanov 			}
59858a2b000SEvgeniy Ivanov 		}
59958a2b000SEvgeniy Ivanov 		if (!clus)
60058a2b000SEvgeniy Ivanov 			break;
60158a2b000SEvgeniy Ivanov 		if ((err = fatget(fs, &clus)))
60258a2b000SEvgeniy Ivanov 			goto out;
60358a2b000SEvgeniy Ivanov 		if (fatend(fs->fatsz, clus))
60458a2b000SEvgeniy Ivanov 			break;
60558a2b000SEvgeniy Ivanov 	}
60658a2b000SEvgeniy Ivanov 	err = ENOENT;
60758a2b000SEvgeniy Ivanov  out:
60858a2b000SEvgeniy Ivanov 	dealloc(dir, sizeof(DOS_DIR) * DEPSEC);
60958a2b000SEvgeniy Ivanov 	dir = NULL;
61058a2b000SEvgeniy Ivanov  out2:
61158a2b000SEvgeniy Ivanov 	return err;
61258a2b000SEvgeniy Ivanov }
61358a2b000SEvgeniy Ivanov 
61458a2b000SEvgeniy Ivanov /*
61558a2b000SEvgeniy Ivanov  * Copy name from extended directory entry
61658a2b000SEvgeniy Ivanov  */
61758a2b000SEvgeniy Ivanov static void
cp_xdnm(u_char * lfn,struct winentry * xde)61858a2b000SEvgeniy Ivanov cp_xdnm(u_char *lfn, struct winentry *xde)
61958a2b000SEvgeniy Ivanov {
62058a2b000SEvgeniy Ivanov 	static const struct {
62158a2b000SEvgeniy Ivanov 		u_int   off;
62258a2b000SEvgeniy Ivanov 		u_int   dim;
62358a2b000SEvgeniy Ivanov 	} ix[3] = {
62458a2b000SEvgeniy Ivanov 		{ offsetof(struct winentry, wePart1),
62558a2b000SEvgeniy Ivanov 		    sizeof(xde->wePart1) / 2 },
62658a2b000SEvgeniy Ivanov 		{ offsetof(struct winentry, wePart2),
62758a2b000SEvgeniy Ivanov 		    sizeof(xde->wePart2) / 2 },
62858a2b000SEvgeniy Ivanov 		{ offsetof(struct winentry, wePart3),
62958a2b000SEvgeniy Ivanov 		    sizeof(xde->wePart3) / 2 }
63058a2b000SEvgeniy Ivanov 	};
63158a2b000SEvgeniy Ivanov 	u_char *p;
63258a2b000SEvgeniy Ivanov 	u_int   n, x, c;
63358a2b000SEvgeniy Ivanov 
63458a2b000SEvgeniy Ivanov 	lfn += 13 * ((xde->weCnt & WIN_CNT) - 1);
63558a2b000SEvgeniy Ivanov 	for (n = 0; n < 3; n++)
63658a2b000SEvgeniy Ivanov 		for (p = (u_char *)xde + ix[n].off, x = ix[n].dim; x;
63758a2b000SEvgeniy Ivanov 		    p += 2, x--) {
63858a2b000SEvgeniy Ivanov 			if ((c = getushort(p)) && (c < 32 || c > 127))
63958a2b000SEvgeniy Ivanov 				c = '?';
64058a2b000SEvgeniy Ivanov 			if (!(*lfn++ = c))
64158a2b000SEvgeniy Ivanov 				return;
64258a2b000SEvgeniy Ivanov 		}
64358a2b000SEvgeniy Ivanov 	if (xde->weCnt & WIN_LAST)
64458a2b000SEvgeniy Ivanov 		*lfn = 0;
64558a2b000SEvgeniy Ivanov }
64658a2b000SEvgeniy Ivanov 
64758a2b000SEvgeniy Ivanov /*
64858a2b000SEvgeniy Ivanov  * Copy short filename
64958a2b000SEvgeniy Ivanov  */
65058a2b000SEvgeniy Ivanov static void
cp_sfn(u_char * sfn,struct direntry * de)65158a2b000SEvgeniy Ivanov cp_sfn(u_char *sfn, struct direntry *de)
65258a2b000SEvgeniy Ivanov {
65358a2b000SEvgeniy Ivanov 	u_char *p;
65458a2b000SEvgeniy Ivanov 	int     j, i;
65558a2b000SEvgeniy Ivanov 
65658a2b000SEvgeniy Ivanov 	p = sfn;
65758a2b000SEvgeniy Ivanov 	if (*de->deName != ' ') {
65858a2b000SEvgeniy Ivanov 		for (j = 7; de->deName[j] == ' '; j--);
65958a2b000SEvgeniy Ivanov 		for (i = 0; i <= j; i++)
66058a2b000SEvgeniy Ivanov 			*p++ = de->deName[i];
66158a2b000SEvgeniy Ivanov 		if (*de->deExtension != ' ') {
66258a2b000SEvgeniy Ivanov 			*p++ = '.';
66358a2b000SEvgeniy Ivanov 			for (j = 2; de->deExtension[j] == ' '; j--);
66458a2b000SEvgeniy Ivanov 			for (i = 0; i <= j; i++)
66558a2b000SEvgeniy Ivanov 				*p++ = de->deExtension[i];
66658a2b000SEvgeniy Ivanov 		}
66758a2b000SEvgeniy Ivanov 	}
66858a2b000SEvgeniy Ivanov 	*p = 0;
66958a2b000SEvgeniy Ivanov 	if (*sfn == 5)
67058a2b000SEvgeniy Ivanov 		*sfn = 0xe5;
67158a2b000SEvgeniy Ivanov }
67258a2b000SEvgeniy Ivanov 
67358a2b000SEvgeniy Ivanov /*
67458a2b000SEvgeniy Ivanov  * Return size of file in bytes
67558a2b000SEvgeniy Ivanov  */
67658a2b000SEvgeniy Ivanov static  off_t
fsize(DOS_FS * fs,struct direntry * de)67758a2b000SEvgeniy Ivanov fsize(DOS_FS *fs, struct direntry *de)
67858a2b000SEvgeniy Ivanov {
67958a2b000SEvgeniy Ivanov 	u_long  size;
68058a2b000SEvgeniy Ivanov 	u_int   c;
68158a2b000SEvgeniy Ivanov 	int     n;
68258a2b000SEvgeniy Ivanov 
68358a2b000SEvgeniy Ivanov 	if (!(size = getulong(de->deFileSize)) &&
68458a2b000SEvgeniy Ivanov 	    de->deAttributes & ATTR_DIRECTORY) {
68558a2b000SEvgeniy Ivanov 		if (!(c = getushort(de->deStartCluster))) {
68658a2b000SEvgeniy Ivanov 			size = fs->dirents * sizeof(struct direntry);
68758a2b000SEvgeniy Ivanov 		} else {
68858a2b000SEvgeniy Ivanov 			if ((n = fatcnt(fs, c)) == -1)
68958a2b000SEvgeniy Ivanov 				return n;
69058a2b000SEvgeniy Ivanov 			size = blkbyt(fs, n);
69158a2b000SEvgeniy Ivanov 		}
69258a2b000SEvgeniy Ivanov 	}
69358a2b000SEvgeniy Ivanov 	return size;
69458a2b000SEvgeniy Ivanov }
69558a2b000SEvgeniy Ivanov 
69658a2b000SEvgeniy Ivanov /*
69758a2b000SEvgeniy Ivanov  * Count number of clusters in chain
69858a2b000SEvgeniy Ivanov  */
69958a2b000SEvgeniy Ivanov static int
fatcnt(DOS_FS * fs,u_int c)70058a2b000SEvgeniy Ivanov fatcnt(DOS_FS *fs, u_int c)
70158a2b000SEvgeniy Ivanov {
70258a2b000SEvgeniy Ivanov 	int     n;
70358a2b000SEvgeniy Ivanov 
70458a2b000SEvgeniy Ivanov 	for (n = 0; okclus(fs, c); n++)
70558a2b000SEvgeniy Ivanov 		if (fatget(fs, &c))
70658a2b000SEvgeniy Ivanov 			return -1;
70758a2b000SEvgeniy Ivanov 	return fatend(fs->fatsz, c) ? n : -1;
70858a2b000SEvgeniy Ivanov }
70958a2b000SEvgeniy Ivanov 
71058a2b000SEvgeniy Ivanov /*
71158a2b000SEvgeniy Ivanov  * Get next cluster in cluster chain
71258a2b000SEvgeniy Ivanov  */
71358a2b000SEvgeniy Ivanov static int
fatget(DOS_FS * fs,u_int * c)71458a2b000SEvgeniy Ivanov fatget(DOS_FS *fs, u_int *c)
71558a2b000SEvgeniy Ivanov {
71658a2b000SEvgeniy Ivanov 	u_char  buf[4];
71758a2b000SEvgeniy Ivanov 	u_int   x;
71858a2b000SEvgeniy Ivanov 	int     err;
71958a2b000SEvgeniy Ivanov 
72058a2b000SEvgeniy Ivanov 	err = ioread(fs, secbyt(fs->lsnfat) + fatoff(fs->fatsz, *c), buf,
72158a2b000SEvgeniy Ivanov 	    fs->fatsz != 32 ? 2 : 4);
72258a2b000SEvgeniy Ivanov 	if (err)
72358a2b000SEvgeniy Ivanov 		return err;
72458a2b000SEvgeniy Ivanov 	x = fs->fatsz != 32 ? getushort(buf) : getulong(buf);
72558a2b000SEvgeniy Ivanov 	*c = fs->fatsz == 12 ? *c & 1 ? x >> 4 : x & 0xfff : x;
72658a2b000SEvgeniy Ivanov 	return 0;
72758a2b000SEvgeniy Ivanov }
72858a2b000SEvgeniy Ivanov 
72958a2b000SEvgeniy Ivanov /*
73058a2b000SEvgeniy Ivanov  * Is cluster an end-of-chain marker?
73158a2b000SEvgeniy Ivanov  */
73258a2b000SEvgeniy Ivanov static int
fatend(u_int sz,u_int c)73358a2b000SEvgeniy Ivanov fatend(u_int sz, u_int c)
73458a2b000SEvgeniy Ivanov {
73558a2b000SEvgeniy Ivanov 	return c > (sz == 12 ? 0xff7U : sz == 16 ? 0xfff7U : 0xffffff7);
73658a2b000SEvgeniy Ivanov }
73758a2b000SEvgeniy Ivanov 
73858a2b000SEvgeniy Ivanov /*
73958a2b000SEvgeniy Ivanov  * Offset-based I/O primitive
74058a2b000SEvgeniy Ivanov  */
74158a2b000SEvgeniy Ivanov static int
ioread(DOS_FS * fs,u_int offset,void * buf,u_int nbyte)74258a2b000SEvgeniy Ivanov ioread(DOS_FS *fs, u_int offset, void *buf, u_int nbyte)
74358a2b000SEvgeniy Ivanov {
74458a2b000SEvgeniy Ivanov 	char   *s;
74558a2b000SEvgeniy Ivanov 	u_int   off, n;
74658a2b000SEvgeniy Ivanov 	int     err;
74758a2b000SEvgeniy Ivanov 
74858a2b000SEvgeniy Ivanov 	s = buf;
74958a2b000SEvgeniy Ivanov 	if ((off = offset & (SECSIZ - 1))) {
75058a2b000SEvgeniy Ivanov 		offset -= off;
75158a2b000SEvgeniy Ivanov 		if ((err = iobuf(fs, bytsec(offset))))
75258a2b000SEvgeniy Ivanov 			return err;
75358a2b000SEvgeniy Ivanov 		offset += SECSIZ;
75458a2b000SEvgeniy Ivanov 		if ((n = SECSIZ - off) > nbyte)
75558a2b000SEvgeniy Ivanov 			n = nbyte;
75658a2b000SEvgeniy Ivanov 		memcpy(s, fs->buf + off, n);
75758a2b000SEvgeniy Ivanov 		s += n;
75858a2b000SEvgeniy Ivanov 		nbyte -= n;
75958a2b000SEvgeniy Ivanov 	}
76058a2b000SEvgeniy Ivanov 	n = nbyte & (SECSIZ - 1);
76158a2b000SEvgeniy Ivanov 	if (nbyte -= n) {
76258a2b000SEvgeniy Ivanov 		if ((err = ioget(fs->fd, bytsec(offset), s, bytsec(nbyte))))
76358a2b000SEvgeniy Ivanov 			return err;
76458a2b000SEvgeniy Ivanov 		offset += nbyte;
76558a2b000SEvgeniy Ivanov 		s += nbyte;
76658a2b000SEvgeniy Ivanov 	}
76758a2b000SEvgeniy Ivanov 	if (n) {
76858a2b000SEvgeniy Ivanov 		if ((err = iobuf(fs, bytsec(offset))))
76958a2b000SEvgeniy Ivanov 			return err;
77058a2b000SEvgeniy Ivanov 		memcpy(s, fs->buf, n);
77158a2b000SEvgeniy Ivanov 	}
77258a2b000SEvgeniy Ivanov 	return 0;
77358a2b000SEvgeniy Ivanov }
77458a2b000SEvgeniy Ivanov 
77558a2b000SEvgeniy Ivanov /*
77658a2b000SEvgeniy Ivanov  * Buffered sector-based I/O primitive
77758a2b000SEvgeniy Ivanov  */
77858a2b000SEvgeniy Ivanov static int
iobuf(DOS_FS * fs,u_int lsec)77958a2b000SEvgeniy Ivanov iobuf(DOS_FS *fs, u_int lsec)
78058a2b000SEvgeniy Ivanov {
78158a2b000SEvgeniy Ivanov 	int     err;
78258a2b000SEvgeniy Ivanov 
78358a2b000SEvgeniy Ivanov 	if (fs->bufsec != lsec) {
78458a2b000SEvgeniy Ivanov 		if ((err = ioget(fs->fd, lsec, fs->buf, 1)))
78558a2b000SEvgeniy Ivanov 			return err;
78658a2b000SEvgeniy Ivanov 		fs->bufsec = lsec;
78758a2b000SEvgeniy Ivanov 	}
78858a2b000SEvgeniy Ivanov 	return 0;
78958a2b000SEvgeniy Ivanov }
79058a2b000SEvgeniy Ivanov 
79158a2b000SEvgeniy Ivanov /*
79258a2b000SEvgeniy Ivanov  * Sector-based I/O primitive
79358a2b000SEvgeniy Ivanov  */
79458a2b000SEvgeniy Ivanov static int
ioget(struct open_file * fd,u_int lsec,void * buf,u_int nsec)79558a2b000SEvgeniy Ivanov ioget(struct open_file *fd, u_int lsec, void *buf, u_int nsec)
79658a2b000SEvgeniy Ivanov {
79758a2b000SEvgeniy Ivanov 	size_t rsize;
79858a2b000SEvgeniy Ivanov 	int err;
79958a2b000SEvgeniy Ivanov 
80058a2b000SEvgeniy Ivanov #ifndef LIBSA_NO_TWIDDLE
80158a2b000SEvgeniy Ivanov 	twiddle();
80258a2b000SEvgeniy Ivanov #endif
80358a2b000SEvgeniy Ivanov 	err = DEV_STRATEGY(fd->f_dev)(fd->f_devdata, F_READ, lsec,
80458a2b000SEvgeniy Ivanov 	    secbyt(nsec), buf, &rsize);
80558a2b000SEvgeniy Ivanov 	return err;
80658a2b000SEvgeniy Ivanov }
807