xref: /titanic_54/usr/src/boot/lib/libstand/dosfs.c (revision 4a5d661a82b942b6538acd26209d959ce98b593a)
1*4a5d661aSToomas Soome /*
2*4a5d661aSToomas Soome  * Copyright (c) 1996, 1998 Robert Nordier
3*4a5d661aSToomas Soome  * All rights reserved.
4*4a5d661aSToomas Soome  *
5*4a5d661aSToomas Soome  * Redistribution and use in source and binary forms, with or without
6*4a5d661aSToomas Soome  * modification, are permitted provided that the following conditions
7*4a5d661aSToomas Soome  * are met:
8*4a5d661aSToomas Soome  * 1. Redistributions of source code must retain the above copyright
9*4a5d661aSToomas Soome  *    notice, this list of conditions and the following disclaimer.
10*4a5d661aSToomas Soome  * 2. Redistributions in binary form must reproduce the above copyright
11*4a5d661aSToomas Soome  *    notice, this list of conditions and the following disclaimer in
12*4a5d661aSToomas Soome  *    the documentation and/or other materials provided with the
13*4a5d661aSToomas Soome  *    distribution.
14*4a5d661aSToomas Soome  *
15*4a5d661aSToomas Soome  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
16*4a5d661aSToomas Soome  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17*4a5d661aSToomas Soome  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18*4a5d661aSToomas Soome  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
19*4a5d661aSToomas Soome  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20*4a5d661aSToomas Soome  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
21*4a5d661aSToomas Soome  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22*4a5d661aSToomas Soome  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
23*4a5d661aSToomas Soome  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24*4a5d661aSToomas Soome  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
25*4a5d661aSToomas Soome  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*4a5d661aSToomas Soome  */
27*4a5d661aSToomas Soome 
28*4a5d661aSToomas Soome #include <sys/cdefs.h>
29*4a5d661aSToomas Soome __FBSDID("$FreeBSD$");
30*4a5d661aSToomas Soome 
31*4a5d661aSToomas Soome /*
32*4a5d661aSToomas Soome  * Readonly filesystem for Microsoft FAT12/FAT16/FAT32 filesystems,
33*4a5d661aSToomas Soome  * also supports VFAT.
34*4a5d661aSToomas Soome  */
35*4a5d661aSToomas Soome 
36*4a5d661aSToomas Soome #include <sys/types.h>
37*4a5d661aSToomas Soome #include <string.h>
38*4a5d661aSToomas Soome #include <stddef.h>
39*4a5d661aSToomas Soome 
40*4a5d661aSToomas Soome #include "stand.h"
41*4a5d661aSToomas Soome 
42*4a5d661aSToomas Soome #include "dosfs.h"
43*4a5d661aSToomas Soome 
44*4a5d661aSToomas Soome 
45*4a5d661aSToomas Soome static int	dos_open(const char *path, struct open_file *fd);
46*4a5d661aSToomas Soome static int	dos_close(struct open_file *fd);
47*4a5d661aSToomas Soome static int	dos_read(struct open_file *fd, void *buf, size_t size, size_t *resid);
48*4a5d661aSToomas Soome static off_t	dos_seek(struct open_file *fd, off_t offset, int whence);
49*4a5d661aSToomas Soome static int	dos_stat(struct open_file *fd, struct stat *sb);
50*4a5d661aSToomas Soome static int	dos_readdir(struct open_file *fd, struct dirent *d);
51*4a5d661aSToomas Soome 
52*4a5d661aSToomas Soome struct fs_ops dosfs_fsops = {
53*4a5d661aSToomas Soome 	"dosfs",
54*4a5d661aSToomas Soome 	dos_open,
55*4a5d661aSToomas Soome 	dos_close,
56*4a5d661aSToomas Soome 	dos_read,
57*4a5d661aSToomas Soome 	null_write,
58*4a5d661aSToomas Soome 	dos_seek,
59*4a5d661aSToomas Soome 	dos_stat,
60*4a5d661aSToomas Soome 	dos_readdir
61*4a5d661aSToomas Soome };
62*4a5d661aSToomas Soome 
63*4a5d661aSToomas Soome #define SECSIZ  512             /* sector size */
64*4a5d661aSToomas Soome #define SSHIFT    9             /* SECSIZ shift */
65*4a5d661aSToomas Soome #define DEPSEC   16             /* directory entries per sector */
66*4a5d661aSToomas Soome #define DSHIFT    4             /* DEPSEC shift */
67*4a5d661aSToomas Soome #define LOCLUS    2             /* lowest cluster number */
68*4a5d661aSToomas Soome 
69*4a5d661aSToomas Soome /* DOS "BIOS Parameter Block" */
70*4a5d661aSToomas Soome typedef struct {
71*4a5d661aSToomas Soome     u_char secsiz[2];           /* sector size */
72*4a5d661aSToomas Soome     u_char spc;                 /* sectors per cluster */
73*4a5d661aSToomas Soome     u_char ressec[2];           /* reserved sectors */
74*4a5d661aSToomas Soome     u_char fats;                /* FATs */
75*4a5d661aSToomas Soome     u_char dirents[2];          /* root directory entries */
76*4a5d661aSToomas Soome     u_char secs[2];             /* total sectors */
77*4a5d661aSToomas Soome     u_char media;               /* media descriptor */
78*4a5d661aSToomas Soome     u_char spf[2];              /* sectors per FAT */
79*4a5d661aSToomas Soome     u_char spt[2];              /* sectors per track */
80*4a5d661aSToomas Soome     u_char heads[2];            /* drive heads */
81*4a5d661aSToomas Soome     u_char hidsec[4];           /* hidden sectors */
82*4a5d661aSToomas Soome     u_char lsecs[4];            /* huge sectors */
83*4a5d661aSToomas Soome     u_char lspf[4];             /* huge sectors per FAT */
84*4a5d661aSToomas Soome     u_char xflg[2];             /* flags */
85*4a5d661aSToomas Soome     u_char vers[2];             /* filesystem version */
86*4a5d661aSToomas Soome     u_char rdcl[4];             /* root directory start cluster */
87*4a5d661aSToomas Soome     u_char infs[2];             /* filesystem info sector */
88*4a5d661aSToomas Soome     u_char bkbs[2];             /* backup boot sector */
89*4a5d661aSToomas Soome } DOS_BPB;
90*4a5d661aSToomas Soome 
91*4a5d661aSToomas Soome /* Initial portion of DOS boot sector */
92*4a5d661aSToomas Soome typedef struct {
93*4a5d661aSToomas Soome     u_char jmp[3];              /* usually 80x86 'jmp' opcode */
94*4a5d661aSToomas Soome     u_char oem[8];              /* OEM name and version */
95*4a5d661aSToomas Soome     DOS_BPB bpb;                /* BPB */
96*4a5d661aSToomas Soome } DOS_BS;
97*4a5d661aSToomas Soome 
98*4a5d661aSToomas Soome /* Supply missing "." and ".." root directory entries */
99*4a5d661aSToomas Soome static const char *const dotstr[2] = {".", ".."};
100*4a5d661aSToomas Soome static DOS_DE dot[2] = {
101*4a5d661aSToomas Soome     {".       ", "   ", FA_DIR, {0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
102*4a5d661aSToomas Soome      {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}},
103*4a5d661aSToomas Soome     {"..      ", "   ", FA_DIR, {0, 0, {0, 0}, {0, 0}, {0, 0}, {0, 0}},
104*4a5d661aSToomas Soome      {0, 0}, {0x21, 0}, {0, 0}, {0, 0, 0, 0}}
105*4a5d661aSToomas Soome };
106*4a5d661aSToomas Soome 
107*4a5d661aSToomas Soome /* The usual conversion macros to avoid multiplication and division */
108*4a5d661aSToomas Soome #define bytsec(n)      ((n) >> SSHIFT)
109*4a5d661aSToomas Soome #define secbyt(s)      ((s) << SSHIFT)
110*4a5d661aSToomas Soome #define entsec(e)      ((e) >> DSHIFT)
111*4a5d661aSToomas Soome #define bytblk(fs, n)  ((n) >> (fs)->bshift)
112*4a5d661aSToomas Soome #define blkbyt(fs, b)  ((b) << (fs)->bshift)
113*4a5d661aSToomas Soome #define secblk(fs, s)  ((s) >> ((fs)->bshift - SSHIFT))
114*4a5d661aSToomas Soome #define blksec(fs, b)  ((b) << ((fs)->bshift - SSHIFT))
115*4a5d661aSToomas Soome 
116*4a5d661aSToomas Soome /* Convert cluster number to offset within filesystem */
117*4a5d661aSToomas Soome #define blkoff(fs, b) (secbyt((fs)->lsndta) + blkbyt(fs, (b) - LOCLUS))
118*4a5d661aSToomas Soome 
119*4a5d661aSToomas Soome /* Convert cluster number to logical sector number */
120*4a5d661aSToomas Soome #define blklsn(fs, b)  ((fs)->lsndta + blksec(fs, (b) - LOCLUS))
121*4a5d661aSToomas Soome 
122*4a5d661aSToomas Soome /* Convert cluster number to offset within FAT */
123*4a5d661aSToomas Soome #define fatoff(sz, c)  ((sz) == 12 ? (c) + ((c) >> 1) :  \
124*4a5d661aSToomas Soome                         (sz) == 16 ? (c) << 1 :          \
125*4a5d661aSToomas Soome 			(c) << 2)
126*4a5d661aSToomas Soome 
127*4a5d661aSToomas Soome /* Does cluster number reference a valid data cluster? */
128*4a5d661aSToomas Soome #define okclus(fs, c)  ((c) >= LOCLUS && (c) <= (fs)->xclus)
129*4a5d661aSToomas Soome 
130*4a5d661aSToomas Soome /* Get start cluster from directory entry */
131*4a5d661aSToomas Soome #define stclus(sz, de)  ((sz) != 32 ? cv2((de)->clus) :          \
132*4a5d661aSToomas Soome                          ((u_int)cv2((de)->dex.h_clus) << 16) |  \
133*4a5d661aSToomas Soome 			 cv2((de)->clus))
134*4a5d661aSToomas Soome 
135*4a5d661aSToomas Soome /*
136*4a5d661aSToomas Soome  * fat cache metadata
137*4a5d661aSToomas Soome  */
138*4a5d661aSToomas Soome struct fatcache {
139*4a5d661aSToomas Soome 	int unit;	/* disk unit number */
140*4a5d661aSToomas Soome 	int size;	/* buffer (and fat) size in sectors */
141*4a5d661aSToomas Soome 	u_char *buf;
142*4a5d661aSToomas Soome };
143*4a5d661aSToomas Soome 
144*4a5d661aSToomas Soome static struct fatcache fat;
145*4a5d661aSToomas Soome 
146*4a5d661aSToomas Soome static int dosunmount(DOS_FS *);
147*4a5d661aSToomas Soome static int parsebs(DOS_FS *, DOS_BS *);
148*4a5d661aSToomas Soome static int namede(DOS_FS *, const char *, DOS_DE **);
149*4a5d661aSToomas Soome static int lookup(DOS_FS *, u_int, const char *, DOS_DE **);
150*4a5d661aSToomas Soome static void cp_xdnm(u_char *, DOS_XDE *);
151*4a5d661aSToomas Soome static void cp_sfn(u_char *, DOS_DE *);
152*4a5d661aSToomas Soome static off_t fsize(DOS_FS *, DOS_DE *);
153*4a5d661aSToomas Soome static int fatcnt(DOS_FS *, u_int);
154*4a5d661aSToomas Soome static int fatget(DOS_FS *, u_int *);
155*4a5d661aSToomas Soome static int fatend(u_int, u_int);
156*4a5d661aSToomas Soome static int ioread(DOS_FS *, u_int, void *, u_int);
157*4a5d661aSToomas Soome static int ioget(struct open_file *, daddr_t, size_t, void *, u_int);
158*4a5d661aSToomas Soome 
159*4a5d661aSToomas Soome static void
160*4a5d661aSToomas Soome dos_read_fat(DOS_FS *fs, struct open_file *fd)
161*4a5d661aSToomas Soome {
162*4a5d661aSToomas Soome     struct devdesc *dd = fd->f_devdata;
163*4a5d661aSToomas Soome 
164*4a5d661aSToomas Soome     if (fat.buf != NULL) {		/* can we reuse old buffer? */
165*4a5d661aSToomas Soome 	if (fat.size != fs->spf) {
166*4a5d661aSToomas Soome 	    free(fat.buf);		/* no, free old buffer */
167*4a5d661aSToomas Soome 	    fat.buf = NULL;
168*4a5d661aSToomas Soome 	}
169*4a5d661aSToomas Soome     }
170*4a5d661aSToomas Soome 
171*4a5d661aSToomas Soome     if (fat.buf == NULL)
172*4a5d661aSToomas Soome 	fat.buf = malloc(secbyt(fs->spf));
173*4a5d661aSToomas Soome 
174*4a5d661aSToomas Soome     if (fat.buf != NULL) {
175*4a5d661aSToomas Soome 	if (ioget(fd, fs->lsnfat, 0, fat.buf, secbyt(fs->spf)) == 0) {
176*4a5d661aSToomas Soome 	    fat.size = fs->spf;
177*4a5d661aSToomas Soome 	    fat.unit = dd->d_unit;
178*4a5d661aSToomas Soome 	    return;
179*4a5d661aSToomas Soome 	}
180*4a5d661aSToomas Soome     }
181*4a5d661aSToomas Soome     if (fat.buf != NULL)	/* got IO error */
182*4a5d661aSToomas Soome 	free(fat.buf);
183*4a5d661aSToomas Soome     fat.buf = NULL;
184*4a5d661aSToomas Soome     fat.unit = -1;	/* impossible unit */
185*4a5d661aSToomas Soome     fat.size = 0;
186*4a5d661aSToomas Soome }
187*4a5d661aSToomas Soome 
188*4a5d661aSToomas Soome /*
189*4a5d661aSToomas Soome  * Mount DOS filesystem
190*4a5d661aSToomas Soome  */
191*4a5d661aSToomas Soome static int
192*4a5d661aSToomas Soome dos_mount(DOS_FS *fs, struct open_file *fd)
193*4a5d661aSToomas Soome {
194*4a5d661aSToomas Soome     int err;
195*4a5d661aSToomas Soome     struct devdesc *dd = fd->f_devdata;
196*4a5d661aSToomas Soome     u_char *buf;
197*4a5d661aSToomas Soome 
198*4a5d661aSToomas Soome     bzero(fs, sizeof(DOS_FS));
199*4a5d661aSToomas Soome     fs->fd = fd;
200*4a5d661aSToomas Soome 
201*4a5d661aSToomas Soome     if ((err = !(buf = malloc(secbyt(1))) ? errno : 0) ||
202*4a5d661aSToomas Soome         (err = ioget(fs->fd, 0, 0, buf, secbyt(1))) ||
203*4a5d661aSToomas Soome         (err = parsebs(fs, (DOS_BS *)buf))) {
204*4a5d661aSToomas Soome 	if (buf != NULL)
205*4a5d661aSToomas Soome 	    free(buf);
206*4a5d661aSToomas Soome         (void)dosunmount(fs);
207*4a5d661aSToomas Soome         return(err);
208*4a5d661aSToomas Soome     }
209*4a5d661aSToomas Soome     free(buf);
210*4a5d661aSToomas Soome 
211*4a5d661aSToomas Soome     if (fat.buf == NULL || fat.unit != dd->d_unit)
212*4a5d661aSToomas Soome 	dos_read_fat(fs, fd);
213*4a5d661aSToomas Soome 
214*4a5d661aSToomas Soome     fs->root = dot[0];
215*4a5d661aSToomas Soome     fs->root.name[0] = ' ';
216*4a5d661aSToomas Soome     if (fs->fatsz == 32) {
217*4a5d661aSToomas Soome         fs->root.clus[0] = fs->rdcl & 0xff;
218*4a5d661aSToomas Soome         fs->root.clus[1] = (fs->rdcl >> 8) & 0xff;
219*4a5d661aSToomas Soome         fs->root.dex.h_clus[0] = (fs->rdcl >> 16) & 0xff;
220*4a5d661aSToomas Soome         fs->root.dex.h_clus[1] = (fs->rdcl >> 24) & 0xff;
221*4a5d661aSToomas Soome     }
222*4a5d661aSToomas Soome     return 0;
223*4a5d661aSToomas Soome }
224*4a5d661aSToomas Soome 
225*4a5d661aSToomas Soome /*
226*4a5d661aSToomas Soome  * Unmount mounted filesystem
227*4a5d661aSToomas Soome  */
228*4a5d661aSToomas Soome static int
229*4a5d661aSToomas Soome dos_unmount(DOS_FS *fs)
230*4a5d661aSToomas Soome {
231*4a5d661aSToomas Soome     int err;
232*4a5d661aSToomas Soome 
233*4a5d661aSToomas Soome     if (fs->links)
234*4a5d661aSToomas Soome         return(EBUSY);
235*4a5d661aSToomas Soome     if ((err = dosunmount(fs)))
236*4a5d661aSToomas Soome         return(err);
237*4a5d661aSToomas Soome     return 0;
238*4a5d661aSToomas Soome }
239*4a5d661aSToomas Soome 
240*4a5d661aSToomas Soome /*
241*4a5d661aSToomas Soome  * Common code shared by dos_mount() and dos_unmount()
242*4a5d661aSToomas Soome  */
243*4a5d661aSToomas Soome static int
244*4a5d661aSToomas Soome dosunmount(DOS_FS *fs)
245*4a5d661aSToomas Soome {
246*4a5d661aSToomas Soome     free(fs);
247*4a5d661aSToomas Soome     return(0);
248*4a5d661aSToomas Soome }
249*4a5d661aSToomas Soome 
250*4a5d661aSToomas Soome /*
251*4a5d661aSToomas Soome  * Open DOS file
252*4a5d661aSToomas Soome  */
253*4a5d661aSToomas Soome static int
254*4a5d661aSToomas Soome dos_open(const char *path, struct open_file *fd)
255*4a5d661aSToomas Soome {
256*4a5d661aSToomas Soome     DOS_DE *de;
257*4a5d661aSToomas Soome     DOS_FILE *f;
258*4a5d661aSToomas Soome     DOS_FS *fs;
259*4a5d661aSToomas Soome     u_int size, clus;
260*4a5d661aSToomas Soome     int err = 0;
261*4a5d661aSToomas Soome 
262*4a5d661aSToomas Soome     /* Allocate mount structure, associate with open */
263*4a5d661aSToomas Soome     fs = malloc(sizeof(DOS_FS));
264*4a5d661aSToomas Soome 
265*4a5d661aSToomas Soome     if ((err = dos_mount(fs, fd)))
266*4a5d661aSToomas Soome 	goto out;
267*4a5d661aSToomas Soome 
268*4a5d661aSToomas Soome     if ((err = namede(fs, path, &de)))
269*4a5d661aSToomas Soome 	goto out;
270*4a5d661aSToomas Soome 
271*4a5d661aSToomas Soome     clus = stclus(fs->fatsz, de);
272*4a5d661aSToomas Soome     size = cv4(de->size);
273*4a5d661aSToomas Soome 
274*4a5d661aSToomas Soome     if ((!(de->attr & FA_DIR) && (!clus != !size)) ||
275*4a5d661aSToomas Soome 	((de->attr & FA_DIR) && size) ||
276*4a5d661aSToomas Soome 	(clus && !okclus(fs, clus))) {
277*4a5d661aSToomas Soome         err = EINVAL;
278*4a5d661aSToomas Soome 	goto out;
279*4a5d661aSToomas Soome     }
280*4a5d661aSToomas Soome     f = malloc(sizeof(DOS_FILE));
281*4a5d661aSToomas Soome     bzero(f, sizeof(DOS_FILE));
282*4a5d661aSToomas Soome     f->fs = fs;
283*4a5d661aSToomas Soome     fs->links++;
284*4a5d661aSToomas Soome     f->de = *de;
285*4a5d661aSToomas Soome     fd->f_fsdata = (void *)f;
286*4a5d661aSToomas Soome 
287*4a5d661aSToomas Soome  out:
288*4a5d661aSToomas Soome     return(err);
289*4a5d661aSToomas Soome }
290*4a5d661aSToomas Soome 
291*4a5d661aSToomas Soome /*
292*4a5d661aSToomas Soome  * Read from file
293*4a5d661aSToomas Soome  */
294*4a5d661aSToomas Soome static int
295*4a5d661aSToomas Soome dos_read(struct open_file *fd, void *buf, size_t nbyte, size_t *resid)
296*4a5d661aSToomas Soome {
297*4a5d661aSToomas Soome     off_t size;
298*4a5d661aSToomas Soome     u_int nb, off, clus, c, cnt, n;
299*4a5d661aSToomas Soome     DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
300*4a5d661aSToomas Soome     int err = 0;
301*4a5d661aSToomas Soome 
302*4a5d661aSToomas Soome     /*
303*4a5d661aSToomas Soome      * as ioget() can be called *a lot*, use twiddle here.
304*4a5d661aSToomas Soome      * also 4 seems to be good value not to slow loading down too much:
305*4a5d661aSToomas Soome      * with 270MB file (~540k ioget() calls, twiddle can easily waste 4-5sec.
306*4a5d661aSToomas Soome      */
307*4a5d661aSToomas Soome     twiddle(4);
308*4a5d661aSToomas Soome     nb = (u_int)nbyte;
309*4a5d661aSToomas Soome     if ((size = fsize(f->fs, &f->de)) == -1)
310*4a5d661aSToomas Soome 	return EINVAL;
311*4a5d661aSToomas Soome     if (nb > (n = size - f->offset))
312*4a5d661aSToomas Soome 	nb = n;
313*4a5d661aSToomas Soome     off = f->offset;
314*4a5d661aSToomas Soome     if ((clus = stclus(f->fs->fatsz, &f->de)))
315*4a5d661aSToomas Soome 	off &= f->fs->bsize - 1;
316*4a5d661aSToomas Soome     c = f->c;
317*4a5d661aSToomas Soome     cnt = nb;
318*4a5d661aSToomas Soome     while (cnt) {
319*4a5d661aSToomas Soome 	n = 0;
320*4a5d661aSToomas Soome 	if (!c) {
321*4a5d661aSToomas Soome 	    if ((c = clus))
322*4a5d661aSToomas Soome 		n = bytblk(f->fs, f->offset);
323*4a5d661aSToomas Soome 	} else if (!off)
324*4a5d661aSToomas Soome 	    n++;
325*4a5d661aSToomas Soome 	while (n--) {
326*4a5d661aSToomas Soome 	    if ((err = fatget(f->fs, &c)))
327*4a5d661aSToomas Soome 		goto out;
328*4a5d661aSToomas Soome 	    if (!okclus(f->fs, c)) {
329*4a5d661aSToomas Soome 		err = EINVAL;
330*4a5d661aSToomas Soome 		goto out;
331*4a5d661aSToomas Soome 	    }
332*4a5d661aSToomas Soome 	}
333*4a5d661aSToomas Soome 	if (!clus || (n = f->fs->bsize - off) > cnt)
334*4a5d661aSToomas Soome 	    n = cnt;
335*4a5d661aSToomas Soome 	if ((err = ioread(f->fs, (c ? blkoff(f->fs, c) :
336*4a5d661aSToomas Soome 				      secbyt(f->fs->lsndir)) + off, buf, n)))
337*4a5d661aSToomas Soome 	    goto out;
338*4a5d661aSToomas Soome 	f->offset += n;
339*4a5d661aSToomas Soome 	f->c = c;
340*4a5d661aSToomas Soome 	off = 0;
341*4a5d661aSToomas Soome 	buf = (char *)buf + n;
342*4a5d661aSToomas Soome 	cnt -= n;
343*4a5d661aSToomas Soome     }
344*4a5d661aSToomas Soome  out:
345*4a5d661aSToomas Soome     if (resid)
346*4a5d661aSToomas Soome 	*resid = nbyte - nb + cnt;
347*4a5d661aSToomas Soome     return(err);
348*4a5d661aSToomas Soome }
349*4a5d661aSToomas Soome 
350*4a5d661aSToomas Soome /*
351*4a5d661aSToomas Soome  * Reposition within file
352*4a5d661aSToomas Soome  */
353*4a5d661aSToomas Soome static off_t
354*4a5d661aSToomas Soome dos_seek(struct open_file *fd, off_t offset, int whence)
355*4a5d661aSToomas Soome {
356*4a5d661aSToomas Soome     off_t off;
357*4a5d661aSToomas Soome     u_int size;
358*4a5d661aSToomas Soome     DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
359*4a5d661aSToomas Soome 
360*4a5d661aSToomas Soome     size = cv4(f->de.size);
361*4a5d661aSToomas Soome     switch (whence) {
362*4a5d661aSToomas Soome     case SEEK_SET:
363*4a5d661aSToomas Soome         off = 0;
364*4a5d661aSToomas Soome         break;
365*4a5d661aSToomas Soome     case SEEK_CUR:
366*4a5d661aSToomas Soome         off = f->offset;
367*4a5d661aSToomas Soome         break;
368*4a5d661aSToomas Soome     case SEEK_END:
369*4a5d661aSToomas Soome         off = size;
370*4a5d661aSToomas Soome         break;
371*4a5d661aSToomas Soome     default:
372*4a5d661aSToomas Soome 	errno = EINVAL;
373*4a5d661aSToomas Soome 	return(-1);
374*4a5d661aSToomas Soome     }
375*4a5d661aSToomas Soome     off += offset;
376*4a5d661aSToomas Soome     if (off < 0 || off > size) {
377*4a5d661aSToomas Soome 	errno = EINVAL;
378*4a5d661aSToomas Soome         return(-1);
379*4a5d661aSToomas Soome     }
380*4a5d661aSToomas Soome     f->offset = (u_int)off;
381*4a5d661aSToomas Soome     f->c = 0;
382*4a5d661aSToomas Soome     return(off);
383*4a5d661aSToomas Soome }
384*4a5d661aSToomas Soome 
385*4a5d661aSToomas Soome /*
386*4a5d661aSToomas Soome  * Close open file
387*4a5d661aSToomas Soome  */
388*4a5d661aSToomas Soome static int
389*4a5d661aSToomas Soome dos_close(struct open_file *fd)
390*4a5d661aSToomas Soome {
391*4a5d661aSToomas Soome     DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
392*4a5d661aSToomas Soome     DOS_FS *fs = f->fs;
393*4a5d661aSToomas Soome 
394*4a5d661aSToomas Soome     f->fs->links--;
395*4a5d661aSToomas Soome     free(f);
396*4a5d661aSToomas Soome     dos_unmount(fs);
397*4a5d661aSToomas Soome     return 0;
398*4a5d661aSToomas Soome }
399*4a5d661aSToomas Soome 
400*4a5d661aSToomas Soome /*
401*4a5d661aSToomas Soome  * Return some stat information on a file.
402*4a5d661aSToomas Soome  */
403*4a5d661aSToomas Soome static int
404*4a5d661aSToomas Soome dos_stat(struct open_file *fd, struct stat *sb)
405*4a5d661aSToomas Soome {
406*4a5d661aSToomas Soome     DOS_FILE *f = (DOS_FILE *)fd->f_fsdata;
407*4a5d661aSToomas Soome 
408*4a5d661aSToomas Soome     /* only important stuff */
409*4a5d661aSToomas Soome     sb->st_mode = f->de.attr & FA_DIR ? S_IFDIR | 0555 : S_IFREG | 0444;
410*4a5d661aSToomas Soome     sb->st_nlink = 1;
411*4a5d661aSToomas Soome     sb->st_uid = 0;
412*4a5d661aSToomas Soome     sb->st_gid = 0;
413*4a5d661aSToomas Soome     if ((sb->st_size = fsize(f->fs, &f->de)) == -1)
414*4a5d661aSToomas Soome 	return EINVAL;
415*4a5d661aSToomas Soome     return (0);
416*4a5d661aSToomas Soome }
417*4a5d661aSToomas Soome 
418*4a5d661aSToomas Soome static int
419*4a5d661aSToomas Soome dos_checksum(char *name, char *ext)
420*4a5d661aSToomas Soome {
421*4a5d661aSToomas Soome     int x, i;
422*4a5d661aSToomas Soome     char buf[11];
423*4a5d661aSToomas Soome 
424*4a5d661aSToomas Soome     bcopy(name, buf, 8);
425*4a5d661aSToomas Soome     bcopy(ext, buf+8, 3);
426*4a5d661aSToomas Soome     x = 0;
427*4a5d661aSToomas Soome     for (i = 0; i < 11; i++) {
428*4a5d661aSToomas Soome 	x = ((x & 1) << 7) | (x >> 1);
429*4a5d661aSToomas Soome 	x += buf[i];
430*4a5d661aSToomas Soome 	x &= 0xff;
431*4a5d661aSToomas Soome     }
432*4a5d661aSToomas Soome     return (x);
433*4a5d661aSToomas Soome }
434*4a5d661aSToomas Soome 
435*4a5d661aSToomas Soome static int
436*4a5d661aSToomas Soome dos_readdir(struct open_file *fd, struct dirent *d)
437*4a5d661aSToomas Soome {
438*4a5d661aSToomas Soome     /* DOS_FILE *f = (DOS_FILE *)fd->f_fsdata; */
439*4a5d661aSToomas Soome     u_char fn[261];
440*4a5d661aSToomas Soome     DOS_DIR dd;
441*4a5d661aSToomas Soome     size_t res;
442*4a5d661aSToomas Soome     u_int chk, x, xdn;
443*4a5d661aSToomas Soome     int err;
444*4a5d661aSToomas Soome 
445*4a5d661aSToomas Soome     x = chk = 0;
446*4a5d661aSToomas Soome     while (1) {
447*4a5d661aSToomas Soome 	xdn = x;
448*4a5d661aSToomas Soome 	x = 0;
449*4a5d661aSToomas Soome 	err = dos_read(fd, &dd, sizeof(dd), &res);
450*4a5d661aSToomas Soome 	if (err)
451*4a5d661aSToomas Soome 	    return (err);
452*4a5d661aSToomas Soome 	if (res == sizeof(dd))
453*4a5d661aSToomas Soome 	    return (ENOENT);
454*4a5d661aSToomas Soome 	if (dd.de.name[0] == 0)
455*4a5d661aSToomas Soome 	    return (ENOENT);
456*4a5d661aSToomas Soome 
457*4a5d661aSToomas Soome 	/* Skip deleted entries */
458*4a5d661aSToomas Soome 	if (dd.de.name[0] == 0xe5)
459*4a5d661aSToomas Soome 	    continue;
460*4a5d661aSToomas Soome 
461*4a5d661aSToomas Soome 	/* Check if directory entry is volume label */
462*4a5d661aSToomas Soome 	if (dd.de.attr & FA_LABEL) {
463*4a5d661aSToomas Soome 	    /*
464*4a5d661aSToomas Soome 	     * If volume label set, check if the current entry is
465*4a5d661aSToomas Soome 	     * extended entry (FA_XDE) for long file names.
466*4a5d661aSToomas Soome 	     */
467*4a5d661aSToomas Soome 	    if ((dd.de.attr & FA_MASK) == FA_XDE) {
468*4a5d661aSToomas Soome 		/*
469*4a5d661aSToomas Soome 		 * Read through all following extended entries
470*4a5d661aSToomas Soome 		 * to get the long file name. 0x40 marks the
471*4a5d661aSToomas Soome 		 * last entry containing part of long file name.
472*4a5d661aSToomas Soome 		 */
473*4a5d661aSToomas Soome 		if (dd.xde.seq & 0x40)
474*4a5d661aSToomas Soome 		    chk = dd.xde.chk;
475*4a5d661aSToomas Soome 		else if (dd.xde.seq != xdn - 1 || dd.xde.chk != chk)
476*4a5d661aSToomas Soome 		    continue;
477*4a5d661aSToomas Soome 		x = dd.xde.seq & ~0x40;
478*4a5d661aSToomas Soome 		if (x < 1 || x > 20) {
479*4a5d661aSToomas Soome 		    x = 0;
480*4a5d661aSToomas Soome 		    continue;
481*4a5d661aSToomas Soome 		}
482*4a5d661aSToomas Soome 		cp_xdnm(fn, &dd.xde);
483*4a5d661aSToomas Soome 	    } else {
484*4a5d661aSToomas Soome 		/* skip only volume label entries */
485*4a5d661aSToomas Soome 		continue;
486*4a5d661aSToomas Soome 	    }
487*4a5d661aSToomas Soome 	} else {
488*4a5d661aSToomas Soome 	    if (xdn == 1) {
489*4a5d661aSToomas Soome 		x = dos_checksum(dd.de.name, dd.de.ext);
490*4a5d661aSToomas Soome 		if (x == chk)
491*4a5d661aSToomas Soome 		    break;
492*4a5d661aSToomas Soome 	    } else {
493*4a5d661aSToomas Soome 		cp_sfn(fn, &dd.de);
494*4a5d661aSToomas Soome 		break;
495*4a5d661aSToomas Soome 	    }
496*4a5d661aSToomas Soome 	    x = 0;
497*4a5d661aSToomas Soome 	}
498*4a5d661aSToomas Soome     }
499*4a5d661aSToomas Soome 
500*4a5d661aSToomas Soome     d->d_fileno = (dd.de.clus[1] << 8) + dd.de.clus[0];
501*4a5d661aSToomas Soome     d->d_reclen = sizeof(*d);
502*4a5d661aSToomas Soome     d->d_type = (dd.de.attr & FA_DIR) ? DT_DIR : DT_REG;
503*4a5d661aSToomas Soome     memcpy(d->d_name, fn, sizeof(d->d_name));
504*4a5d661aSToomas Soome     return(0);
505*4a5d661aSToomas Soome }
506*4a5d661aSToomas Soome 
507*4a5d661aSToomas Soome /*
508*4a5d661aSToomas Soome  * Parse DOS boot sector
509*4a5d661aSToomas Soome  */
510*4a5d661aSToomas Soome static int
511*4a5d661aSToomas Soome parsebs(DOS_FS *fs, DOS_BS *bs)
512*4a5d661aSToomas Soome {
513*4a5d661aSToomas Soome     u_int sc;
514*4a5d661aSToomas Soome 
515*4a5d661aSToomas Soome     if ((bs->jmp[0] != 0x69 &&
516*4a5d661aSToomas Soome          bs->jmp[0] != 0xe9 &&
517*4a5d661aSToomas Soome          (bs->jmp[0] != 0xeb || bs->jmp[2] != 0x90)) ||
518*4a5d661aSToomas Soome         bs->bpb.media < 0xf0)
519*4a5d661aSToomas Soome         return EINVAL;
520*4a5d661aSToomas Soome     if (cv2(bs->bpb.secsiz) != SECSIZ)
521*4a5d661aSToomas Soome         return EINVAL;
522*4a5d661aSToomas Soome     if (!(fs->spc = bs->bpb.spc) || fs->spc & (fs->spc - 1))
523*4a5d661aSToomas Soome         return EINVAL;
524*4a5d661aSToomas Soome     fs->bsize = secbyt(fs->spc);
525*4a5d661aSToomas Soome     fs->bshift = ffs(fs->bsize) - 1;
526*4a5d661aSToomas Soome     if ((fs->spf = cv2(bs->bpb.spf))) {
527*4a5d661aSToomas Soome         if (bs->bpb.fats != 2)
528*4a5d661aSToomas Soome             return EINVAL;
529*4a5d661aSToomas Soome         if (!(fs->dirents = cv2(bs->bpb.dirents)))
530*4a5d661aSToomas Soome             return EINVAL;
531*4a5d661aSToomas Soome     } else {
532*4a5d661aSToomas Soome         if (!(fs->spf = cv4(bs->bpb.lspf)))
533*4a5d661aSToomas Soome             return EINVAL;
534*4a5d661aSToomas Soome         if (!bs->bpb.fats || bs->bpb.fats > 16)
535*4a5d661aSToomas Soome             return EINVAL;
536*4a5d661aSToomas Soome         if ((fs->rdcl = cv4(bs->bpb.rdcl)) < LOCLUS)
537*4a5d661aSToomas Soome             return EINVAL;
538*4a5d661aSToomas Soome     }
539*4a5d661aSToomas Soome     if (!(fs->lsnfat = cv2(bs->bpb.ressec)))
540*4a5d661aSToomas Soome         return EINVAL;
541*4a5d661aSToomas Soome     fs->lsndir = fs->lsnfat + fs->spf * bs->bpb.fats;
542*4a5d661aSToomas Soome     fs->lsndta = fs->lsndir + entsec(fs->dirents);
543*4a5d661aSToomas Soome     if (!(sc = cv2(bs->bpb.secs)) && !(sc = cv4(bs->bpb.lsecs)))
544*4a5d661aSToomas Soome         return EINVAL;
545*4a5d661aSToomas Soome     if (fs->lsndta > sc)
546*4a5d661aSToomas Soome         return EINVAL;
547*4a5d661aSToomas Soome     if ((fs->xclus = secblk(fs, sc - fs->lsndta) + 1) < LOCLUS)
548*4a5d661aSToomas Soome         return EINVAL;
549*4a5d661aSToomas Soome     fs->fatsz = fs->dirents ? fs->xclus < 0xff6 ? 12 : 16 : 32;
550*4a5d661aSToomas Soome     sc = (secbyt(fs->spf) << 1) / (fs->fatsz >> 2) - 1;
551*4a5d661aSToomas Soome     if (fs->xclus > sc)
552*4a5d661aSToomas Soome         fs->xclus = sc;
553*4a5d661aSToomas Soome     return 0;
554*4a5d661aSToomas Soome }
555*4a5d661aSToomas Soome 
556*4a5d661aSToomas Soome /*
557*4a5d661aSToomas Soome  * Return directory entry from path
558*4a5d661aSToomas Soome  */
559*4a5d661aSToomas Soome static int
560*4a5d661aSToomas Soome namede(DOS_FS *fs, const char *path, DOS_DE **dep)
561*4a5d661aSToomas Soome {
562*4a5d661aSToomas Soome     char name[256];
563*4a5d661aSToomas Soome     DOS_DE *de;
564*4a5d661aSToomas Soome     char *s;
565*4a5d661aSToomas Soome     size_t n;
566*4a5d661aSToomas Soome     int err;
567*4a5d661aSToomas Soome 
568*4a5d661aSToomas Soome     err = 0;
569*4a5d661aSToomas Soome     de = &fs->root;
570*4a5d661aSToomas Soome     while (*path) {
571*4a5d661aSToomas Soome         while (*path == '/')
572*4a5d661aSToomas Soome             path++;
573*4a5d661aSToomas Soome         if (*path == '\0')
574*4a5d661aSToomas Soome             break;
575*4a5d661aSToomas Soome         if (!(s = strchr(path, '/')))
576*4a5d661aSToomas Soome             s = strchr(path, 0);
577*4a5d661aSToomas Soome         if ((n = s - path) > 255)
578*4a5d661aSToomas Soome             return ENAMETOOLONG;
579*4a5d661aSToomas Soome         memcpy(name, path, n);
580*4a5d661aSToomas Soome         name[n] = 0;
581*4a5d661aSToomas Soome         path = s;
582*4a5d661aSToomas Soome         if (!(de->attr & FA_DIR))
583*4a5d661aSToomas Soome             return ENOTDIR;
584*4a5d661aSToomas Soome         if ((err = lookup(fs, stclus(fs->fatsz, de), name, &de)))
585*4a5d661aSToomas Soome             return err;
586*4a5d661aSToomas Soome     }
587*4a5d661aSToomas Soome     *dep = de;
588*4a5d661aSToomas Soome     return 0;
589*4a5d661aSToomas Soome }
590*4a5d661aSToomas Soome 
591*4a5d661aSToomas Soome /*
592*4a5d661aSToomas Soome  * Lookup path segment
593*4a5d661aSToomas Soome  */
594*4a5d661aSToomas Soome static int
595*4a5d661aSToomas Soome lookup(DOS_FS *fs, u_int clus, const char *name, DOS_DE **dep)
596*4a5d661aSToomas Soome {
597*4a5d661aSToomas Soome     static DOS_DIR dir[DEPSEC];
598*4a5d661aSToomas Soome     u_char lfn[261];
599*4a5d661aSToomas Soome     u_char sfn[13];
600*4a5d661aSToomas Soome     u_int nsec, lsec, xdn, chk, sec, ent, x;
601*4a5d661aSToomas Soome     int err, ok;
602*4a5d661aSToomas Soome 
603*4a5d661aSToomas Soome     if (!clus)
604*4a5d661aSToomas Soome         for (ent = 0; ent < 2; ent++)
605*4a5d661aSToomas Soome             if (!strcasecmp(name, dotstr[ent])) {
606*4a5d661aSToomas Soome                 *dep = dot + ent;
607*4a5d661aSToomas Soome                 return 0;
608*4a5d661aSToomas Soome             }
609*4a5d661aSToomas Soome     if (!clus && fs->fatsz == 32)
610*4a5d661aSToomas Soome         clus = fs->rdcl;
611*4a5d661aSToomas Soome     nsec = !clus ? entsec(fs->dirents) : fs->spc;
612*4a5d661aSToomas Soome     lsec = 0;
613*4a5d661aSToomas Soome     xdn = chk = 0;
614*4a5d661aSToomas Soome     for (;;) {
615*4a5d661aSToomas Soome         if (!clus && !lsec)
616*4a5d661aSToomas Soome             lsec = fs->lsndir;
617*4a5d661aSToomas Soome         else if (okclus(fs, clus))
618*4a5d661aSToomas Soome             lsec = blklsn(fs, clus);
619*4a5d661aSToomas Soome         else
620*4a5d661aSToomas Soome             return EINVAL;
621*4a5d661aSToomas Soome         for (sec = 0; sec < nsec; sec++) {
622*4a5d661aSToomas Soome             if ((err = ioget(fs->fd, lsec + sec, 0, dir, secbyt(1))))
623*4a5d661aSToomas Soome                 return err;
624*4a5d661aSToomas Soome             for (ent = 0; ent < DEPSEC; ent++) {
625*4a5d661aSToomas Soome                 if (!*dir[ent].de.name)
626*4a5d661aSToomas Soome                     return ENOENT;
627*4a5d661aSToomas Soome                 if (*dir[ent].de.name != 0xe5) {
628*4a5d661aSToomas Soome                     if ((dir[ent].de.attr & FA_MASK) == FA_XDE) {
629*4a5d661aSToomas Soome                         x = dir[ent].xde.seq;
630*4a5d661aSToomas Soome                         if (x & 0x40 || (x + 1 == xdn &&
631*4a5d661aSToomas Soome                                          dir[ent].xde.chk == chk)) {
632*4a5d661aSToomas Soome                             if (x & 0x40) {
633*4a5d661aSToomas Soome                                 chk = dir[ent].xde.chk;
634*4a5d661aSToomas Soome                                 x &= ~0x40;
635*4a5d661aSToomas Soome                             }
636*4a5d661aSToomas Soome                             if (x >= 1 && x <= 20) {
637*4a5d661aSToomas Soome                                 cp_xdnm(lfn, &dir[ent].xde);
638*4a5d661aSToomas Soome                                 xdn = x;
639*4a5d661aSToomas Soome                                 continue;
640*4a5d661aSToomas Soome                             }
641*4a5d661aSToomas Soome                         }
642*4a5d661aSToomas Soome                     } else if (!(dir[ent].de.attr & FA_LABEL)) {
643*4a5d661aSToomas Soome                         if ((ok = xdn == 1)) {
644*4a5d661aSToomas Soome 			    x = dos_checksum(dir[ent].de.name, dir[ent].de.ext);
645*4a5d661aSToomas Soome                             ok = chk == x &&
646*4a5d661aSToomas Soome                                 !strcasecmp(name, (const char *)lfn);
647*4a5d661aSToomas Soome                         }
648*4a5d661aSToomas Soome                         if (!ok) {
649*4a5d661aSToomas Soome                             cp_sfn(sfn, &dir[ent].de);
650*4a5d661aSToomas Soome                             ok = !strcasecmp(name, (const char *)sfn);
651*4a5d661aSToomas Soome                         }
652*4a5d661aSToomas Soome                         if (ok) {
653*4a5d661aSToomas Soome                             *dep = &dir[ent].de;
654*4a5d661aSToomas Soome                             return 0;
655*4a5d661aSToomas Soome                         }
656*4a5d661aSToomas Soome                     }
657*4a5d661aSToomas Soome 		}
658*4a5d661aSToomas Soome                 xdn = 0;
659*4a5d661aSToomas Soome             }
660*4a5d661aSToomas Soome         }
661*4a5d661aSToomas Soome         if (!clus)
662*4a5d661aSToomas Soome             break;
663*4a5d661aSToomas Soome         if ((err = fatget(fs, &clus)))
664*4a5d661aSToomas Soome             return err;
665*4a5d661aSToomas Soome         if (fatend(fs->fatsz, clus))
666*4a5d661aSToomas Soome             break;
667*4a5d661aSToomas Soome     }
668*4a5d661aSToomas Soome     return ENOENT;
669*4a5d661aSToomas Soome }
670*4a5d661aSToomas Soome 
671*4a5d661aSToomas Soome /*
672*4a5d661aSToomas Soome  * Copy name from extended directory entry
673*4a5d661aSToomas Soome  */
674*4a5d661aSToomas Soome static void
675*4a5d661aSToomas Soome cp_xdnm(u_char *lfn, DOS_XDE *xde)
676*4a5d661aSToomas Soome {
677*4a5d661aSToomas Soome     static struct {
678*4a5d661aSToomas Soome         u_int off;
679*4a5d661aSToomas Soome         u_int dim;
680*4a5d661aSToomas Soome     } ix[3] = {
681*4a5d661aSToomas Soome         {offsetof(DOS_XDE, name1), sizeof(xde->name1) / 2},
682*4a5d661aSToomas Soome         {offsetof(DOS_XDE, name2), sizeof(xde->name2) / 2},
683*4a5d661aSToomas Soome         {offsetof(DOS_XDE, name3), sizeof(xde->name3) / 2}
684*4a5d661aSToomas Soome     };
685*4a5d661aSToomas Soome     u_char *p;
686*4a5d661aSToomas Soome     u_int n, x, c;
687*4a5d661aSToomas Soome 
688*4a5d661aSToomas Soome     lfn += 13 * ((xde->seq & ~0x40) - 1);
689*4a5d661aSToomas Soome     for (n = 0; n < 3; n++)
690*4a5d661aSToomas Soome         for (p = (u_char *)xde + ix[n].off, x = ix[n].dim; x;
691*4a5d661aSToomas Soome 	     p += 2, x--) {
692*4a5d661aSToomas Soome             if ((c = cv2(p)) && (c < 32 || c > 127))
693*4a5d661aSToomas Soome                 c = '?';
694*4a5d661aSToomas Soome             if (!(*lfn++ = c))
695*4a5d661aSToomas Soome                 return;
696*4a5d661aSToomas Soome         }
697*4a5d661aSToomas Soome     if (xde->seq & 0x40)
698*4a5d661aSToomas Soome         *lfn = 0;
699*4a5d661aSToomas Soome }
700*4a5d661aSToomas Soome 
701*4a5d661aSToomas Soome /*
702*4a5d661aSToomas Soome  * Copy short filename
703*4a5d661aSToomas Soome  */
704*4a5d661aSToomas Soome static void
705*4a5d661aSToomas Soome cp_sfn(u_char *sfn, DOS_DE *de)
706*4a5d661aSToomas Soome {
707*4a5d661aSToomas Soome     u_char *p;
708*4a5d661aSToomas Soome     int j, i;
709*4a5d661aSToomas Soome 
710*4a5d661aSToomas Soome     p = sfn;
711*4a5d661aSToomas Soome     if (*de->name != ' ') {
712*4a5d661aSToomas Soome         for (j = 7; de->name[j] == ' '; j--);
713*4a5d661aSToomas Soome         for (i = 0; i <= j; i++)
714*4a5d661aSToomas Soome             *p++ = de->name[i];
715*4a5d661aSToomas Soome         if (*de->ext != ' ') {
716*4a5d661aSToomas Soome             *p++ = '.';
717*4a5d661aSToomas Soome             for (j = 2; de->ext[j] == ' '; j--);
718*4a5d661aSToomas Soome             for (i = 0; i <= j; i++)
719*4a5d661aSToomas Soome                 *p++ = de->ext[i];
720*4a5d661aSToomas Soome         }
721*4a5d661aSToomas Soome     }
722*4a5d661aSToomas Soome     *p = 0;
723*4a5d661aSToomas Soome     if (*sfn == 5)
724*4a5d661aSToomas Soome         *sfn = 0xe5;
725*4a5d661aSToomas Soome }
726*4a5d661aSToomas Soome 
727*4a5d661aSToomas Soome /*
728*4a5d661aSToomas Soome  * Return size of file in bytes
729*4a5d661aSToomas Soome  */
730*4a5d661aSToomas Soome static off_t
731*4a5d661aSToomas Soome fsize(DOS_FS *fs, DOS_DE *de)
732*4a5d661aSToomas Soome {
733*4a5d661aSToomas Soome    u_long size;
734*4a5d661aSToomas Soome    u_int c;
735*4a5d661aSToomas Soome    int n;
736*4a5d661aSToomas Soome 
737*4a5d661aSToomas Soome    if (!(size = cv4(de->size)) && de->attr & FA_DIR) {
738*4a5d661aSToomas Soome       if (!(c = cv2(de->clus)))
739*4a5d661aSToomas Soome          size = fs->dirents * sizeof(DOS_DE);
740*4a5d661aSToomas Soome       else {
741*4a5d661aSToomas Soome          if ((n = fatcnt(fs, c)) == -1)
742*4a5d661aSToomas Soome             return n;
743*4a5d661aSToomas Soome          size = blkbyt(fs, n);
744*4a5d661aSToomas Soome       }
745*4a5d661aSToomas Soome    }
746*4a5d661aSToomas Soome    return size;
747*4a5d661aSToomas Soome }
748*4a5d661aSToomas Soome 
749*4a5d661aSToomas Soome /*
750*4a5d661aSToomas Soome  * Count number of clusters in chain
751*4a5d661aSToomas Soome  */
752*4a5d661aSToomas Soome static int
753*4a5d661aSToomas Soome fatcnt(DOS_FS *fs, u_int c)
754*4a5d661aSToomas Soome {
755*4a5d661aSToomas Soome    int n;
756*4a5d661aSToomas Soome 
757*4a5d661aSToomas Soome    for (n = 0; okclus(fs, c); n++)
758*4a5d661aSToomas Soome       if (fatget(fs, &c))
759*4a5d661aSToomas Soome 	  return -1;
760*4a5d661aSToomas Soome    return fatend(fs->fatsz, c) ? n : -1;
761*4a5d661aSToomas Soome }
762*4a5d661aSToomas Soome 
763*4a5d661aSToomas Soome /*
764*4a5d661aSToomas Soome  * Get next cluster in cluster chain. Use in core fat cache unless another
765*4a5d661aSToomas Soome  * device replaced it.
766*4a5d661aSToomas Soome  */
767*4a5d661aSToomas Soome static int
768*4a5d661aSToomas Soome fatget(DOS_FS *fs, u_int *c)
769*4a5d661aSToomas Soome {
770*4a5d661aSToomas Soome     u_char buf[4];
771*4a5d661aSToomas Soome     u_char *s;
772*4a5d661aSToomas Soome     u_int x, offset, off, n, nbyte, lsec;
773*4a5d661aSToomas Soome     struct devdesc *dd = fs->fd->f_devdata;
774*4a5d661aSToomas Soome     int err = 0;
775*4a5d661aSToomas Soome 
776*4a5d661aSToomas Soome     if (fat.unit != dd->d_unit) {
777*4a5d661aSToomas Soome 	/* fat cache was changed to another device, don't use it */
778*4a5d661aSToomas Soome 	err = ioread(fs, secbyt(fs->lsnfat) + fatoff(fs->fatsz, *c), buf,
779*4a5d661aSToomas Soome 	    fs->fatsz != 32 ? 2 : 4);
780*4a5d661aSToomas Soome 	if (err)
781*4a5d661aSToomas Soome 	    return (err);
782*4a5d661aSToomas Soome     } else {
783*4a5d661aSToomas Soome 	offset = fatoff(fs->fatsz, *c);
784*4a5d661aSToomas Soome 	nbyte = fs->fatsz != 32 ? 2 : 4;
785*4a5d661aSToomas Soome 
786*4a5d661aSToomas Soome 	s = buf;
787*4a5d661aSToomas Soome 	if ((off = offset & (SECSIZ - 1))) {
788*4a5d661aSToomas Soome 	    offset -= off;
789*4a5d661aSToomas Soome 	    lsec = bytsec(offset);
790*4a5d661aSToomas Soome 	    offset += SECSIZ;
791*4a5d661aSToomas Soome 	    if ((n = SECSIZ - off) > nbyte)
792*4a5d661aSToomas Soome 		n = nbyte;
793*4a5d661aSToomas Soome 	    memcpy(s, fat.buf + secbyt(lsec) + off, n);
794*4a5d661aSToomas Soome 	    s += n;
795*4a5d661aSToomas Soome 	    nbyte -= n;
796*4a5d661aSToomas Soome 	}
797*4a5d661aSToomas Soome 	n = nbyte & (SECSIZ - 1);
798*4a5d661aSToomas Soome 	if (nbyte -= n) {
799*4a5d661aSToomas Soome 	    memcpy(s, fat.buf + secbyt(bytsec(offset)), nbyte);
800*4a5d661aSToomas Soome 	    offset += nbyte;
801*4a5d661aSToomas Soome 	    s += nbyte;
802*4a5d661aSToomas Soome 	}
803*4a5d661aSToomas Soome 	if (n)
804*4a5d661aSToomas Soome 	    memcpy(s, fat.buf + secbyt(bytsec(offset)), n);
805*4a5d661aSToomas Soome     }
806*4a5d661aSToomas Soome 
807*4a5d661aSToomas Soome     x = fs->fatsz != 32 ? cv2(buf) : cv4(buf);
808*4a5d661aSToomas Soome     *c = fs->fatsz == 12 ? *c & 1 ? x >> 4 : x & 0xfff : x;
809*4a5d661aSToomas Soome     return (0);
810*4a5d661aSToomas Soome }
811*4a5d661aSToomas Soome 
812*4a5d661aSToomas Soome /*
813*4a5d661aSToomas Soome  * Is cluster an end-of-chain marker?
814*4a5d661aSToomas Soome  */
815*4a5d661aSToomas Soome static int
816*4a5d661aSToomas Soome fatend(u_int sz, u_int c)
817*4a5d661aSToomas Soome {
818*4a5d661aSToomas Soome     return c > (sz == 12 ? 0xff7U : sz == 16 ? 0xfff7U : 0xffffff7);
819*4a5d661aSToomas Soome }
820*4a5d661aSToomas Soome 
821*4a5d661aSToomas Soome /*
822*4a5d661aSToomas Soome  * Offset-based I/O primitive
823*4a5d661aSToomas Soome  */
824*4a5d661aSToomas Soome static int
825*4a5d661aSToomas Soome ioread(DOS_FS *fs, u_int offset, void *buf, u_int nbyte)
826*4a5d661aSToomas Soome {
827*4a5d661aSToomas Soome     char *s;
828*4a5d661aSToomas Soome     u_int off, n;
829*4a5d661aSToomas Soome     int err;
830*4a5d661aSToomas Soome 
831*4a5d661aSToomas Soome     s = buf;
832*4a5d661aSToomas Soome     if ((off = offset & (SECSIZ - 1))) {
833*4a5d661aSToomas Soome         offset -= off;
834*4a5d661aSToomas Soome         if ((n = SECSIZ - off) > nbyte)
835*4a5d661aSToomas Soome             n = nbyte;
836*4a5d661aSToomas Soome         if ((err = ioget(fs->fd, bytsec(offset), off, s, n)))
837*4a5d661aSToomas Soome             return err;
838*4a5d661aSToomas Soome         offset += SECSIZ;
839*4a5d661aSToomas Soome         s += n;
840*4a5d661aSToomas Soome         nbyte -= n;
841*4a5d661aSToomas Soome     }
842*4a5d661aSToomas Soome     n = nbyte & (SECSIZ - 1);
843*4a5d661aSToomas Soome     if (nbyte -= n) {
844*4a5d661aSToomas Soome         if ((err = ioget(fs->fd, bytsec(offset), 0, s, nbyte)))
845*4a5d661aSToomas Soome             return err;
846*4a5d661aSToomas Soome         offset += nbyte;
847*4a5d661aSToomas Soome         s += nbyte;
848*4a5d661aSToomas Soome     }
849*4a5d661aSToomas Soome     if (n) {
850*4a5d661aSToomas Soome         if ((err = ioget(fs->fd, bytsec(offset), 0, s, n)))
851*4a5d661aSToomas Soome             return err;
852*4a5d661aSToomas Soome     }
853*4a5d661aSToomas Soome     return 0;
854*4a5d661aSToomas Soome }
855*4a5d661aSToomas Soome 
856*4a5d661aSToomas Soome /*
857*4a5d661aSToomas Soome  * Sector-based I/O primitive
858*4a5d661aSToomas Soome  */
859*4a5d661aSToomas Soome static int
860*4a5d661aSToomas Soome ioget(struct open_file *fd, daddr_t lsec, size_t offset, void *buf, u_int size)
861*4a5d661aSToomas Soome {
862*4a5d661aSToomas Soome     return ((fd->f_dev->dv_strategy)(fd->f_devdata, F_READ, lsec, offset,
863*4a5d661aSToomas Soome 	size, buf, NULL));
864*4a5d661aSToomas Soome }
865