xref: /illumos-gate/usr/src/boot/libsa/dosfs.c (revision 22028508)
1199767f8SToomas Soome /*
2199767f8SToomas Soome  * Copyright (c) 1996, 1998 Robert Nordier
3199767f8SToomas Soome  * All rights reserved.
4199767f8SToomas Soome  *
5199767f8SToomas Soome  * Redistribution and use in source and binary forms, with or without
6199767f8SToomas Soome  * modification, are permitted provided that the following conditions
7199767f8SToomas Soome  * are met:
8199767f8SToomas Soome  * 1. Redistributions of source code must retain the above copyright
9199767f8SToomas Soome  *    notice, this list of conditions and the following disclaimer.
10199767f8SToomas Soome  * 2. Redistributions in binary form must reproduce the above copyright
11199767f8SToomas Soome  *    notice, this list of conditions and the following disclaimer in
12199767f8SToomas Soome  *    the documentation and/or other materials provided with the
13199767f8SToomas Soome  *    distribution.
14199767f8SToomas Soome  *
15199767f8SToomas Soome  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
16199767f8SToomas Soome  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17199767f8SToomas Soome  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18199767f8SToomas Soome  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
19199767f8SToomas Soome  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20199767f8SToomas Soome  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
21199767f8SToomas Soome  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22199767f8SToomas Soome  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
23199767f8SToomas Soome  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24199767f8SToomas Soome  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
25199767f8SToomas Soome  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26199767f8SToomas Soome  */
27199767f8SToomas Soome 
28199767f8SToomas Soome #include <sys/cdefs.h>
29199767f8SToomas Soome 
30199767f8SToomas Soome /*
31199767f8SToomas Soome  * Readonly filesystem for Microsoft FAT12/FAT16/FAT32 filesystems,
32199767f8SToomas Soome  * also supports VFAT.
33199767f8SToomas Soome  */
34199767f8SToomas Soome 
35199767f8SToomas Soome #include <sys/types.h>
36199767f8SToomas Soome #include <string.h>
37199767f8SToomas Soome #include <stddef.h>
38199767f8SToomas Soome 
39199767f8SToomas Soome #include "stand.h"
40199767f8SToomas Soome 
41199767f8SToomas Soome #include "dosfs.h"
42199767f8SToomas Soome 
43199767f8SToomas Soome 
44734b3a42SToomas Soome static int dos_open(const char *, struct open_file *);
45734b3a42SToomas Soome static int dos_close(struct open_file *);
46734b3a42SToomas Soome static int dos_read(struct open_file *, void *, size_t, size_t *);
47734b3a42SToomas Soome static off_t dos_seek(struct open_file *, off_t offset, int);
48734b3a42SToomas Soome static int dos_stat(struct open_file *, struct stat *);
49734b3a42SToomas Soome static int dos_readdir(struct open_file *, struct dirent *);
50199767f8SToomas Soome 
51199767f8SToomas Soome struct fs_ops dosfs_fsops = {
52734b3a42SToomas Soome 	.fs_name = "dosfs",
53734b3a42SToomas Soome 	.fo_open = dos_open,
54734b3a42SToomas Soome 	.fo_close = dos_close,
55734b3a42SToomas Soome 	.fo_read = dos_read,
56734b3a42SToomas Soome 	.fo_write = null_write,
57734b3a42SToomas Soome 	.fo_seek = dos_seek,
58734b3a42SToomas Soome 	.fo_stat = dos_stat,
59734b3a42SToomas Soome 	.fo_readdir = dos_readdir
60199767f8SToomas Soome };
61199767f8SToomas Soome 
62734b3a42SToomas Soome #define	SECSIZ	512		/* sector size */
63734b3a42SToomas Soome #define	SSHIFT	9		/* SECSIZ shift */
64734b3a42SToomas Soome #define	DEPSEC	16		/* directory entries per sector */
65734b3a42SToomas Soome #define	DSHIFT	4		/* DEPSEC shift */
66734b3a42SToomas Soome #define	LOCLUS	2		/* lowest cluster number */
67734b3a42SToomas Soome #define	FATBLKSZ	0x20000	/* size of block in the FAT cache buffer */
68199767f8SToomas Soome 
69199767f8SToomas Soome /* DOS "BIOS Parameter Block" */
70199767f8SToomas Soome typedef struct {
71734b3a42SToomas Soome 	uchar_t secsiz[2];	/* sector size */
72734b3a42SToomas Soome 	uchar_t spc;		/* sectors per cluster */
73734b3a42SToomas Soome 	uchar_t ressec[2];	/* reserved sectors */
74734b3a42SToomas Soome 	uchar_t fats;		/* FATs */
75734b3a42SToomas Soome 	uchar_t dirents[2];	/* root directory entries */
76734b3a42SToomas Soome 	uchar_t secs[2];	/* total sectors */
77734b3a42SToomas Soome 	uchar_t media;		/* media descriptor */
78734b3a42SToomas Soome 	uchar_t spf[2];		/* sectors per FAT */
79734b3a42SToomas Soome 	uchar_t spt[2];		/* sectors per track */
80734b3a42SToomas Soome 	uchar_t heads[2];	/* drive heads */
81734b3a42SToomas Soome 	uchar_t hidsec[4];	/* hidden sectors */
82734b3a42SToomas Soome 	uchar_t lsecs[4];	/* huge sectors */
83734b3a42SToomas Soome 	uchar_t lspf[4];	/* huge sectors per FAT */
84734b3a42SToomas Soome 	uchar_t xflg[2];	/* flags */
85734b3a42SToomas Soome 	uchar_t vers[2];	/* filesystem version */
86734b3a42SToomas Soome 	uchar_t rdcl[4];	/* root directory start cluster */
87734b3a42SToomas Soome 	uchar_t infs[2];	/* filesystem info sector */
88734b3a42SToomas Soome 	uchar_t bkbs[2];	/* backup boot sector */
89199767f8SToomas Soome } DOS_BPB;
90199767f8SToomas Soome 
91199767f8SToomas Soome /* Initial portion of DOS boot sector */
92199767f8SToomas Soome typedef struct {
93734b3a42SToomas Soome 	uchar_t jmp[3];		/* usually 80x86 'jmp' opcode */
94734b3a42SToomas Soome 	uchar_t oem[8];		/* OEM name and version */
95734b3a42SToomas Soome 	DOS_BPB bpb;		/* BPB */
96199767f8SToomas Soome } DOS_BS;
97199767f8SToomas Soome 
98199767f8SToomas Soome /* Supply missing "." and ".." root directory entries */
99199767f8SToomas Soome static const char *const dotstr[2] = {".", ".."};
100199767f8SToomas Soome static DOS_DE dot[2] = {
101734b3a42SToomas Soome 	{".       ", "   ", FA_DIR, {0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
102734b3a42SToomas Soome 	{0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}},
103734b3a42SToomas Soome 	{"..      ", "   ", FA_DIR, {0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
104734b3a42SToomas Soome 	{0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}}
105199767f8SToomas Soome };
106199767f8SToomas Soome 
107199767f8SToomas Soome /* The usual conversion macros to avoid multiplication and division */
108734b3a42SToomas Soome #define	bytsec(n)	((n) >> SSHIFT)
109734b3a42SToomas Soome #define	secbyt(s)	((s) << SSHIFT)
110734b3a42SToomas Soome #define	entsec(e)	((e) >> DSHIFT)
111734b3a42SToomas Soome #define	bytblk(fs, n)	((n) >> (fs)->bshift)
112734b3a42SToomas Soome #define	blkbyt(fs, b)	((b) << (fs)->bshift)
113734b3a42SToomas Soome #define	secblk(fs, s)	((s) >> ((fs)->bshift - SSHIFT))
114734b3a42SToomas Soome #define	blksec(fs, b)	((b) << ((fs)->bshift - SSHIFT))
115199767f8SToomas Soome 
116199767f8SToomas Soome /* Convert cluster number to offset within filesystem */
117734b3a42SToomas Soome #define	blkoff(fs, b)	(secbyt((fs)->lsndta) + blkbyt(fs, (b) - LOCLUS))
118199767f8SToomas Soome 
119199767f8SToomas Soome /* Convert cluster number to logical sector number */
120734b3a42SToomas Soome #define	blklsn(fs, b)	((fs)->lsndta + blksec(fs, (b) - LOCLUS))
121199767f8SToomas Soome 
122199767f8SToomas Soome /* Convert cluster number to offset within FAT */
123734b3a42SToomas Soome #define	fatoff(sz, c)	((sz) == 12 ? (c) + ((c) >> 1) :  \
124734b3a42SToomas Soome 			(sz) == 16 ? (c) << 1 :          \
125199767f8SToomas Soome 			(c) << 2)
126199767f8SToomas Soome 
127199767f8SToomas Soome /* Does cluster number reference a valid data cluster? */
128734b3a42SToomas Soome #define	okclus(fs, c)	((c) >= LOCLUS && (c) <= (fs)->xclus)
129199767f8SToomas Soome 
130199767f8SToomas Soome /* Get start cluster from directory entry */
131734b3a42SToomas Soome #define	stclus(sz, de)	((sz) != 32 ? cv2((de)->clus) :          \
132734b3a42SToomas Soome 			((uint_t)cv2((de)->dex.h_clus) << 16) |  \
133734b3a42SToomas Soome 			cv2((de)->clus))
134199767f8SToomas Soome 
135199767f8SToomas Soome static int parsebs(DOS_FS *, DOS_BS *);
136199767f8SToomas Soome static int namede(DOS_FS *, const char *, DOS_DE **);
137734b3a42SToomas Soome static int lookup(DOS_FS *, uint_t, const char *, DOS_DE **);
138734b3a42SToomas Soome static void cp_xdnm(uchar_t *, DOS_XDE *);
139734b3a42SToomas Soome static void cp_sfn(uchar_t *, DOS_DE *);
140199767f8SToomas Soome static off_t fsize(DOS_FS *, DOS_DE *);
141734b3a42SToomas Soome static int fatcnt(DOS_FS *, uint_t);
142734b3a42SToomas Soome static int fatget(DOS_FS *, uint_t *);
143734b3a42SToomas Soome static int fatend(uint_t, uint_t);
144734b3a42SToomas Soome static int ioread(DOS_FS *, uint_t, void *, size_t);
14563982b82SToomas Soome static int ioget(struct open_file *, daddr_t, void *, size_t);
146199767f8SToomas Soome 
14763982b82SToomas Soome static int
dos_read_fatblk(DOS_FS * fs,struct open_file * fd,uint_t blknum)148734b3a42SToomas Soome dos_read_fatblk(DOS_FS *fs, struct open_file *fd, uint_t blknum)
149199767f8SToomas Soome {
150734b3a42SToomas Soome 	int err;
151734b3a42SToomas Soome 	size_t io_size;
152734b3a42SToomas Soome 	daddr_t offset_in_fat, max_offset_in_fat;
153734b3a42SToomas Soome 
154734b3a42SToomas Soome 	offset_in_fat = ((daddr_t)blknum) * FATBLKSZ;
155130d7652SToomas Soome 	max_offset_in_fat = secbyt((daddr_t)fs->spf);
156734b3a42SToomas Soome 	io_size = FATBLKSZ;
157734b3a42SToomas Soome 	if (offset_in_fat > max_offset_in_fat)
158734b3a42SToomas Soome 		offset_in_fat = max_offset_in_fat;
159734b3a42SToomas Soome 	if (offset_in_fat + io_size > max_offset_in_fat)
160734b3a42SToomas Soome 		io_size = ((size_t)(max_offset_in_fat - offset_in_fat));
161734b3a42SToomas Soome 
162734b3a42SToomas Soome 	if (io_size != 0) {
163734b3a42SToomas Soome 		err = ioget(fd, fs->lsnfat + bytsec(offset_in_fat),
164734b3a42SToomas Soome 		    fs->fatbuf, io_size);
165734b3a42SToomas Soome 		if (err != 0) {
166734b3a42SToomas Soome 			fs->fatbuf_blknum = ((uint_t)(-1));
167734b3a42SToomas Soome 			return (err);
168734b3a42SToomas Soome 		}
169199767f8SToomas Soome 	}
170199767f8SToomas Soome 
171734b3a42SToomas Soome 	if (io_size < FATBLKSZ)
172734b3a42SToomas Soome 		memset(fs->fatbuf + io_size, 0, FATBLKSZ - io_size);
173199767f8SToomas Soome 
174734b3a42SToomas Soome 	fs->fatbuf_blknum = blknum;
175734b3a42SToomas Soome 	return (0);
176199767f8SToomas Soome }
177199767f8SToomas Soome 
178199767f8SToomas Soome /*
179199767f8SToomas Soome  * Mount DOS filesystem
180199767f8SToomas Soome  */
181199767f8SToomas Soome static int
dos_mount(DOS_FS * fs,struct open_file * fd)182199767f8SToomas Soome dos_mount(DOS_FS *fs, struct open_file *fd)
183199767f8SToomas Soome {
184734b3a42SToomas Soome 	int err;
185734b3a42SToomas Soome 	uchar_t *buf;
186734b3a42SToomas Soome 
187734b3a42SToomas Soome 	bzero(fs, sizeof (DOS_FS));
188734b3a42SToomas Soome 	fs->fd = fd;
189734b3a42SToomas Soome 
190734b3a42SToomas Soome 	if ((buf = malloc(secbyt(1))) == NULL)
191734b3a42SToomas Soome 		return (errno);
192734b3a42SToomas Soome 	if ((err = ioget(fs->fd, 0, buf, secbyt(1))) ||
193734b3a42SToomas Soome 	    (err = parsebs(fs, (DOS_BS *)buf))) {
194734b3a42SToomas Soome 		free(buf);
195734b3a42SToomas Soome 		return (err);
196734b3a42SToomas Soome 	}
197734b3a42SToomas Soome 	free(buf);
198199767f8SToomas Soome 
199734b3a42SToomas Soome 	if ((fs->fatbuf = malloc(FATBLKSZ)) == NULL)
200734b3a42SToomas Soome 		return (errno);
201734b3a42SToomas Soome 	err = dos_read_fatblk(fs, fd, 0);
202734b3a42SToomas Soome 	if (err != 0) {
203734b3a42SToomas Soome 		free(fs->fatbuf);
204734b3a42SToomas Soome 		return (err);
205734b3a42SToomas Soome 	}
206199767f8SToomas Soome 
207734b3a42SToomas Soome 	fs->root = dot[0];
208734b3a42SToomas Soome 	fs->root.name[0] = ' ';
209734b3a42SToomas Soome 	if (fs->fatsz == 32) {
210734b3a42SToomas Soome 		fs->root.clus[0] = fs->rdcl & 0xff;
211734b3a42SToomas Soome 		fs->root.clus[1] = (fs->rdcl >> 8) & 0xff;
212734b3a42SToomas Soome 		fs->root.dex.h_clus[0] = (fs->rdcl >> 16) & 0xff;
213734b3a42SToomas Soome 		fs->root.dex.h_clus[1] = (fs->rdcl >> 24) & 0xff;
214734b3a42SToomas Soome 	}
215734b3a42SToomas Soome 	return (0);
216199767f8SToomas Soome }
217199767f8SToomas Soome 
218199767f8SToomas Soome /*
219199767f8SToomas Soome  * Unmount mounted filesystem
220199767f8SToomas Soome  */
221199767f8SToomas Soome static int
dos_unmount(DOS_FS * fs)222199767f8SToomas Soome dos_unmount(DOS_FS *fs)
223199767f8SToomas Soome {
224734b3a42SToomas Soome 	if (fs->links)
225734b3a42SToomas Soome 		return (EBUSY);
226734b3a42SToomas Soome 	free(fs->fatbuf);
227734b3a42SToomas Soome 	free(fs);
228734b3a42SToomas Soome 	return (0);
229199767f8SToomas Soome }
230199767f8SToomas Soome 
231199767f8SToomas Soome /*
232199767f8SToomas Soome  * Open DOS file
233199767f8SToomas Soome  */
234199767f8SToomas Soome static int
dos_open(const char * path,struct open_file * fd)235199767f8SToomas Soome dos_open(const char *path, struct open_file *fd)
236199767f8SToomas Soome {
237734b3a42SToomas Soome 	DOS_DE *de;
238734b3a42SToomas Soome 	DOS_FILE *f;
239734b3a42SToomas Soome 	DOS_FS *fs;
240734b3a42SToomas Soome 	uint_t size, clus;
241734b3a42SToomas Soome 	int err;
242734b3a42SToomas Soome 
243734b3a42SToomas Soome 	/* Allocate mount structure, associate with open */
244734b3a42SToomas Soome 	if ((fs = malloc(sizeof (DOS_FS))) == NULL)
245734b3a42SToomas Soome 		return (errno);
246734b3a42SToomas Soome 	if ((err = dos_mount(fs, fd))) {
247734b3a42SToomas Soome 		free(fs);
248734b3a42SToomas Soome 		return (err);
249734b3a42SToomas Soome 	}
250199767f8SToomas Soome 
251734b3a42SToomas Soome 	if ((err = namede(fs, path, &de))) {
252734b3a42SToomas Soome 		dos_unmount(fs);
253734b3a42SToomas Soome 		return (err);
254734b3a42SToomas Soome 	}
255199767f8SToomas Soome 
256734b3a42SToomas Soome 	clus = stclus(fs->fatsz, de);
257734b3a42SToomas Soome 	size = cv4(de->size);
258199767f8SToomas Soome 
259734b3a42SToomas Soome 	if ((!(de->attr & FA_DIR) && (!clus != !size)) ||
260734b3a42SToomas Soome 	    ((de->attr & FA_DIR) && size) ||
261734b3a42SToomas Soome 	    (clus && !okclus(fs, clus))) {
262734b3a42SToomas Soome 		dos_unmount(fs);
263734b3a42SToomas Soome 		return (EINVAL);
264734b3a42SToomas Soome 	}
265734b3a42SToomas Soome 	if ((f = malloc(sizeof (DOS_FILE))) == NULL) {
266734b3a42SToomas Soome 		err = errno;
267734b3a42SToomas Soome 		dos_unmount(fs);
268734b3a42SToomas Soome 		return (err);
269734b3a42SToomas Soome 	}
270734b3a42SToomas Soome 	bzero(f, sizeof (DOS_FILE));
271734b3a42SToomas Soome 	f->fs = fs;
272734b3a42SToomas Soome 	fs->links++;
273734b3a42SToomas Soome 	f->de = *de;
274734b3a42SToomas Soome 	fd->f_fsdata = f;
275734b3a42SToomas Soome 	return (0);
276199767f8SToomas Soome }
277199767f8SToomas Soome 
278199767f8SToomas Soome /*
279199767f8SToomas Soome  * Read from file
280199767f8SToomas Soome  */
281199767f8SToomas Soome static int
dos_read(struct open_file * fd,void * buf,size_t nbyte,size_t * resid)282199767f8SToomas Soome dos_read(struct open_file *fd, void *buf, size_t nbyte, size_t *resid)
283199767f8SToomas Soome {
284734b3a42SToomas Soome 	off_t size;
285734b3a42SToomas Soome 	uint_t nb, off, clus, c, cnt, n;
286734b3a42SToomas Soome 	DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
287734b3a42SToomas Soome 	int err = 0;
288734b3a42SToomas Soome 
289734b3a42SToomas Soome 	/*
290734b3a42SToomas Soome 	 * as ioget() can be called *a lot*, use twiddle here.
291734b3a42SToomas Soome 	 * also 4 seems to be good value not to slow loading down too much:
292734b3a42SToomas Soome 	 * with 270MB file (~540k ioget() calls, twiddle can easily waste
293734b3a42SToomas Soome 	 * 4-5 sec.
294734b3a42SToomas Soome 	 */
295734b3a42SToomas Soome 	twiddle(4);
296734b3a42SToomas Soome 	nb = (uint_t)nbyte;
297734b3a42SToomas Soome 	if ((size = fsize(f->fs, &f->de)) == -1)
298734b3a42SToomas Soome 		return (EINVAL);
299734b3a42SToomas Soome 	if (nb > (n = size - f->offset))
300734b3a42SToomas Soome 		nb = n;
301734b3a42SToomas Soome 	off = f->offset;
302734b3a42SToomas Soome 	if ((clus = stclus(f->fs->fatsz, &f->de)))
303734b3a42SToomas Soome 		off &= f->fs->bsize - 1;
304734b3a42SToomas Soome 	c = f->c;
305734b3a42SToomas Soome 	cnt = nb;
306734b3a42SToomas Soome 	while (cnt) {
307734b3a42SToomas Soome 		n = 0;
308734b3a42SToomas Soome 		if (!c) {
309734b3a42SToomas Soome 			if ((c = clus))
310734b3a42SToomas Soome 				n = bytblk(f->fs, f->offset);
311734b3a42SToomas Soome 		} else if (!off)
312734b3a42SToomas Soome 			n++;
313734b3a42SToomas Soome 		while (n--) {
314734b3a42SToomas Soome 			if ((err = fatget(f->fs, &c)))
315734b3a42SToomas Soome 				goto out;
316734b3a42SToomas Soome 			if (!okclus(f->fs, c)) {
317734b3a42SToomas Soome 				err = EINVAL;
318734b3a42SToomas Soome 				goto out;
319734b3a42SToomas Soome 			}
320734b3a42SToomas Soome 		}
321734b3a42SToomas Soome 		if (!clus || (n = f->fs->bsize - off) > cnt)
322734b3a42SToomas Soome 			n = cnt;
323734b3a42SToomas Soome 		if ((err = ioread(f->fs, (c ? blkoff(f->fs, c) :
324734b3a42SToomas Soome 		    secbyt(f->fs->lsndir)) + off, buf, n)))
325734b3a42SToomas Soome 			goto out;
326734b3a42SToomas Soome 		f->offset += n;
327734b3a42SToomas Soome 		f->c = c;
328734b3a42SToomas Soome 		off = 0;
329734b3a42SToomas Soome 		buf = (char *)buf + n;
330734b3a42SToomas Soome 		cnt -= n;
331199767f8SToomas Soome 	}
332734b3a42SToomas Soome out:
333734b3a42SToomas Soome 	if (resid)
334734b3a42SToomas Soome 		*resid = nbyte - nb + cnt;
335734b3a42SToomas Soome 	return (err);
336199767f8SToomas Soome }
337199767f8SToomas Soome 
338199767f8SToomas Soome /*
339199767f8SToomas Soome  * Reposition within file
340199767f8SToomas Soome  */
341199767f8SToomas Soome static off_t
dos_seek(struct open_file * fd,off_t offset,int whence)342199767f8SToomas Soome dos_seek(struct open_file *fd, off_t offset, int whence)
343199767f8SToomas Soome {
344734b3a42SToomas Soome 	off_t off;
345734b3a42SToomas Soome 	uint_t size;
346734b3a42SToomas Soome 	DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
347734b3a42SToomas Soome 
348734b3a42SToomas Soome 	size = cv4(f->de.size);
349734b3a42SToomas Soome 	switch (whence) {
350734b3a42SToomas Soome 	case SEEK_SET:
351734b3a42SToomas Soome 		off = 0;
352734b3a42SToomas Soome 		break;
353734b3a42SToomas Soome 	case SEEK_CUR:
354734b3a42SToomas Soome 		off = f->offset;
355734b3a42SToomas Soome 		break;
356734b3a42SToomas Soome 	case SEEK_END:
357734b3a42SToomas Soome 		off = size;
358734b3a42SToomas Soome 		break;
359734b3a42SToomas Soome 	default:
360734b3a42SToomas Soome 		errno = EINVAL;
361734b3a42SToomas Soome 		return (-1);
362734b3a42SToomas Soome 	}
363734b3a42SToomas Soome 	off += offset;
364734b3a42SToomas Soome 	if (off < 0 || off > size) {
365734b3a42SToomas Soome 		errno = EINVAL;
366734b3a42SToomas Soome 		return (-1);
367734b3a42SToomas Soome 	}
368734b3a42SToomas Soome 	f->offset = (uint_t)off;
369734b3a42SToomas Soome 	f->c = 0;
370734b3a42SToomas Soome 	return (off);
371199767f8SToomas Soome }
372199767f8SToomas Soome 
373199767f8SToomas Soome /*
374199767f8SToomas Soome  * Close open file
375199767f8SToomas Soome  */
376199767f8SToomas Soome static int
dos_close(struct open_file * fd)377199767f8SToomas Soome dos_close(struct open_file *fd)
378199767f8SToomas Soome {
379734b3a42SToomas Soome 	DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
380734b3a42SToomas Soome 	DOS_FS *fs = f->fs;
381199767f8SToomas Soome 
382734b3a42SToomas Soome 	f->fs->links--;
383734b3a42SToomas Soome 	free(f);
384734b3a42SToomas Soome 	dos_unmount(fs);
385734b3a42SToomas Soome 	return (0);
386199767f8SToomas Soome }
387199767f8SToomas Soome 
388199767f8SToomas Soome /*
389199767f8SToomas Soome  * Return some stat information on a file.
390199767f8SToomas Soome  */
391199767f8SToomas Soome static int
dos_stat(struct open_file * fd,struct stat * sb)392199767f8SToomas Soome dos_stat(struct open_file *fd, struct stat *sb)
393199767f8SToomas Soome {
394734b3a42SToomas Soome 	DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
395734b3a42SToomas Soome 
396734b3a42SToomas Soome 	/* only important stuff */
397734b3a42SToomas Soome 	sb->st_mode = f->de.attr & FA_DIR ? S_IFDIR | 0555 : S_IFREG | 0444;
398734b3a42SToomas Soome 	sb->st_nlink = 1;
399734b3a42SToomas Soome 	sb->st_uid = 0;
400734b3a42SToomas Soome 	sb->st_gid = 0;
401734b3a42SToomas Soome 	if ((sb->st_size = fsize(f->fs, &f->de)) == -1)
402734b3a42SToomas Soome 		return (EINVAL);
403734b3a42SToomas Soome 	return (0);
404199767f8SToomas Soome }
405199767f8SToomas Soome 
406199767f8SToomas Soome static int
dos_checksum(unsigned char * name,unsigned char * ext)40738005992SToomas Soome dos_checksum(unsigned char *name, unsigned char *ext)
408199767f8SToomas Soome {
409734b3a42SToomas Soome 	int x, i;
410734b3a42SToomas Soome 	char buf[11];
411734b3a42SToomas Soome 
412734b3a42SToomas Soome 	bcopy(name, buf, 8);
413734b3a42SToomas Soome 	bcopy(ext, buf+8, 3);
414734b3a42SToomas Soome 	x = 0;
415734b3a42SToomas Soome 	for (i = 0; i < 11; i++) {
416734b3a42SToomas Soome 		x = ((x & 1) << 7) | (x >> 1);
417734b3a42SToomas Soome 		x += buf[i];
418734b3a42SToomas Soome 		x &= 0xff;
419734b3a42SToomas Soome 	}
420734b3a42SToomas Soome 	return (x);
421199767f8SToomas Soome }
422199767f8SToomas Soome 
423199767f8SToomas Soome static int
dos_readdir(struct open_file * fd,struct dirent * d)424199767f8SToomas Soome dos_readdir(struct open_file *fd, struct dirent *d)
425199767f8SToomas Soome {
426734b3a42SToomas Soome 	uchar_t fn[261];
427734b3a42SToomas Soome 	DOS_DIR dd;
428734b3a42SToomas Soome 	size_t res;
429734b3a42SToomas Soome 	uint_t chk, x, xdn;
430734b3a42SToomas Soome 	int err;
431734b3a42SToomas Soome 
432734b3a42SToomas Soome 	x = chk = 0;
433734b3a42SToomas Soome 	while (1) {
434734b3a42SToomas Soome 		xdn = x;
435734b3a42SToomas Soome 		x = 0;
436734b3a42SToomas Soome 		err = dos_read(fd, &dd, sizeof (dd), &res);
437734b3a42SToomas Soome 		if (err)
438734b3a42SToomas Soome 			return (err);
439734b3a42SToomas Soome 		if (res == sizeof (dd))
440734b3a42SToomas Soome 			return (ENOENT);
441734b3a42SToomas Soome 		if (dd.de.name[0] == 0)
442734b3a42SToomas Soome 			return (ENOENT);
443734b3a42SToomas Soome 
444734b3a42SToomas Soome 		/* Skip deleted entries */
445734b3a42SToomas Soome 		if (dd.de.name[0] == 0xe5)
446734b3a42SToomas Soome 			continue;
447734b3a42SToomas Soome 
448734b3a42SToomas Soome 		/* Check if directory entry is volume label */
449734b3a42SToomas Soome 		if (dd.de.attr & FA_LABEL) {
450734b3a42SToomas Soome 			/*
451734b3a42SToomas Soome 			 * If volume label set, check if the current entry is
452734b3a42SToomas Soome 			 * extended entry (FA_XDE) for long file names.
453734b3a42SToomas Soome 			 */
454734b3a42SToomas Soome 			if ((dd.de.attr & FA_MASK) == FA_XDE) {
455734b3a42SToomas Soome 				/*
456734b3a42SToomas Soome 				 * Read through all following extended entries
457734b3a42SToomas Soome 				 * to get the long file name. 0x40 marks the
458734b3a42SToomas Soome 				 * last entry containing part of long file name.
459734b3a42SToomas Soome 				 */
460734b3a42SToomas Soome 				if (dd.xde.seq & 0x40)
461734b3a42SToomas Soome 					chk = dd.xde.chk;
462734b3a42SToomas Soome 				else if (dd.xde.seq != xdn - 1 ||
463734b3a42SToomas Soome 				    dd.xde.chk != chk)
464734b3a42SToomas Soome 					continue;
465734b3a42SToomas Soome 				x = dd.xde.seq & ~0x40;
466734b3a42SToomas Soome 				if (x < 1 || x > 20) {
467734b3a42SToomas Soome 					x = 0;
468734b3a42SToomas Soome 					continue;
469734b3a42SToomas Soome 				}
470734b3a42SToomas Soome 				cp_xdnm(fn, &dd.xde);
471734b3a42SToomas Soome 			} else {
472734b3a42SToomas Soome 				/* skip only volume label entries */
473734b3a42SToomas Soome 				continue;
474734b3a42SToomas Soome 			}
475734b3a42SToomas Soome 		} else {
476734b3a42SToomas Soome 			if (xdn == 1) {
477734b3a42SToomas Soome 				x = dos_checksum(dd.de.name, dd.de.ext);
478734b3a42SToomas Soome 				if (x == chk)
479734b3a42SToomas Soome 					break;
480734b3a42SToomas Soome 			} else {
481734b3a42SToomas Soome 				cp_sfn(fn, &dd.de);
482734b3a42SToomas Soome 				break;
483734b3a42SToomas Soome 			}
484734b3a42SToomas Soome 			x = 0;
485199767f8SToomas Soome 		}
486199767f8SToomas Soome 	}
487199767f8SToomas Soome 
488734b3a42SToomas Soome 	d->d_fileno = (dd.de.clus[1] << 8) + dd.de.clus[0];
489734b3a42SToomas Soome 	d->d_reclen = sizeof (*d);
490734b3a42SToomas Soome 	d->d_type = (dd.de.attr & FA_DIR) ? DT_DIR : DT_REG;
491734b3a42SToomas Soome 	memcpy(d->d_name, fn, sizeof (d->d_name));
492734b3a42SToomas Soome 	return (0);
493199767f8SToomas Soome }
494199767f8SToomas Soome 
495199767f8SToomas Soome /*
496199767f8SToomas Soome  * Parse DOS boot sector
497199767f8SToomas Soome  */
498199767f8SToomas Soome static int
parsebs(DOS_FS * fs,DOS_BS * bs)499199767f8SToomas Soome parsebs(DOS_FS *fs, DOS_BS *bs)
500199767f8SToomas Soome {
501734b3a42SToomas Soome 	uint_t sc;
502734b3a42SToomas Soome 
503734b3a42SToomas Soome 	if ((bs->jmp[0] != 0x69 &&
504734b3a42SToomas Soome 	    bs->jmp[0] != 0xe9 &&
505734b3a42SToomas Soome 	    (bs->jmp[0] != 0xeb || bs->jmp[2] != 0x90)) ||
506734b3a42SToomas Soome 	    bs->bpb.media < 0xf0)
507734b3a42SToomas Soome 		return (EINVAL);
508734b3a42SToomas Soome 	if (cv2(bs->bpb.secsiz) != SECSIZ)
509734b3a42SToomas Soome 		return (EINVAL);
510734b3a42SToomas Soome 	if (!(fs->spc = bs->bpb.spc) || fs->spc & (fs->spc - 1))
511734b3a42SToomas Soome 		return (EINVAL);
512734b3a42SToomas Soome 	fs->bsize = secbyt(fs->spc);
513734b3a42SToomas Soome 	fs->bshift = ffs(fs->bsize) - 1;
514734b3a42SToomas Soome 	if ((fs->spf = cv2(bs->bpb.spf))) {
515734b3a42SToomas Soome 		if (bs->bpb.fats != 2)
516734b3a42SToomas Soome 			return (EINVAL);
517734b3a42SToomas Soome 		if (!(fs->dirents = cv2(bs->bpb.dirents)))
518734b3a42SToomas Soome 			return (EINVAL);
519734b3a42SToomas Soome 	} else {
520734b3a42SToomas Soome 		if (!(fs->spf = cv4(bs->bpb.lspf)))
521734b3a42SToomas Soome 			return (EINVAL);
522734b3a42SToomas Soome 		if (!bs->bpb.fats || bs->bpb.fats > 16)
523734b3a42SToomas Soome 			return (EINVAL);
524734b3a42SToomas Soome 		if ((fs->rdcl = cv4(bs->bpb.rdcl)) < LOCLUS)
525734b3a42SToomas Soome 			return (EINVAL);
526734b3a42SToomas Soome 	}
527734b3a42SToomas Soome 	if (!(fs->lsnfat = cv2(bs->bpb.ressec)))
528734b3a42SToomas Soome 		return (EINVAL);
529734b3a42SToomas Soome 	fs->lsndir = fs->lsnfat + fs->spf * bs->bpb.fats;
530734b3a42SToomas Soome 	fs->lsndta = fs->lsndir + entsec(fs->dirents);
531734b3a42SToomas Soome 	if (!(sc = cv2(bs->bpb.secs)) && !(sc = cv4(bs->bpb.lsecs)))
532734b3a42SToomas Soome 		return (EINVAL);
533734b3a42SToomas Soome 	if (fs->lsndta > sc)
534734b3a42SToomas Soome 		return (EINVAL);
535734b3a42SToomas Soome 	if ((fs->xclus = secblk(fs, sc - fs->lsndta) + 1) < LOCLUS)
536734b3a42SToomas Soome 		return (EINVAL);
537734b3a42SToomas Soome 	fs->fatsz = fs->dirents ? fs->xclus < 0xff6 ? 12 : 16 : 32;
538734b3a42SToomas Soome 	sc = (secbyt(fs->spf) << 1) / (fs->fatsz >> 2) - 1;
539734b3a42SToomas Soome 	if (fs->xclus > sc)
540734b3a42SToomas Soome 		fs->xclus = sc;
541734b3a42SToomas Soome 	return (0);
542199767f8SToomas Soome }
543199767f8SToomas Soome 
544199767f8SToomas Soome /*
545199767f8SToomas Soome  * Return directory entry from path
546199767f8SToomas Soome  */
547199767f8SToomas Soome static int
namede(DOS_FS * fs,const char * path,DOS_DE ** dep)548199767f8SToomas Soome namede(DOS_FS *fs, const char *path, DOS_DE **dep)
549199767f8SToomas Soome {
550734b3a42SToomas Soome 	char name[256];
551734b3a42SToomas Soome 	DOS_DE *de;
552734b3a42SToomas Soome 	char *s;
553734b3a42SToomas Soome 	size_t n;
554734b3a42SToomas Soome 	int err;
555734b3a42SToomas Soome 
556734b3a42SToomas Soome 	err = 0;
557734b3a42SToomas Soome 	de = &fs->root;
558734b3a42SToomas Soome 	while (*path) {
559734b3a42SToomas Soome 		while (*path == '/')
560734b3a42SToomas Soome 			path++;
561734b3a42SToomas Soome 		if (*path == '\0')
562734b3a42SToomas Soome 			break;
563734b3a42SToomas Soome 		if (!(s = strchr(path, '/')))
564734b3a42SToomas Soome 			s = strchr(path, 0);
565734b3a42SToomas Soome 		if ((n = s - path) > 255)
566734b3a42SToomas Soome 			return (ENAMETOOLONG);
567734b3a42SToomas Soome 		memcpy(name, path, n);
568734b3a42SToomas Soome 		name[n] = 0;
569734b3a42SToomas Soome 		path = s;
570734b3a42SToomas Soome 		if (!(de->attr & FA_DIR))
571734b3a42SToomas Soome 			return (ENOTDIR);
572734b3a42SToomas Soome 		if ((err = lookup(fs, stclus(fs->fatsz, de), name, &de)))
573734b3a42SToomas Soome 			return (err);
574734b3a42SToomas Soome 	}
575734b3a42SToomas Soome 	*dep = de;
576734b3a42SToomas Soome 	return (0);
577199767f8SToomas Soome }
578199767f8SToomas Soome 
579199767f8SToomas Soome /*
580199767f8SToomas Soome  * Lookup path segment
581199767f8SToomas Soome  */
582199767f8SToomas Soome static int
lookup(DOS_FS * fs,uint_t clus,const char * name,DOS_DE ** dep)583734b3a42SToomas Soome lookup(DOS_FS *fs, uint_t clus, const char *name, DOS_DE **dep)
584199767f8SToomas Soome {
585734b3a42SToomas Soome 	static DOS_DIR dir[DEPSEC];
586734b3a42SToomas Soome 	uchar_t lfn[261];
587734b3a42SToomas Soome 	uchar_t sfn[13];
588734b3a42SToomas Soome 	uint_t nsec, lsec, xdn, chk, sec, ent, x;
589734b3a42SToomas Soome 	int err, ok;
590734b3a42SToomas Soome 
591734b3a42SToomas Soome 	if (!clus)
592734b3a42SToomas Soome 		for (ent = 0; ent < 2; ent++)
593734b3a42SToomas Soome 			if (!strcasecmp(name, dotstr[ent])) {
594734b3a42SToomas Soome 				*dep = dot + ent;
595734b3a42SToomas Soome 				return (0);
596734b3a42SToomas Soome 			}
597734b3a42SToomas Soome 	if (!clus && fs->fatsz == 32)
598734b3a42SToomas Soome 		clus = fs->rdcl;
599734b3a42SToomas Soome 	nsec = !clus ? entsec(fs->dirents) : fs->spc;
600734b3a42SToomas Soome 	lsec = 0;
601734b3a42SToomas Soome 	xdn = chk = 0;
602734b3a42SToomas Soome 	for (;;) {
603734b3a42SToomas Soome 		if (!clus && !lsec)
604734b3a42SToomas Soome 			lsec = fs->lsndir;
605734b3a42SToomas Soome 		else if (okclus(fs, clus))
606734b3a42SToomas Soome 			lsec = blklsn(fs, clus);
607734b3a42SToomas Soome 		else
608734b3a42SToomas Soome 			return (EINVAL);
609734b3a42SToomas Soome 
610734b3a42SToomas Soome 		for (sec = 0; sec < nsec; sec++) {
611734b3a42SToomas Soome 			if ((err = ioget(fs->fd, lsec + sec, dir, secbyt(1))))
612734b3a42SToomas Soome 				return (err);
613734b3a42SToomas Soome 			for (ent = 0; ent < DEPSEC; ent++) {
614734b3a42SToomas Soome 				if (dir[ent].de.name[0] == 0)
615734b3a42SToomas Soome 					return (ENOENT);
616734b3a42SToomas Soome 				if (dir[ent].de.name[0] == 0xe5) {
617734b3a42SToomas Soome 					xdn = 0;
618734b3a42SToomas Soome 					continue;
619734b3a42SToomas Soome 				}
620734b3a42SToomas Soome 				if ((dir[ent].de.attr & FA_MASK) == FA_XDE) {
621734b3a42SToomas Soome 					x = dir[ent].xde.seq;
622734b3a42SToomas Soome 					if (x & 0x40 ||
623734b3a42SToomas Soome 					    (x + 1 == xdn &&
624734b3a42SToomas Soome 					    dir[ent].xde.chk == chk)) {
625734b3a42SToomas Soome 						if (x & 0x40) {
626734b3a42SToomas Soome 							chk = dir[ent].xde.chk;
627734b3a42SToomas Soome 							x &= ~0x40;
628734b3a42SToomas Soome 						}
629734b3a42SToomas Soome 						if (x >= 1 && x <= 20) {
630734b3a42SToomas Soome 							cp_xdnm(lfn,
631734b3a42SToomas Soome 							    &dir[ent].xde);
632734b3a42SToomas Soome 							xdn = x;
633734b3a42SToomas Soome 							continue;
634734b3a42SToomas Soome 						}
635734b3a42SToomas Soome 					}
636734b3a42SToomas Soome 				} else if (!(dir[ent].de.attr & FA_LABEL)) {
637734b3a42SToomas Soome 					if ((ok = xdn == 1)) {
638734b3a42SToomas Soome 						x = dos_checksum(
639734b3a42SToomas Soome 						    dir[ent].de.name,
640734b3a42SToomas Soome 						    dir[ent].de.ext);
641734b3a42SToomas Soome 						ok = chk == x &&
642734b3a42SToomas Soome 						    !strcasecmp(name,
643734b3a42SToomas Soome 						    (const char *)lfn);
644734b3a42SToomas Soome 					}
645734b3a42SToomas Soome 					if (!ok) {
646734b3a42SToomas Soome 						cp_sfn(sfn, &dir[ent].de);
647734b3a42SToomas Soome 						ok = !strcasecmp(name,
648734b3a42SToomas Soome 						    (const char *)sfn);
649734b3a42SToomas Soome 					}
650734b3a42SToomas Soome 					if (ok) {
651734b3a42SToomas Soome 						*dep = &dir[ent].de;
652734b3a42SToomas Soome 						return (0);
653734b3a42SToomas Soome 					}
654734b3a42SToomas Soome 				}
655734b3a42SToomas Soome 				xdn = 0;
656734b3a42SToomas Soome 			}
657199767f8SToomas Soome 		}
658734b3a42SToomas Soome 		if (!clus)
659734b3a42SToomas Soome 			break;
660734b3a42SToomas Soome 		if ((err = fatget(fs, &clus)))
661734b3a42SToomas Soome 			return (err);
662734b3a42SToomas Soome 		if (fatend(fs->fatsz, clus))
663734b3a42SToomas Soome 			break;
664734b3a42SToomas Soome 	}
665734b3a42SToomas Soome 	return (ENOENT);
666199767f8SToomas Soome }
667199767f8SToomas Soome 
668199767f8SToomas Soome /*
669199767f8SToomas Soome  * Copy name from extended directory entry
670199767f8SToomas Soome  */
671199767f8SToomas Soome static void
cp_xdnm(uchar_t * lfn,DOS_XDE * xde)672734b3a42SToomas Soome cp_xdnm(uchar_t *lfn, DOS_XDE *xde)
673199767f8SToomas Soome {
674734b3a42SToomas Soome 	static struct {
675734b3a42SToomas Soome 		uint_t off;
676734b3a42SToomas Soome 		uint_t dim;
677734b3a42SToomas Soome 	} ix[3] = {
678734b3a42SToomas Soome 		{ offsetof(DOS_XDE, name1), sizeof (xde->name1) / 2},
679734b3a42SToomas Soome 		{ offsetof(DOS_XDE, name2), sizeof (xde->name2) / 2},
680734b3a42SToomas Soome 		{ offsetof(DOS_XDE, name3), sizeof (xde->name3) / 2}
681734b3a42SToomas Soome 	};
682734b3a42SToomas Soome 	uchar_t *p;
683734b3a42SToomas Soome 	uint_t n, x, c;
684734b3a42SToomas Soome 
685734b3a42SToomas Soome 	lfn += 13 * ((xde->seq & ~0x40) - 1);
686734b3a42SToomas Soome 	for (n = 0; n < 3; n++)
687734b3a42SToomas Soome 		for (p = (uchar_t *)xde + ix[n].off, x = ix[n].dim; x;
688734b3a42SToomas Soome 		    p += 2, x--) {
689734b3a42SToomas Soome 			if ((c = cv2(p)) && (c < 32 || c > 127))
690734b3a42SToomas Soome 				c = '?';
691734b3a42SToomas Soome 			if (!(*lfn++ = c))
692734b3a42SToomas Soome 				return;
693734b3a42SToomas Soome 		}
694734b3a42SToomas Soome 	if (xde->seq & 0x40)
695130d7652SToomas Soome 		*lfn = 0;
696199767f8SToomas Soome }
697199767f8SToomas Soome 
698199767f8SToomas Soome /*
699199767f8SToomas Soome  * Copy short filename
700199767f8SToomas Soome  */
701199767f8SToomas Soome static void
cp_sfn(uchar_t * sfn,DOS_DE * de)702734b3a42SToomas Soome cp_sfn(uchar_t *sfn, DOS_DE *de)
703199767f8SToomas Soome {
704734b3a42SToomas Soome 	uchar_t *p;
705734b3a42SToomas Soome 	int j, i;
706734b3a42SToomas Soome 
707734b3a42SToomas Soome 	p = sfn;
708734b3a42SToomas Soome 	if (*de->name != ' ') {
709734b3a42SToomas Soome 		for (j = 7; de->name[j] == ' '; j--)
710734b3a42SToomas Soome 			;
711734b3a42SToomas Soome 		for (i = 0; i <= j; i++)
712734b3a42SToomas Soome 			*p++ = de->name[i];
713734b3a42SToomas Soome 		if (*de->ext != ' ') {
714734b3a42SToomas Soome 			*p++ = '.';
715734b3a42SToomas Soome 			for (j = 2; de->ext[j] == ' '; j--)
716734b3a42SToomas Soome 				;
717734b3a42SToomas Soome 			for (i = 0; i <= j; i++)
718734b3a42SToomas Soome 				*p++ = de->ext[i];
719734b3a42SToomas Soome 		}
720734b3a42SToomas Soome 	}
721734b3a42SToomas Soome 	*p = '\0';
722734b3a42SToomas Soome 	if (*sfn == 5)
723734b3a42SToomas Soome 		*sfn = 0xe5;
724199767f8SToomas Soome }
725199767f8SToomas Soome 
726199767f8SToomas Soome /*
727199767f8SToomas Soome  * Return size of file in bytes
728199767f8SToomas Soome  */
729199767f8SToomas Soome static off_t
fsize(DOS_FS * fs,DOS_DE * de)730199767f8SToomas Soome fsize(DOS_FS *fs, DOS_DE *de)
731199767f8SToomas Soome {
732734b3a42SToomas Soome 	ulong_t size;
733734b3a42SToomas Soome 	uint_t c;
734734b3a42SToomas Soome 	int n;
735734b3a42SToomas Soome 
736734b3a42SToomas Soome 	if (!(size = cv4(de->size)) && de->attr & FA_DIR) {
737734b3a42SToomas Soome 		if (!(c = cv2(de->clus)))
738734b3a42SToomas Soome 			size = fs->dirents * sizeof (DOS_DE);
739734b3a42SToomas Soome 		else {
740734b3a42SToomas Soome 			if ((n = fatcnt(fs, c)) == -1)
741734b3a42SToomas Soome 				return (n);
742734b3a42SToomas Soome 			size = blkbyt(fs, n);
743734b3a42SToomas Soome 		}
744734b3a42SToomas Soome 	}
745734b3a42SToomas Soome 	return (size);
746199767f8SToomas Soome }
747199767f8SToomas Soome 
748199767f8SToomas Soome /*
749199767f8SToomas Soome  * Count number of clusters in chain
750199767f8SToomas Soome  */
751199767f8SToomas Soome static int
fatcnt(DOS_FS * fs,uint_t c)752734b3a42SToomas Soome fatcnt(DOS_FS *fs, uint_t c)
753199767f8SToomas Soome {
754734b3a42SToomas Soome 	int n;
755199767f8SToomas Soome 
756734b3a42SToomas Soome 	for (n = 0; okclus(fs, c); n++)
757734b3a42SToomas Soome 		if (fatget(fs, &c))
758734b3a42SToomas Soome 			return (-1);
759734b3a42SToomas Soome 	return (fatend(fs->fatsz, c) ? n : -1);
760199767f8SToomas Soome }
761199767f8SToomas Soome 
762199767f8SToomas Soome /*
76363982b82SToomas Soome  * Get next cluster in cluster chain. Use in core fat cache unless
76463982b82SToomas Soome  * the number of current 128K block in FAT has changed.
765199767f8SToomas Soome  */
766199767f8SToomas Soome static int
fatget(DOS_FS * fs,uint_t * c)767734b3a42SToomas Soome fatget(DOS_FS *fs, uint_t *c)
768199767f8SToomas Soome {
769734b3a42SToomas Soome 	uint_t val_in, val_out, offset, blknum, nbyte;
770734b3a42SToomas Soome 	const uchar_t *p_entry;
771734b3a42SToomas Soome 	int err;
772734b3a42SToomas Soome 
773734b3a42SToomas Soome 	/* check input value to prevent overflow in fatoff() */
774734b3a42SToomas Soome 	val_in = *c;
775734b3a42SToomas Soome 	if (val_in & 0xf0000000)
776734b3a42SToomas Soome 		return (EINVAL);
777734b3a42SToomas Soome 
778734b3a42SToomas Soome 	/* ensure that current 128K FAT block is cached */
779734b3a42SToomas Soome 	offset = fatoff(fs->fatsz, val_in);
780734b3a42SToomas Soome 	nbyte = fs->fatsz != 32 ? 2 : 4;
781734b3a42SToomas Soome 	if (offset + nbyte > secbyt(fs->spf))
782734b3a42SToomas Soome 		return (EINVAL);
783734b3a42SToomas Soome 	blknum = offset / FATBLKSZ;
784734b3a42SToomas Soome 	offset %= FATBLKSZ;
785734b3a42SToomas Soome 	if (offset + nbyte > FATBLKSZ)
786734b3a42SToomas Soome 		return (EINVAL);
787734b3a42SToomas Soome 	if (blknum != fs->fatbuf_blknum) {
788734b3a42SToomas Soome 		err = dos_read_fatblk(fs, fs->fd, blknum);
789734b3a42SToomas Soome 		if (err != 0)
790734b3a42SToomas Soome 			return (err);
791734b3a42SToomas Soome 	}
792734b3a42SToomas Soome 	p_entry = fs->fatbuf + offset;
793734b3a42SToomas Soome 
794734b3a42SToomas Soome 	/* extract cluster number from FAT entry */
795734b3a42SToomas Soome 	switch (fs->fatsz) {
796734b3a42SToomas Soome 	case 32:
797734b3a42SToomas Soome 		val_out = cv4(p_entry);
798734b3a42SToomas Soome 		val_out &= 0x0fffffff;
799734b3a42SToomas Soome 		break;
800734b3a42SToomas Soome 	case 16:
801734b3a42SToomas Soome 		val_out = cv2(p_entry);
802734b3a42SToomas Soome 		break;
803734b3a42SToomas Soome 	case 12:
804734b3a42SToomas Soome 		val_out = cv2(p_entry);
805734b3a42SToomas Soome 		if (val_in & 1)
806734b3a42SToomas Soome 			val_out >>= 4;
807734b3a42SToomas Soome 		else
808734b3a42SToomas Soome 			val_out &= 0xfff;
809734b3a42SToomas Soome 		break;
810734b3a42SToomas Soome 	default:
811734b3a42SToomas Soome 		return (EINVAL);
812734b3a42SToomas Soome 	}
813734b3a42SToomas Soome 	*c = val_out;
814734b3a42SToomas Soome 	return (0);
815199767f8SToomas Soome }
816199767f8SToomas Soome 
817199767f8SToomas Soome /*
818199767f8SToomas Soome  * Is cluster an end-of-chain marker?
819199767f8SToomas Soome  */
820199767f8SToomas Soome static int
fatend(uint_t sz,uint_t c)821734b3a42SToomas Soome fatend(uint_t sz, uint_t c)
822199767f8SToomas Soome {
823734b3a42SToomas Soome 	return (c > (sz == 12 ? 0xff7U : sz == 16 ? 0xfff7U : 0xffffff7));
824199767f8SToomas Soome }
825199767f8SToomas Soome 
826199767f8SToomas Soome /*
827199767f8SToomas Soome  * Offset-based I/O primitive
828199767f8SToomas Soome  */
829199767f8SToomas Soome static int
ioread(DOS_FS * fs,uint_t offset,void * buf,size_t nbyte)830734b3a42SToomas Soome ioread(DOS_FS *fs, uint_t offset, void *buf, size_t nbyte)
831199767f8SToomas Soome {
832734b3a42SToomas Soome 	char *s;
833734b3a42SToomas Soome 	uint_t off, n;
834734b3a42SToomas Soome 	int err;
835734b3a42SToomas Soome 	uchar_t local_buf[SECSIZ];
836734b3a42SToomas Soome 
837734b3a42SToomas Soome 	s = buf;
838734b3a42SToomas Soome 	if ((off = offset & (SECSIZ - 1))) {
839734b3a42SToomas Soome 		offset -= off;
840734b3a42SToomas Soome 		if ((n = SECSIZ - off) > nbyte)
841734b3a42SToomas Soome 			n = nbyte;
842734b3a42SToomas Soome 		err = ioget(fs->fd, bytsec(offset), local_buf,
843734b3a42SToomas Soome 		    sizeof (local_buf));
844734b3a42SToomas Soome 		if (err != 0)
845734b3a42SToomas Soome 			return (err);
846734b3a42SToomas Soome 		memcpy(s, local_buf + off, n);
847734b3a42SToomas Soome 		offset += SECSIZ;
848734b3a42SToomas Soome 		s += n;
849734b3a42SToomas Soome 		nbyte -= n;
850734b3a42SToomas Soome 	}
851734b3a42SToomas Soome 	n = nbyte & (SECSIZ - 1);
852734b3a42SToomas Soome 	if (nbyte -= n) {
853734b3a42SToomas Soome 		if ((err = ioget(fs->fd, bytsec(offset), s, nbyte)))
854734b3a42SToomas Soome 			return (err);
855734b3a42SToomas Soome 		offset += nbyte;
856734b3a42SToomas Soome 		s += nbyte;
857734b3a42SToomas Soome 	}
858734b3a42SToomas Soome 	if (n != 0) {
859734b3a42SToomas Soome 		err = ioget(fs->fd, bytsec(offset), local_buf,
860734b3a42SToomas Soome 		    sizeof (local_buf));
861734b3a42SToomas Soome 		if (err != 0)
862734b3a42SToomas Soome 			return (err);
863734b3a42SToomas Soome 		memcpy(s, local_buf, n);
864734b3a42SToomas Soome 	}
865734b3a42SToomas Soome 	return (0);
866199767f8SToomas Soome }
867199767f8SToomas Soome 
868199767f8SToomas Soome /*
869199767f8SToomas Soome  * Sector-based I/O primitive
870199767f8SToomas Soome  */
871199767f8SToomas Soome static int
ioget(struct open_file * fd,daddr_t lsec,void * buf,size_t size)87263982b82SToomas Soome ioget(struct open_file *fd, daddr_t lsec, void *buf, size_t size)
873199767f8SToomas Soome {
874734b3a42SToomas Soome 	size_t rsize;
875734b3a42SToomas Soome 	int rv;
876734b3a42SToomas Soome 
877734b3a42SToomas Soome 	/* Make sure we get full read or error. */
878734b3a42SToomas Soome 	rsize = 0;
879734b3a42SToomas Soome 	rv = (fd->f_dev->dv_strategy)(fd->f_devdata, F_READ, lsec,
880734b3a42SToomas Soome 	    size, buf, &rsize);
881734b3a42SToomas Soome 	if ((rv == 0) && (size != rsize))
882734b3a42SToomas Soome 		rv = EIO;
883734b3a42SToomas Soome 	return (rv);
884199767f8SToomas Soome }
885