1 /*-
2  * Copyright (c) 2005, 2008, 2009, 2011
3  *	Thorsten Glaser <tg@mirbsd.org>
4  *
5  * Provided that these terms and disclaimer and all copyright notices
6  * are retained or reproduced in an accompanying document, permission
7  * is granted to deal in this work without restriction, including un-
8  * limited rights to use, publicly perform, distribute, sell, modify,
9  * merge, give away, or sublicence.
10  *
11  * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
12  * the utmost extent permitted by applicable law, neither express nor
13  * implied; without malicious intent or gross negligence. In no event
14  * may a licensor, author or contributor be held liable for indirect,
15  * direct, other damage, loss, or other issues arising in any way out
16  * of dealing in the work, even if advised of the possibility of such
17  * damage or existence of a defect, except proven that it results out
18  * of said person's immediate fault when using the work as intended.
19  */
20 
21 #include <sys/param.h>
22 #include <sys/time.h>
23 #include <sys/stat.h>
24 #include <sys/slibkern.h>
25 #include <lib/libsa/stand.h>
26 #include <lib/libsa/fat.h>
27 
28 __RCSID("$MirOS: src/sys/lib/libsa/fat.c,v 1.20 2011/01/03 19:00:17 tg Exp $");
29 
30 #if BYTE_ORDER != LITTLE_ENDIAN
31 #define getlew(ofs) (buf[(ofs)] + ((unsigned)buf[(ofs) + 1] << 8))
32 #define getled(ofs) (getlew(ofs) + (getlew((ofs) + 2) << 16))
33 #else
34 #define getlew(ofs) (*((uint16_t *)(&buf[(ofs)])))
35 #define getled(ofs) (*((uint32_t *)(&buf[(ofs)])))
36 #endif
37 
38 enum fat_type {
39 	FAT12, FAT16, FAT28
40 };
41 
42 struct fat_file {
43 	struct open_file *open_file;	/* our "parent" structure */
44 	uint32_t rootofs;		/* root offset (FAT12/FAT16) */
45 	uint32_t invalc;		/* first invalid cluster no. */
46 	uint32_t firstds;		/* first data sector */
47 	char *fatbuf;			/* sector buffer (FAT) */
48 	uint32_t fatsec;		/* which sector is buffered */
49 	char *databuf;			/* cluster buffer (data) */
50 	uint32_t datasec;		/* which cluster is buffered */
51 	uint32_t nodecluster;		/* current node: cluster */
52 	uint32_t nodeseekp;		/* current node: file offset */
53 	uint32_t nodesize;		/* current node: file size */
54 	uint16_t ress;			/* BPB: reserved sectors */
55 	uint16_t bpc;			/* BPB: bytes per cluster */
56 	uint8_t nodetype;		/* 1=directory 2=file */
57 	enum fat_type type;		/* FAT12, FAT16 or FAT28 */
58 };
59 
60 static int rd(struct open_file *, void *, daddr_t, size_t);
61 static uint32_t getfat(struct fat_file *, uint32_t);
62 static int search_dir(struct open_file *, char *);
63 
64 static unsigned char fat_dirbuf[32];
65 
66 #define fillbuf(ff)	rd((ff)->open_file, (ff)->databuf, \
67 			    ((ff)->datasec - 2) * ((ff)->bpc / 512) + \
68 			    (ff)->firstds, (ff)->bpc)
69 #define locase(c)	(((c) < 'A') || ((c) > 'Z') ? (c) : (c) - 'A' + 'a')
70 
71 static int
rd(struct open_file * f,void * buf,daddr_t blk,size_t size)72 rd(struct open_file *f, void *buf, daddr_t blk, size_t size)
73 {
74 	size_t buflen;
75 	int rv;
76 
77 	twiddle();
78 	if ((rv = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
79 	    blk, size, buf, &buflen)))
80 		;
81 	else if (buflen != size)
82 		rv = EIO;
83 	return (rv);
84 }
85 
86 static uint32_t
getfat(struct fat_file * ff,uint32_t entry)87 getfat(struct fat_file *ff, uint32_t entry)
88 {
89 	uint32_t fofs, fsec, secofs, rv = 0, b;
90 	char *buf;
91 
92 	b = (ff->type == FAT12) ? 1024 : 512;
93 	if (ff->fatbuf == NULL) {
94 		ff->fatbuf = alloc(b);
95 		ff->fatsec = 0;
96 	}
97 	buf = ff->fatbuf;
98 
99 	fofs = (ff->type == FAT16) ? (entry << 1) :
100 	    ((ff->type == FAT28) ? (entry << 2) :
101 	    (entry + (entry / 2)));
102 	fsec = ff->ress + (fofs / 512);
103 	secofs = fofs % 512;
104 
105 	if (fsec != ff->fatsec) {
106 		if ((rv = rd(ff->open_file, buf, fsec, b))) {
107 			free(buf, b);
108 			ff->fatbuf = NULL;
109 			return (rv | 0x80000000);
110 		}
111 		ff->fatsec = fsec;
112 	}
113 
114 	switch (ff->type) {
115 	case FAT28:
116 		rv = getled(secofs) & 0x0FFFFFFF;
117 		break;
118 	case FAT16:
119 		rv = getlew(secofs);
120 		break;
121 	case FAT12:
122 		rv = getlew(secofs);
123 		if (entry & 1)
124 			rv >>= 4;
125 		else
126 			rv &= 0x0FFF;
127 		break;
128 	}
129 	return (rv);
130 }
131 
132 int
fat_open(char * path,struct open_file * f)133 fat_open(char *path, struct open_file *f)
134 {
135 	struct fat_file *ff;
136 	unsigned char *buf;
137 	int rv;
138 	uint32_t spc, nfats, rootcnt, spd, spf, dblk;
139 
140 	/* allocate fs specific data structure */
141 	ff = alloc(sizeof (struct fat_file));
142 	bzero(ff, sizeof (struct fat_file));
143 	f->f_fsdata = ff;
144 	ff->open_file = f;
145 
146 	/* allocate space and read BPB */
147 	buf = alloc(512);
148 	if ((rv = rd(f, buf, 0, 512)))
149 		goto out;
150 
151 	/* parse BPB */
152 	rv = EINVAL;
153 	if (((buf[0] != 0xEB) || (buf[2] != 0x90)) && (buf[0] != 0xE9))
154 		goto out;
155 	if ((buf[510] != 0x55) || (buf[511] != 0xAA))
156 		goto out;
157 
158 	/* only 512 Bytes per sector for now */
159 	if (getlew(11) != 512)
160 		goto out;
161 
162 	spc = buf[13];
163 	if (!spc || spc > 0x40 || (spc & (spc - 1)))
164 		/* 0, > 0x40, or not a power of two */
165 		goto out;
166 	ff->bpc = spc * 512;
167 	if ((ff->ress = getlew(14)) < 1)
168 		goto out;
169 	nfats = buf[16];
170 	rootcnt = (getlew(17) * 32 + 511) / 512;
171 	spd = getlew(19);
172 	spf = getlew(22);
173 	if (!spd)
174 		spd = getled(32);
175 	if (!spf)
176 		spf = getled(36);
177 	ff->rootofs = ff->ress + (nfats * spf);
178 	ff->firstds = ff->rootofs + rootcnt;
179 	dblk = (spd - ff->firstds) / spc;
180 	ff->type = (dblk < 4085) ? FAT12 : ((dblk < 65525) ? FAT16 : FAT28);
181 	switch (ff->type) {
182 	case FAT12:
183 		ff->invalc = 0x0FF7;
184 		break;
185 	case FAT16:
186 		ff->invalc = 0xFFF7;
187 		break;
188 	case FAT28:
189 		ff->invalc = 0x0FFFFFF7;
190 		if ((ff->rootofs = getled(44)) < 2)
191 			goto out;
192 		break;
193 	}
194 
195 	ff->nodetype = 1;	/* 1 = directory, 2 = file */
196 	ff->nodecluster = 1;	/* 1 = root directory (special) */
197 	ff->nodeseekp = 0;
198 	if (ff->type == FAT28)
199 		/* we have a real root directory file */
200 		ff->nodecluster = ff->rootofs;
201 	else
202 		ff->nodesize = rootcnt * 512;
203 
204 	while (*path) {
205 		char c, *cp, *npath;
206 		size_t len;
207 
208 		/* remove extra separators */
209 		while (*path == '/')
210 			++path;
211 		if (*path == '\0')
212 			break;
213 
214 		/* check that current node is a directory */
215 		if (ff->nodetype != 1) {
216 			rv = ENOTDIR;
217 			goto out;
218 		}
219 
220 		/* get next component of path name */
221 		len = 0;
222 		cp = path;
223 		while (((c = *cp) != '\0') && (c != '/')) {
224 			if (++len > 14) {
225 				rv = ENOENT;
226 				goto out;
227 			}
228 			cp++;
229 		}
230 		if (cp == path + 1 && *path == '.') {
231 			path = cp;
232 			continue;
233 		}
234 		npath = cp;
235 		*cp = '\0';
236 
237 		/* look up component in current directory */
238 		rv = search_dir(f, path);
239 		*cp = c;
240 		if (rv)
241 			goto out;
242 		path = npath;
243 	}
244 
245 	/* found terminal component */
246 	rv = 0;
247  out:
248 	free(buf, 512);
249 	if (rv) {
250 		if (ff->fatbuf)
251 			free(ff->fatbuf, (ff->type == FAT12) ? 1024 : 512);
252 		if (ff->databuf)
253 			free(ff->databuf, ff->bpc);
254 		free(ff, sizeof (struct fat_file));
255 	}
256 	return (rv);
257 }
258 
259 int
fat_close(struct open_file * f)260 fat_close(struct open_file *f)
261 {
262 	struct fat_file *ff;
263 
264 	if (f == NULL)
265 		return (0);
266 
267 	ff = f->f_fsdata;
268 	if (ff) {
269 		if (ff->fatbuf)
270 			free(ff->fatbuf, (ff->type == FAT12) ? 1024 : 512);
271 		if (ff->databuf)
272 			free(ff->databuf, ff->bpc);
273 		free(ff, sizeof (struct fat_file));
274 	}
275 	return (0);
276 }
277 
278 int
fat_read(struct open_file * f,void * buf_,size_t size,size_t * resid)279 fat_read(struct open_file *f, void *buf_, size_t size, size_t *resid)
280 {
281 	struct fat_file *ff = f->f_fsdata;
282 	int rv = 0, otmp, isroot = 0, blksiz = ff->bpc;
283 	size_t stmp;
284 	char *buf = buf_;
285 
286 	if (!ff->databuf) {
287 		ff->databuf = alloc(ff->bpc);
288 		ff->datasec = 0;
289 	}
290 
291 	if (ff->nodecluster == 1) {
292 		isroot = 1;
293 		blksiz = 512;
294 	}
295 
296 	if (ff->datasec == 0) {
297 		int skip = (ff->nodeseekp / ff->bpc);
298 		ff->datasec = ff->nodecluster;
299 		if (ff->datasec == 1) {
300 			ff->datasec = ff->nodeseekp / 512 + ff->rootofs;
301 			if ((rv = rd(f, ff->databuf, ff->datasec, 512))) {
302 				ff->datasec = 0;
303 				goto out;
304 			}
305 			goto filled;
306 		}
307 		if ((ff->datasec == 0) || (ff->datasec >= ff->invalc)) {
308  invclust:
309 			ff->datasec = rv = 0;
310 			goto out;
311 		}
312 		while (skip--) {
313 			ff->datasec = getfat(ff, ff->datasec);
314 			if ((ff->datasec == 0) || (ff->datasec >= ff->invalc))
315 				goto invclust;
316 		}
317 		if ((rv = fillbuf(ff))) {
318  rderr:
319 			ff->datasec = 0;
320 			goto out;
321 		}
322 	}
323  filled:
324 
325 	while (size > 0) {
326 		/* don't read beyond EOF */
327 		if (ff->nodesize && (ff->nodeseekp >= ff->nodesize))
328 			break;
329 
330 		otmp = ff->nodeseekp % blksiz;
331 		stmp = blksiz - otmp;
332 		if (ff->nodesize && (ff->nodesize - ff->nodeseekp) < stmp)
333 			stmp = ff->nodesize - ff->nodeseekp;
334 		if (stmp >= size)
335 			stmp = size;
336 		memmove(buf, ff->databuf + otmp, stmp);
337 
338 		size -= stmp;
339 		buf += stmp;
340 		ff->nodeseekp += stmp;
341 
342 		if (ff->nodeseekp % blksiz == 0) {
343 			if (isroot) {
344 				ff->datasec++;
345 				if ((rv = rd(f, ff->databuf, ff->datasec, 512)))
346 					goto rderr;
347 				goto refilled;
348 			}
349 			ff->datasec = getfat(ff, ff->datasec);
350 			if ((ff->datasec >= ff->invalc) || (rv = fillbuf(ff)))
351 				ff->datasec = 0;
352 			if (ff->datasec == 0 && size)
353 				goto out;
354  refilled:		;
355  		}
356 	}
357  out:
358 	if (resid)
359 		*resid = size;
360 	return (rv);
361 }
362 
363 int
fat_write(struct open_file * f __unused,void * buf __unused,size_t size __unused,size_t * resid __unused)364 fat_write(struct open_file *f __unused, void *buf __unused,
365     size_t size __unused, size_t *resid __unused)
366 {
367 	return (EROFS);
368 }
369 
370 off_t
fat_seek(struct open_file * f,off_t offset,int where)371 fat_seek(struct open_file *f, off_t offset, int where)
372 {
373 	switch (where) {
374 	case SEEK_SET:
375 		((struct fat_file *)f->f_fsdata)->nodeseekp = offset;
376 		break;
377 	case SEEK_CUR:
378 		((struct fat_file *)f->f_fsdata)->nodeseekp += offset;
379 		break;
380 	case SEEK_END:
381 		((struct fat_file *)f->f_fsdata)->nodeseekp =
382 		    ((struct fat_file *)f->f_fsdata)->nodesize + offset;
383 		break;
384 	default:
385 		return (-1);
386 	}
387 	/* invalidate file data buffer */
388 	((struct fat_file *)f->f_fsdata)->datasec = 0;
389 	return (((struct fat_file *)f->f_fsdata)->nodeseekp);
390 }
391 
392 int
fat_stat(struct open_file * f,struct stat * sb)393 fat_stat(struct open_file *f, struct stat *sb)
394 {
395 	/* quick and dirty */
396 	bzero(sb, sizeof (struct stat));
397 	sb->st_mode = (((struct fat_file *)f->f_fsdata)->nodetype == 1) ?
398 	    040555 : 0444;
399 	sb->st_size = ((struct fat_file *)f->f_fsdata)->nodesize;
400 
401 	return (0);
402 }
403 
404 int
fat_readdir(struct open_file * f,char * name)405 fat_readdir(struct open_file *f, char *name)
406 {
407 	int rv;
408 	char ch, *cp;
409 	size_t sr;
410 
411 	/* reset? */
412 	if (name == NULL) {
413 		fat_seek(f, 0, SEEK_SET);
414 		return (0);
415 	}
416 
417 	*name = '\0';
418  getrec:
419 	if ((rv = fat_read(f, fat_dirbuf, sizeof (fat_dirbuf), &sr)))
420 		return (rv);
421 	if (sr)
422 		return (EIO);
423 	/* end of directory? */
424 	if (fat_dirbuf[0] == 0)
425 		return (ENOENT);
426 	/* deleted file? */
427 	if (fat_dirbuf[0] == 0xE5)
428 		goto getrec;
429 	/* part of long filename? */
430 	if ((fat_dirbuf[11] & 0x3F) == 0x0F)
431 		goto getrec;
432 	/* volume label or FCFS symlink? */
433 	if ((fat_dirbuf[11] & 0xC8))
434 		goto getrec;
435 	/* okay, we have a directory or regular file */
436 	cp = fat_dirbuf;
437 	rv = 0;
438 	while (*cp != 0x20) {
439 		ch = *cp++;
440 		*name++ = locase(ch);
441 		if (++rv == 8)
442 			break;
443 	}
444 	cp = fat_dirbuf + 8;
445 	if (*cp != 0x20)
446 		*name++ = '.';
447 	rv = 0;
448 	while (*cp != 0x20) {
449 		ch = *cp++;
450 		*name++ = locase(ch);
451 		if (++rv == 3)
452 			break;
453 	}
454 	*name++ = '\0';
455 
456 	return (0);
457 }
458 
459 static int
search_dir(struct open_file * f,char * name)460 search_dir(struct open_file *f, char *name)
461 {
462 	struct fat_file *ff = f->f_fsdata;
463 	int rv;
464 	char fn[14];
465 	char *buf = fat_dirbuf;
466 
467 	if ((rv = fat_readdir(f, NULL)))
468 		return (rv);
469 	while ((rv = fat_readdir(f, fn)) == 0)
470 		if (!strcmp(fn, name)) {
471 			/* found a match, follow it */
472 			ff->nodecluster = getlew(20) << 16 | getlew(26);
473 			ff->nodesize = getled(28);
474 			if (ff->nodecluster < 2 && ff->nodesize != 0)
475 				return (ENOENT);
476 			ff->nodeseekp = 0;
477 			ff->nodetype = (buf[11] & 0x10) ? 1 : 2;
478 			/* invalidate file data buffer */
479 			ff->datasec = 0;
480 			return (0);
481 		}
482 	return (ENOENT);
483 }
484