xref: /openbmc/u-boot/fs/fat/fat.c (revision b8948d2a)
1 /*
2  * fat.c
3  *
4  * R/O (V)FAT 12/16/32 filesystem implementation by Marcus Sundberg
5  *
6  * 2002-07-28 - rjones@nexus-tech.net - ported to ppcboot v1.1.6
7  * 2003-03-10 - kharris@nexus-tech.net - ported to uboot
8  *
9  * SPDX-License-Identifier:	GPL-2.0+
10  */
11 
12 #include <common.h>
13 #include <blk.h>
14 #include <config.h>
15 #include <exports.h>
16 #include <fat.h>
17 #include <asm/byteorder.h>
18 #include <part.h>
19 #include <malloc.h>
20 #include <memalign.h>
21 #include <linux/compiler.h>
22 #include <linux/ctype.h>
23 
24 #ifdef CONFIG_SUPPORT_VFAT
25 static const int vfat_enabled = 1;
26 #else
27 static const int vfat_enabled = 0;
28 #endif
29 
30 /*
31  * Convert a string to lowercase.
32  */
33 static void downcase(char *str)
34 {
35 	while (*str != '\0') {
36 		*str = tolower(*str);
37 		str++;
38 	}
39 }
40 
41 static struct blk_desc *cur_dev;
42 static disk_partition_t cur_part_info;
43 
44 #define DOS_BOOT_MAGIC_OFFSET	0x1fe
45 #define DOS_FS_TYPE_OFFSET	0x36
46 #define DOS_FS32_TYPE_OFFSET	0x52
47 
48 static int disk_read(__u32 block, __u32 nr_blocks, void *buf)
49 {
50 	ulong ret;
51 
52 	if (!cur_dev)
53 		return -1;
54 
55 	ret = blk_dread(cur_dev, cur_part_info.start + block, nr_blocks, buf);
56 
57 	if (nr_blocks && ret == 0)
58 		return -1;
59 
60 	return ret;
61 }
62 
63 int fat_set_blk_dev(struct blk_desc *dev_desc, disk_partition_t *info)
64 {
65 	ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz);
66 
67 	cur_dev = dev_desc;
68 	cur_part_info = *info;
69 
70 	/* Make sure it has a valid FAT header */
71 	if (disk_read(0, 1, buffer) != 1) {
72 		cur_dev = NULL;
73 		return -1;
74 	}
75 
76 	/* Check if it's actually a DOS volume */
77 	if (memcmp(buffer + DOS_BOOT_MAGIC_OFFSET, "\x55\xAA", 2)) {
78 		cur_dev = NULL;
79 		return -1;
80 	}
81 
82 	/* Check for FAT12/FAT16/FAT32 filesystem */
83 	if (!memcmp(buffer + DOS_FS_TYPE_OFFSET, "FAT", 3))
84 		return 0;
85 	if (!memcmp(buffer + DOS_FS32_TYPE_OFFSET, "FAT32", 5))
86 		return 0;
87 
88 	cur_dev = NULL;
89 	return -1;
90 }
91 
92 int fat_register_device(struct blk_desc *dev_desc, int part_no)
93 {
94 	disk_partition_t info;
95 
96 	/* First close any currently found FAT filesystem */
97 	cur_dev = NULL;
98 
99 	/* Read the partition table, if present */
100 	if (part_get_info(dev_desc, part_no, &info)) {
101 		if (part_no != 0) {
102 			printf("** Partition %d not valid on device %d **\n",
103 					part_no, dev_desc->devnum);
104 			return -1;
105 		}
106 
107 		info.start = 0;
108 		info.size = dev_desc->lba;
109 		info.blksz = dev_desc->blksz;
110 		info.name[0] = 0;
111 		info.type[0] = 0;
112 		info.bootable = 0;
113 #ifdef CONFIG_PARTITION_UUIDS
114 		info.uuid[0] = 0;
115 #endif
116 	}
117 
118 	return fat_set_blk_dev(dev_desc, &info);
119 }
120 
121 /*
122  * Get the first occurence of a directory delimiter ('/' or '\') in a string.
123  * Return index into string if found, -1 otherwise.
124  */
125 static int dirdelim(char *str)
126 {
127 	char *start = str;
128 
129 	while (*str != '\0') {
130 		if (ISDIRDELIM(*str))
131 			return str - start;
132 		str++;
133 	}
134 	return -1;
135 }
136 
137 /*
138  * Extract zero terminated short name from a directory entry.
139  */
140 static void get_name(dir_entry *dirent, char *s_name)
141 {
142 	char *ptr;
143 
144 	memcpy(s_name, dirent->name, 8);
145 	s_name[8] = '\0';
146 	ptr = s_name;
147 	while (*ptr && *ptr != ' ')
148 		ptr++;
149 	if (dirent->ext[0] && dirent->ext[0] != ' ') {
150 		*ptr = '.';
151 		ptr++;
152 		memcpy(ptr, dirent->ext, 3);
153 		ptr[3] = '\0';
154 		while (*ptr && *ptr != ' ')
155 			ptr++;
156 	}
157 	*ptr = '\0';
158 	if (*s_name == DELETED_FLAG)
159 		*s_name = '\0';
160 	else if (*s_name == aRING)
161 		*s_name = DELETED_FLAG;
162 	downcase(s_name);
163 }
164 
165 static int flush_dirty_fat_buffer(fsdata *mydata);
166 #if !defined(CONFIG_FAT_WRITE)
167 /* Stub for read only operation */
168 int flush_dirty_fat_buffer(fsdata *mydata)
169 {
170 	(void)(mydata);
171 	return 0;
172 }
173 #endif
174 
175 /*
176  * Get the entry at index 'entry' in a FAT (12/16/32) table.
177  * On failure 0x00 is returned.
178  */
179 static __u32 get_fatent(fsdata *mydata, __u32 entry)
180 {
181 	__u32 bufnum;
182 	__u32 off16, offset;
183 	__u32 ret = 0x00;
184 	__u16 val1, val2;
185 
186 	if (CHECK_CLUST(entry, mydata->fatsize)) {
187 		printf("Error: Invalid FAT entry: 0x%08x\n", entry);
188 		return ret;
189 	}
190 
191 	switch (mydata->fatsize) {
192 	case 32:
193 		bufnum = entry / FAT32BUFSIZE;
194 		offset = entry - bufnum * FAT32BUFSIZE;
195 		break;
196 	case 16:
197 		bufnum = entry / FAT16BUFSIZE;
198 		offset = entry - bufnum * FAT16BUFSIZE;
199 		break;
200 	case 12:
201 		bufnum = entry / FAT12BUFSIZE;
202 		offset = entry - bufnum * FAT12BUFSIZE;
203 		break;
204 
205 	default:
206 		/* Unsupported FAT size */
207 		return ret;
208 	}
209 
210 	debug("FAT%d: entry: 0x%08x = %d, offset: 0x%04x = %d\n",
211 	       mydata->fatsize, entry, entry, offset, offset);
212 
213 	/* Read a new block of FAT entries into the cache. */
214 	if (bufnum != mydata->fatbufnum) {
215 		__u32 getsize = FATBUFBLOCKS;
216 		__u8 *bufptr = mydata->fatbuf;
217 		__u32 fatlength = mydata->fatlength;
218 		__u32 startblock = bufnum * FATBUFBLOCKS;
219 
220 		/* Cap length if fatlength is not a multiple of FATBUFBLOCKS */
221 		if (startblock + getsize > fatlength)
222 			getsize = fatlength - startblock;
223 
224 		startblock += mydata->fat_sect;	/* Offset from start of disk */
225 
226 		/* Write back the fatbuf to the disk */
227 		if (flush_dirty_fat_buffer(mydata) < 0)
228 			return -1;
229 
230 		if (disk_read(startblock, getsize, bufptr) < 0) {
231 			debug("Error reading FAT blocks\n");
232 			return ret;
233 		}
234 		mydata->fatbufnum = bufnum;
235 	}
236 
237 	/* Get the actual entry from the table */
238 	switch (mydata->fatsize) {
239 	case 32:
240 		ret = FAT2CPU32(((__u32 *) mydata->fatbuf)[offset]);
241 		break;
242 	case 16:
243 		ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[offset]);
244 		break;
245 	case 12:
246 		off16 = (offset * 3) / 4;
247 
248 		switch (offset & 0x3) {
249 		case 0:
250 			ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[off16]);
251 			ret &= 0xfff;
252 			break;
253 		case 1:
254 			val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
255 			val1 &= 0xf000;
256 			val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]);
257 			val2 &= 0x00ff;
258 			ret = (val2 << 4) | (val1 >> 12);
259 			break;
260 		case 2:
261 			val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
262 			val1 &= 0xff00;
263 			val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]);
264 			val2 &= 0x000f;
265 			ret = (val2 << 8) | (val1 >> 8);
266 			break;
267 		case 3:
268 			ret = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
269 			ret = (ret & 0xfff0) >> 4;
270 			break;
271 		default:
272 			break;
273 		}
274 		break;
275 	}
276 	debug("FAT%d: ret: 0x%08x, entry: 0x%08x, offset: 0x%04x\n",
277 	       mydata->fatsize, ret, entry, offset);
278 
279 	return ret;
280 }
281 
282 /*
283  * Read at most 'size' bytes from the specified cluster into 'buffer'.
284  * Return 0 on success, -1 otherwise.
285  */
286 static int
287 get_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size)
288 {
289 	__u32 idx = 0;
290 	__u32 startsect;
291 	int ret;
292 
293 	if (clustnum > 0) {
294 		startsect = mydata->data_begin +
295 				clustnum * mydata->clust_size;
296 	} else {
297 		startsect = mydata->rootdir_sect;
298 	}
299 
300 	debug("gc - clustnum: %d, startsect: %d\n", clustnum, startsect);
301 
302 	if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
303 		ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
304 
305 		printf("FAT: Misaligned buffer address (%p)\n", buffer);
306 
307 		while (size >= mydata->sect_size) {
308 			ret = disk_read(startsect++, 1, tmpbuf);
309 			if (ret != 1) {
310 				debug("Error reading data (got %d)\n", ret);
311 				return -1;
312 			}
313 
314 			memcpy(buffer, tmpbuf, mydata->sect_size);
315 			buffer += mydata->sect_size;
316 			size -= mydata->sect_size;
317 		}
318 	} else {
319 		idx = size / mydata->sect_size;
320 		ret = disk_read(startsect, idx, buffer);
321 		if (ret != idx) {
322 			debug("Error reading data (got %d)\n", ret);
323 			return -1;
324 		}
325 		startsect += idx;
326 		idx *= mydata->sect_size;
327 		buffer += idx;
328 		size -= idx;
329 	}
330 	if (size) {
331 		ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
332 
333 		ret = disk_read(startsect, 1, tmpbuf);
334 		if (ret != 1) {
335 			debug("Error reading data (got %d)\n", ret);
336 			return -1;
337 		}
338 
339 		memcpy(buffer, tmpbuf, size);
340 	}
341 
342 	return 0;
343 }
344 
345 /*
346  * Read at most 'maxsize' bytes from 'pos' in the file associated with 'dentptr'
347  * into 'buffer'.
348  * Update the number of bytes read in *gotsize or return -1 on fatal errors.
349  */
350 __u8 get_contents_vfatname_block[MAX_CLUSTSIZE]
351 	__aligned(ARCH_DMA_MINALIGN);
352 
353 static int get_contents(fsdata *mydata, dir_entry *dentptr, loff_t pos,
354 			__u8 *buffer, loff_t maxsize, loff_t *gotsize)
355 {
356 	loff_t filesize = FAT2CPU32(dentptr->size);
357 	unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
358 	__u32 curclust = START(dentptr);
359 	__u32 endclust, newclust;
360 	loff_t actsize;
361 
362 	*gotsize = 0;
363 	debug("Filesize: %llu bytes\n", filesize);
364 
365 	if (pos >= filesize) {
366 		debug("Read position past EOF: %llu\n", pos);
367 		return 0;
368 	}
369 
370 	if (maxsize > 0 && filesize > pos + maxsize)
371 		filesize = pos + maxsize;
372 
373 	debug("%llu bytes\n", filesize);
374 
375 	actsize = bytesperclust;
376 
377 	/* go to cluster at pos */
378 	while (actsize <= pos) {
379 		curclust = get_fatent(mydata, curclust);
380 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
381 			debug("curclust: 0x%x\n", curclust);
382 			debug("Invalid FAT entry\n");
383 			return 0;
384 		}
385 		actsize += bytesperclust;
386 	}
387 
388 	/* actsize > pos */
389 	actsize -= bytesperclust;
390 	filesize -= actsize;
391 	pos -= actsize;
392 
393 	/* align to beginning of next cluster if any */
394 	if (pos) {
395 		actsize = min(filesize, (loff_t)bytesperclust);
396 		if (get_cluster(mydata, curclust, get_contents_vfatname_block,
397 				(int)actsize) != 0) {
398 			printf("Error reading cluster\n");
399 			return -1;
400 		}
401 		filesize -= actsize;
402 		actsize -= pos;
403 		memcpy(buffer, get_contents_vfatname_block + pos, actsize);
404 		*gotsize += actsize;
405 		if (!filesize)
406 			return 0;
407 		buffer += actsize;
408 
409 		curclust = get_fatent(mydata, curclust);
410 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
411 			debug("curclust: 0x%x\n", curclust);
412 			debug("Invalid FAT entry\n");
413 			return 0;
414 		}
415 	}
416 
417 	actsize = bytesperclust;
418 	endclust = curclust;
419 
420 	do {
421 		/* search for consecutive clusters */
422 		while (actsize < filesize) {
423 			newclust = get_fatent(mydata, endclust);
424 			if ((newclust - 1) != endclust)
425 				goto getit;
426 			if (CHECK_CLUST(newclust, mydata->fatsize)) {
427 				debug("curclust: 0x%x\n", newclust);
428 				debug("Invalid FAT entry\n");
429 				return 0;
430 			}
431 			endclust = newclust;
432 			actsize += bytesperclust;
433 		}
434 
435 		/* get remaining bytes */
436 		actsize = filesize;
437 		if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
438 			printf("Error reading cluster\n");
439 			return -1;
440 		}
441 		*gotsize += actsize;
442 		return 0;
443 getit:
444 		if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
445 			printf("Error reading cluster\n");
446 			return -1;
447 		}
448 		*gotsize += (int)actsize;
449 		filesize -= actsize;
450 		buffer += actsize;
451 
452 		curclust = get_fatent(mydata, endclust);
453 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
454 			debug("curclust: 0x%x\n", curclust);
455 			printf("Invalid FAT entry\n");
456 			return 0;
457 		}
458 		actsize = bytesperclust;
459 		endclust = curclust;
460 	} while (1);
461 }
462 
463 /*
464  * Extract the file name information from 'slotptr' into 'l_name',
465  * starting at l_name[*idx].
466  * Return 1 if terminator (zero byte) is found, 0 otherwise.
467  */
468 static int slot2str(dir_slot *slotptr, char *l_name, int *idx)
469 {
470 	int j;
471 
472 	for (j = 0; j <= 8; j += 2) {
473 		l_name[*idx] = slotptr->name0_4[j];
474 		if (l_name[*idx] == 0x00)
475 			return 1;
476 		(*idx)++;
477 	}
478 	for (j = 0; j <= 10; j += 2) {
479 		l_name[*idx] = slotptr->name5_10[j];
480 		if (l_name[*idx] == 0x00)
481 			return 1;
482 		(*idx)++;
483 	}
484 	for (j = 0; j <= 2; j += 2) {
485 		l_name[*idx] = slotptr->name11_12[j];
486 		if (l_name[*idx] == 0x00)
487 			return 1;
488 		(*idx)++;
489 	}
490 
491 	return 0;
492 }
493 
494 /*
495  * Extract the full long filename starting at 'retdent' (which is really
496  * a slot) into 'l_name'. If successful also copy the real directory entry
497  * into 'retdent'
498  * Return 0 on success, -1 otherwise.
499  */
500 static int
501 get_vfatname(fsdata *mydata, int curclust, __u8 *cluster,
502 	     dir_entry *retdent, char *l_name)
503 {
504 	dir_entry *realdent;
505 	dir_slot *slotptr = (dir_slot *)retdent;
506 	__u8 *buflimit = cluster + mydata->sect_size * ((curclust == 0) ?
507 							PREFETCH_BLOCKS :
508 							mydata->clust_size);
509 	__u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff;
510 	int idx = 0;
511 
512 	if (counter > VFAT_MAXSEQ) {
513 		debug("Error: VFAT name is too long\n");
514 		return -1;
515 	}
516 
517 	while ((__u8 *)slotptr < buflimit) {
518 		if (counter == 0)
519 			break;
520 		if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter)
521 			return -1;
522 		slotptr++;
523 		counter--;
524 	}
525 
526 	if ((__u8 *)slotptr >= buflimit) {
527 		dir_slot *slotptr2;
528 
529 		if (curclust == 0)
530 			return -1;
531 		curclust = get_fatent(mydata, curclust);
532 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
533 			debug("curclust: 0x%x\n", curclust);
534 			printf("Invalid FAT entry\n");
535 			return -1;
536 		}
537 
538 		if (get_cluster(mydata, curclust, get_contents_vfatname_block,
539 				mydata->clust_size * mydata->sect_size) != 0) {
540 			debug("Error: reading directory block\n");
541 			return -1;
542 		}
543 
544 		slotptr2 = (dir_slot *)get_contents_vfatname_block;
545 		while (counter > 0) {
546 			if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK)
547 			    & 0xff) != counter)
548 				return -1;
549 			slotptr2++;
550 			counter--;
551 		}
552 
553 		/* Save the real directory entry */
554 		realdent = (dir_entry *)slotptr2;
555 		while ((__u8 *)slotptr2 > get_contents_vfatname_block) {
556 			slotptr2--;
557 			slot2str(slotptr2, l_name, &idx);
558 		}
559 	} else {
560 		/* Save the real directory entry */
561 		realdent = (dir_entry *)slotptr;
562 	}
563 
564 	do {
565 		slotptr--;
566 		if (slot2str(slotptr, l_name, &idx))
567 			break;
568 	} while (!(slotptr->id & LAST_LONG_ENTRY_MASK));
569 
570 	l_name[idx] = '\0';
571 	if (*l_name == DELETED_FLAG)
572 		*l_name = '\0';
573 	else if (*l_name == aRING)
574 		*l_name = DELETED_FLAG;
575 	downcase(l_name);
576 
577 	/* Return the real directory entry */
578 	memcpy(retdent, realdent, sizeof(dir_entry));
579 
580 	return 0;
581 }
582 
583 /* Calculate short name checksum */
584 static __u8 mkcksum(const char name[8], const char ext[3])
585 {
586 	int i;
587 
588 	__u8 ret = 0;
589 
590 	for (i = 0; i < 8; i++)
591 		ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + name[i];
592 	for (i = 0; i < 3; i++)
593 		ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + ext[i];
594 
595 	return ret;
596 }
597 
598 /*
599  * Get the directory entry associated with 'filename' from the directory
600  * starting at 'startsect'
601  */
602 __u8 get_dentfromdir_block[MAX_CLUSTSIZE]
603 	__aligned(ARCH_DMA_MINALIGN);
604 
605 static dir_entry *get_dentfromdir(fsdata *mydata, int startsect,
606 				  char *filename, dir_entry *retdent,
607 				  int dols)
608 {
609 	__u16 prevcksum = 0xffff;
610 	__u32 curclust = START(retdent);
611 	int files = 0, dirs = 0;
612 
613 	debug("get_dentfromdir: %s\n", filename);
614 
615 	while (1) {
616 		dir_entry *dentptr;
617 
618 		int i;
619 
620 		if (get_cluster(mydata, curclust, get_dentfromdir_block,
621 				mydata->clust_size * mydata->sect_size) != 0) {
622 			debug("Error: reading directory block\n");
623 			return NULL;
624 		}
625 
626 		dentptr = (dir_entry *)get_dentfromdir_block;
627 
628 		for (i = 0; i < DIRENTSPERCLUST; i++) {
629 			char s_name[14], l_name[VFAT_MAXLEN_BYTES];
630 
631 			l_name[0] = '\0';
632 			if (dentptr->name[0] == DELETED_FLAG) {
633 				dentptr++;
634 				continue;
635 			}
636 			if ((dentptr->attr & ATTR_VOLUME)) {
637 				if (vfat_enabled &&
638 				    (dentptr->attr & ATTR_VFAT) == ATTR_VFAT &&
639 				    (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
640 					prevcksum = ((dir_slot *)dentptr)->alias_checksum;
641 					get_vfatname(mydata, curclust,
642 						     get_dentfromdir_block,
643 						     dentptr, l_name);
644 					if (dols) {
645 						int isdir;
646 						char dirc;
647 						int doit = 0;
648 
649 						isdir = (dentptr->attr & ATTR_DIR);
650 
651 						if (isdir) {
652 							dirs++;
653 							dirc = '/';
654 							doit = 1;
655 						} else {
656 							dirc = ' ';
657 							if (l_name[0] != 0) {
658 								files++;
659 								doit = 1;
660 							}
661 						}
662 						if (doit) {
663 							if (dirc == ' ') {
664 								printf(" %8u   %s%c\n",
665 								       FAT2CPU32(dentptr->size),
666 									l_name,
667 									dirc);
668 							} else {
669 								printf("            %s%c\n",
670 									l_name,
671 									dirc);
672 							}
673 						}
674 						dentptr++;
675 						continue;
676 					}
677 					debug("vfatname: |%s|\n", l_name);
678 				} else {
679 					/* Volume label or VFAT entry */
680 					dentptr++;
681 					continue;
682 				}
683 			}
684 			if (dentptr->name[0] == 0) {
685 				if (dols) {
686 					printf("\n%d file(s), %d dir(s)\n\n",
687 						files, dirs);
688 				}
689 				debug("Dentname == NULL - %d\n", i);
690 				return NULL;
691 			}
692 			if (vfat_enabled) {
693 				__u8 csum = mkcksum(dentptr->name, dentptr->ext);
694 				if (dols && csum == prevcksum) {
695 					prevcksum = 0xffff;
696 					dentptr++;
697 					continue;
698 				}
699 			}
700 
701 			get_name(dentptr, s_name);
702 			if (dols) {
703 				int isdir = (dentptr->attr & ATTR_DIR);
704 				char dirc;
705 				int doit = 0;
706 
707 				if (isdir) {
708 					dirs++;
709 					dirc = '/';
710 					doit = 1;
711 				} else {
712 					dirc = ' ';
713 					if (s_name[0] != 0) {
714 						files++;
715 						doit = 1;
716 					}
717 				}
718 
719 				if (doit) {
720 					if (dirc == ' ') {
721 						printf(" %8u   %s%c\n",
722 						       FAT2CPU32(dentptr->size),
723 							s_name, dirc);
724 					} else {
725 						printf("            %s%c\n",
726 							s_name, dirc);
727 					}
728 				}
729 
730 				dentptr++;
731 				continue;
732 			}
733 
734 			if (strcmp(filename, s_name)
735 			    && strcmp(filename, l_name)) {
736 				debug("Mismatch: |%s|%s|\n", s_name, l_name);
737 				dentptr++;
738 				continue;
739 			}
740 
741 			memcpy(retdent, dentptr, sizeof(dir_entry));
742 
743 			debug("DentName: %s", s_name);
744 			debug(", start: 0x%x", START(dentptr));
745 			debug(", size:  0x%x %s\n",
746 			      FAT2CPU32(dentptr->size),
747 			      (dentptr->attr & ATTR_DIR) ? "(DIR)" : "");
748 
749 			return retdent;
750 		}
751 
752 		curclust = get_fatent(mydata, curclust);
753 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
754 			debug("curclust: 0x%x\n", curclust);
755 			printf("Invalid FAT entry\n");
756 			return NULL;
757 		}
758 	}
759 
760 	return NULL;
761 }
762 
763 /*
764  * Read boot sector and volume info from a FAT filesystem
765  */
766 static int
767 read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize)
768 {
769 	__u8 *block;
770 	volume_info *vistart;
771 	int ret = 0;
772 
773 	if (cur_dev == NULL) {
774 		debug("Error: no device selected\n");
775 		return -1;
776 	}
777 
778 	block = memalign(ARCH_DMA_MINALIGN, cur_dev->blksz);
779 	if (block == NULL) {
780 		debug("Error: allocating block\n");
781 		return -1;
782 	}
783 
784 	if (disk_read(0, 1, block) < 0) {
785 		debug("Error: reading block\n");
786 		goto fail;
787 	}
788 
789 	memcpy(bs, block, sizeof(boot_sector));
790 	bs->reserved = FAT2CPU16(bs->reserved);
791 	bs->fat_length = FAT2CPU16(bs->fat_length);
792 	bs->secs_track = FAT2CPU16(bs->secs_track);
793 	bs->heads = FAT2CPU16(bs->heads);
794 	bs->total_sect = FAT2CPU32(bs->total_sect);
795 
796 	/* FAT32 entries */
797 	if (bs->fat_length == 0) {
798 		/* Assume FAT32 */
799 		bs->fat32_length = FAT2CPU32(bs->fat32_length);
800 		bs->flags = FAT2CPU16(bs->flags);
801 		bs->root_cluster = FAT2CPU32(bs->root_cluster);
802 		bs->info_sector = FAT2CPU16(bs->info_sector);
803 		bs->backup_boot = FAT2CPU16(bs->backup_boot);
804 		vistart = (volume_info *)(block + sizeof(boot_sector));
805 		*fatsize = 32;
806 	} else {
807 		vistart = (volume_info *)&(bs->fat32_length);
808 		*fatsize = 0;
809 	}
810 	memcpy(volinfo, vistart, sizeof(volume_info));
811 
812 	if (*fatsize == 32) {
813 		if (strncmp(FAT32_SIGN, vistart->fs_type, SIGNLEN) == 0)
814 			goto exit;
815 	} else {
816 		if (strncmp(FAT12_SIGN, vistart->fs_type, SIGNLEN) == 0) {
817 			*fatsize = 12;
818 			goto exit;
819 		}
820 		if (strncmp(FAT16_SIGN, vistart->fs_type, SIGNLEN) == 0) {
821 			*fatsize = 16;
822 			goto exit;
823 		}
824 	}
825 
826 	debug("Error: broken fs_type sign\n");
827 fail:
828 	ret = -1;
829 exit:
830 	free(block);
831 	return ret;
832 }
833 
834 __u8 do_fat_read_at_block[MAX_CLUSTSIZE]
835 	__aligned(ARCH_DMA_MINALIGN);
836 
837 int do_fat_read_at(const char *filename, loff_t pos, void *buffer,
838 		   loff_t maxsize, int dols, int dogetsize, loff_t *size)
839 {
840 	char fnamecopy[2048];
841 	boot_sector bs;
842 	volume_info volinfo;
843 	fsdata datablock;
844 	fsdata *mydata = &datablock;
845 	dir_entry *dentptr = NULL;
846 	__u16 prevcksum = 0xffff;
847 	char *subname = "";
848 	__u32 cursect;
849 	int idx, isdir = 0;
850 	int files = 0, dirs = 0;
851 	int ret = -1;
852 	int firsttime;
853 	__u32 root_cluster = 0;
854 	__u32 read_blk;
855 	int rootdir_size = 0;
856 	int buffer_blk_cnt;
857 	int do_read;
858 	__u8 *dir_ptr;
859 
860 	if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) {
861 		debug("Error: reading boot sector\n");
862 		return -1;
863 	}
864 
865 	if (mydata->fatsize == 32) {
866 		root_cluster = bs.root_cluster;
867 		mydata->fatlength = bs.fat32_length;
868 	} else {
869 		mydata->fatlength = bs.fat_length;
870 	}
871 
872 	mydata->fat_sect = bs.reserved;
873 
874 	cursect = mydata->rootdir_sect
875 		= mydata->fat_sect + mydata->fatlength * bs.fats;
876 
877 	mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0];
878 	mydata->clust_size = bs.cluster_size;
879 	if (mydata->sect_size != cur_part_info.blksz) {
880 		printf("Error: FAT sector size mismatch (fs=%hu, dev=%lu)\n",
881 				mydata->sect_size, cur_part_info.blksz);
882 		return -1;
883 	}
884 
885 	if (mydata->fatsize == 32) {
886 		mydata->data_begin = mydata->rootdir_sect -
887 					(mydata->clust_size * 2);
888 	} else {
889 		rootdir_size = ((bs.dir_entries[1]  * (int)256 +
890 				 bs.dir_entries[0]) *
891 				 sizeof(dir_entry)) /
892 				 mydata->sect_size;
893 		mydata->data_begin = mydata->rootdir_sect +
894 					rootdir_size -
895 					(mydata->clust_size * 2);
896 	}
897 
898 	mydata->fatbufnum = -1;
899 	mydata->fat_dirty = 0;
900 	mydata->fatbuf = memalign(ARCH_DMA_MINALIGN, FATBUFSIZE);
901 	if (mydata->fatbuf == NULL) {
902 		debug("Error: allocating memory\n");
903 		return -1;
904 	}
905 
906 	if (vfat_enabled)
907 		debug("VFAT Support enabled\n");
908 
909 	debug("FAT%d, fat_sect: %d, fatlength: %d\n",
910 	       mydata->fatsize, mydata->fat_sect, mydata->fatlength);
911 	debug("Rootdir begins at cluster: %d, sector: %d, offset: %x\n"
912 	       "Data begins at: %d\n",
913 	       root_cluster,
914 	       mydata->rootdir_sect,
915 	       mydata->rootdir_sect * mydata->sect_size, mydata->data_begin);
916 	debug("Sector size: %d, cluster size: %d\n", mydata->sect_size,
917 	      mydata->clust_size);
918 
919 	/* "cwd" is always the root... */
920 	while (ISDIRDELIM(*filename))
921 		filename++;
922 
923 	/* Make a copy of the filename and convert it to lowercase */
924 	strcpy(fnamecopy, filename);
925 	downcase(fnamecopy);
926 
927 root_reparse:
928 	if (*fnamecopy == '\0') {
929 		if (!dols)
930 			goto exit;
931 
932 		dols = LS_ROOT;
933 	} else if ((idx = dirdelim(fnamecopy)) >= 0) {
934 		isdir = 1;
935 		fnamecopy[idx] = '\0';
936 		subname = fnamecopy + idx + 1;
937 
938 		/* Handle multiple delimiters */
939 		while (ISDIRDELIM(*subname))
940 			subname++;
941 	} else if (dols) {
942 		isdir = 1;
943 	}
944 
945 	buffer_blk_cnt = 0;
946 	firsttime = 1;
947 	while (1) {
948 		int i;
949 
950 		if (mydata->fatsize == 32 || firsttime) {
951 			dir_ptr = do_fat_read_at_block;
952 			firsttime = 0;
953 		} else {
954 			/**
955 			 * FAT16 sector buffer modification:
956 			 * Each loop, the second buffered block is moved to
957 			 * the buffer begin, and two next sectors are read
958 			 * next to the previously moved one. So the sector
959 			 * buffer keeps always 3 sectors for fat16.
960 			 * And the current sector is the buffer second sector
961 			 * beside the "firsttime" read, when it is the first one.
962 			 *
963 			 * PREFETCH_BLOCKS is 2 for FAT16 == loop[0:1]
964 			 * n = computed root dir sector
965 			 * loop |  cursect-1  | cursect    | cursect+1  |
966 			 *   0  |  sector n+0 | sector n+1 | none       |
967 			 *   1  |  none       | sector n+0 | sector n+1 |
968 			 *   0  |  sector n+1 | sector n+2 | sector n+3 |
969 			 *   1  |  sector n+3 | ...
970 			*/
971 			dir_ptr = (do_fat_read_at_block + mydata->sect_size);
972 			memcpy(do_fat_read_at_block, dir_ptr, mydata->sect_size);
973 		}
974 
975 		do_read = 1;
976 
977 		if (mydata->fatsize == 32 && buffer_blk_cnt)
978 			do_read = 0;
979 
980 		if (do_read) {
981 			read_blk = (mydata->fatsize == 32) ?
982 				    mydata->clust_size : PREFETCH_BLOCKS;
983 
984 			debug("FAT read(sect=%d, cnt:%d), clust_size=%d, DIRENTSPERBLOCK=%zd\n",
985 				cursect, read_blk, mydata->clust_size, DIRENTSPERBLOCK);
986 
987 			if (disk_read(cursect, read_blk, dir_ptr) < 0) {
988 				debug("Error: reading rootdir block\n");
989 				goto exit;
990 			}
991 
992 			dentptr = (dir_entry *)dir_ptr;
993 		}
994 
995 		for (i = 0; i < DIRENTSPERBLOCK; i++) {
996 			char s_name[14], l_name[VFAT_MAXLEN_BYTES];
997 			__u8 csum;
998 
999 			l_name[0] = '\0';
1000 			if (dentptr->name[0] == DELETED_FLAG) {
1001 				dentptr++;
1002 				continue;
1003 			}
1004 
1005 			if (vfat_enabled)
1006 				csum = mkcksum(dentptr->name, dentptr->ext);
1007 
1008 			if (dentptr->attr & ATTR_VOLUME) {
1009 				if (vfat_enabled &&
1010 				    (dentptr->attr & ATTR_VFAT) == ATTR_VFAT &&
1011 				    (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
1012 					prevcksum =
1013 						((dir_slot *)dentptr)->alias_checksum;
1014 
1015 					get_vfatname(mydata,
1016 						     root_cluster,
1017 						     dir_ptr,
1018 						     dentptr, l_name);
1019 
1020 					if (dols == LS_ROOT) {
1021 						char dirc;
1022 						int doit = 0;
1023 						int isdir =
1024 							(dentptr->attr & ATTR_DIR);
1025 
1026 						if (isdir) {
1027 							dirs++;
1028 							dirc = '/';
1029 							doit = 1;
1030 						} else {
1031 							dirc = ' ';
1032 							if (l_name[0] != 0) {
1033 								files++;
1034 								doit = 1;
1035 							}
1036 						}
1037 						if (doit) {
1038 							if (dirc == ' ') {
1039 								printf(" %8u   %s%c\n",
1040 								       FAT2CPU32(dentptr->size),
1041 									l_name,
1042 									dirc);
1043 							} else {
1044 								printf("            %s%c\n",
1045 									l_name,
1046 									dirc);
1047 							}
1048 						}
1049 						dentptr++;
1050 						continue;
1051 					}
1052 					debug("Rootvfatname: |%s|\n",
1053 					       l_name);
1054 				} else {
1055 					/* Volume label or VFAT entry */
1056 					dentptr++;
1057 					continue;
1058 				}
1059 			} else if (dentptr->name[0] == 0) {
1060 				debug("RootDentname == NULL - %d\n", i);
1061 				if (dols == LS_ROOT) {
1062 					printf("\n%d file(s), %d dir(s)\n\n",
1063 						files, dirs);
1064 					ret = 0;
1065 				}
1066 				goto exit;
1067 			}
1068 			else if (vfat_enabled &&
1069 				 dols == LS_ROOT && csum == prevcksum) {
1070 				prevcksum = 0xffff;
1071 				dentptr++;
1072 				continue;
1073 			}
1074 
1075 			get_name(dentptr, s_name);
1076 
1077 			if (dols == LS_ROOT) {
1078 				int isdir = (dentptr->attr & ATTR_DIR);
1079 				char dirc;
1080 				int doit = 0;
1081 
1082 				if (isdir) {
1083 					dirc = '/';
1084 					if (s_name[0] != 0) {
1085 						dirs++;
1086 						doit = 1;
1087 					}
1088 				} else {
1089 					dirc = ' ';
1090 					if (s_name[0] != 0) {
1091 						files++;
1092 						doit = 1;
1093 					}
1094 				}
1095 				if (doit) {
1096 					if (dirc == ' ') {
1097 						printf(" %8u   %s%c\n",
1098 						       FAT2CPU32(dentptr->size),
1099 							s_name, dirc);
1100 					} else {
1101 						printf("            %s%c\n",
1102 							s_name, dirc);
1103 					}
1104 				}
1105 				dentptr++;
1106 				continue;
1107 			}
1108 
1109 			if (strcmp(fnamecopy, s_name)
1110 			    && strcmp(fnamecopy, l_name)) {
1111 				debug("RootMismatch: |%s|%s|\n", s_name,
1112 				       l_name);
1113 				dentptr++;
1114 				continue;
1115 			}
1116 
1117 			if (isdir && !(dentptr->attr & ATTR_DIR))
1118 				goto exit;
1119 
1120 			debug("RootName: %s", s_name);
1121 			debug(", start: 0x%x", START(dentptr));
1122 			debug(", size:  0x%x %s\n",
1123 			       FAT2CPU32(dentptr->size),
1124 			       isdir ? "(DIR)" : "");
1125 
1126 			goto rootdir_done;	/* We got a match */
1127 		}
1128 		debug("END LOOP: buffer_blk_cnt=%d   clust_size=%d\n", buffer_blk_cnt,
1129 		       mydata->clust_size);
1130 
1131 		/*
1132 		 * On FAT32 we must fetch the FAT entries for the next
1133 		 * root directory clusters when a cluster has been
1134 		 * completely processed.
1135 		 */
1136 		++buffer_blk_cnt;
1137 		int rootdir_end = 0;
1138 		if (mydata->fatsize == 32) {
1139 			if (buffer_blk_cnt == mydata->clust_size) {
1140 				int nxtsect = 0;
1141 				int nxt_clust = 0;
1142 
1143 				nxt_clust = get_fatent(mydata, root_cluster);
1144 				rootdir_end = CHECK_CLUST(nxt_clust, 32);
1145 
1146 				nxtsect = mydata->data_begin +
1147 					(nxt_clust * mydata->clust_size);
1148 
1149 				root_cluster = nxt_clust;
1150 
1151 				cursect = nxtsect;
1152 				buffer_blk_cnt = 0;
1153 			}
1154 		} else {
1155 			if (buffer_blk_cnt == PREFETCH_BLOCKS)
1156 				buffer_blk_cnt = 0;
1157 
1158 			rootdir_end = (++cursect - mydata->rootdir_sect >=
1159 				       rootdir_size);
1160 		}
1161 
1162 		/* If end of rootdir reached */
1163 		if (rootdir_end) {
1164 			if (dols == LS_ROOT) {
1165 				printf("\n%d file(s), %d dir(s)\n\n",
1166 				       files, dirs);
1167 				*size = 0;
1168 			}
1169 			goto exit;
1170 		}
1171 	}
1172 rootdir_done:
1173 
1174 	firsttime = 1;
1175 
1176 	while (isdir) {
1177 		int startsect = mydata->data_begin
1178 			+ START(dentptr) * mydata->clust_size;
1179 		dir_entry dent;
1180 		char *nextname = NULL;
1181 
1182 		dent = *dentptr;
1183 		dentptr = &dent;
1184 
1185 		idx = dirdelim(subname);
1186 
1187 		if (idx >= 0) {
1188 			subname[idx] = '\0';
1189 			nextname = subname + idx + 1;
1190 			/* Handle multiple delimiters */
1191 			while (ISDIRDELIM(*nextname))
1192 				nextname++;
1193 			if (dols && *nextname == '\0')
1194 				firsttime = 0;
1195 		} else {
1196 			if (dols && firsttime) {
1197 				firsttime = 0;
1198 			} else {
1199 				isdir = 0;
1200 			}
1201 		}
1202 
1203 		if (get_dentfromdir(mydata, startsect, subname, dentptr,
1204 				     isdir ? 0 : dols) == NULL) {
1205 			if (dols && !isdir)
1206 				*size = 0;
1207 			goto exit;
1208 		}
1209 
1210 		if (isdir && !(dentptr->attr & ATTR_DIR))
1211 			goto exit;
1212 
1213 		/*
1214 		 * If we are looking for a directory, and found a directory
1215 		 * type entry, and the entry is for the root directory (as
1216 		 * denoted by a cluster number of 0), jump back to the start
1217 		 * of the function, since at least on FAT12/16, the root dir
1218 		 * lives in a hard-coded location and needs special handling
1219 		 * to parse, rather than simply following the cluster linked
1220 		 * list in the FAT, like other directories.
1221 		 */
1222 		if (isdir && (dentptr->attr & ATTR_DIR) && !START(dentptr)) {
1223 			/*
1224 			 * Modify the filename to remove the prefix that gets
1225 			 * back to the root directory, so the initial root dir
1226 			 * parsing code can continue from where we are without
1227 			 * confusion.
1228 			 */
1229 			strcpy(fnamecopy, nextname ?: "");
1230 			/*
1231 			 * Set up state the same way as the function does when
1232 			 * first started. This is required for the root dir
1233 			 * parsing code operates in its expected environment.
1234 			 */
1235 			subname = "";
1236 			cursect = mydata->rootdir_sect;
1237 			isdir = 0;
1238 			goto root_reparse;
1239 		}
1240 
1241 		if (idx >= 0)
1242 			subname = nextname;
1243 	}
1244 
1245 	if (dogetsize) {
1246 		*size = FAT2CPU32(dentptr->size);
1247 		ret = 0;
1248 	} else {
1249 		ret = get_contents(mydata, dentptr, pos, buffer, maxsize, size);
1250 	}
1251 	debug("Size: %u, got: %llu\n", FAT2CPU32(dentptr->size), *size);
1252 
1253 exit:
1254 	free(mydata->fatbuf);
1255 	return ret;
1256 }
1257 
1258 int do_fat_read(const char *filename, void *buffer, loff_t maxsize, int dols,
1259 		loff_t *actread)
1260 {
1261 	return do_fat_read_at(filename, 0, buffer, maxsize, dols, 0, actread);
1262 }
1263 
1264 int file_fat_detectfs(void)
1265 {
1266 	boot_sector bs;
1267 	volume_info volinfo;
1268 	int fatsize;
1269 	char vol_label[12];
1270 
1271 	if (cur_dev == NULL) {
1272 		printf("No current device\n");
1273 		return 1;
1274 	}
1275 
1276 #if defined(CONFIG_CMD_IDE) || \
1277     defined(CONFIG_CMD_SATA) || \
1278     defined(CONFIG_SCSI) || \
1279     defined(CONFIG_CMD_USB) || \
1280     defined(CONFIG_MMC)
1281 	printf("Interface:  ");
1282 	switch (cur_dev->if_type) {
1283 	case IF_TYPE_IDE:
1284 		printf("IDE");
1285 		break;
1286 	case IF_TYPE_SATA:
1287 		printf("SATA");
1288 		break;
1289 	case IF_TYPE_SCSI:
1290 		printf("SCSI");
1291 		break;
1292 	case IF_TYPE_ATAPI:
1293 		printf("ATAPI");
1294 		break;
1295 	case IF_TYPE_USB:
1296 		printf("USB");
1297 		break;
1298 	case IF_TYPE_DOC:
1299 		printf("DOC");
1300 		break;
1301 	case IF_TYPE_MMC:
1302 		printf("MMC");
1303 		break;
1304 	default:
1305 		printf("Unknown");
1306 	}
1307 
1308 	printf("\n  Device %d: ", cur_dev->devnum);
1309 	dev_print(cur_dev);
1310 #endif
1311 
1312 	if (read_bootsectandvi(&bs, &volinfo, &fatsize)) {
1313 		printf("\nNo valid FAT fs found\n");
1314 		return 1;
1315 	}
1316 
1317 	memcpy(vol_label, volinfo.volume_label, 11);
1318 	vol_label[11] = '\0';
1319 	volinfo.fs_type[5] = '\0';
1320 
1321 	printf("Filesystem: %s \"%s\"\n", volinfo.fs_type, vol_label);
1322 
1323 	return 0;
1324 }
1325 
1326 int file_fat_ls(const char *dir)
1327 {
1328 	loff_t size;
1329 
1330 	return do_fat_read(dir, NULL, 0, LS_YES, &size);
1331 }
1332 
1333 int fat_exists(const char *filename)
1334 {
1335 	int ret;
1336 	loff_t size;
1337 
1338 	ret = do_fat_read_at(filename, 0, NULL, 0, LS_NO, 1, &size);
1339 	return ret == 0;
1340 }
1341 
1342 int fat_size(const char *filename, loff_t *size)
1343 {
1344 	return do_fat_read_at(filename, 0, NULL, 0, LS_NO, 1, size);
1345 }
1346 
1347 int file_fat_read_at(const char *filename, loff_t pos, void *buffer,
1348 		     loff_t maxsize, loff_t *actread)
1349 {
1350 	printf("reading %s\n", filename);
1351 	return do_fat_read_at(filename, pos, buffer, maxsize, LS_NO, 0,
1352 			      actread);
1353 }
1354 
1355 int file_fat_read(const char *filename, void *buffer, int maxsize)
1356 {
1357 	loff_t actread;
1358 	int ret;
1359 
1360 	ret =  file_fat_read_at(filename, 0, buffer, maxsize, &actread);
1361 	if (ret)
1362 		return ret;
1363 	else
1364 		return actread;
1365 }
1366 
1367 int fat_read_file(const char *filename, void *buf, loff_t offset, loff_t len,
1368 		  loff_t *actread)
1369 {
1370 	int ret;
1371 
1372 	ret = file_fat_read_at(filename, offset, buf, len, actread);
1373 	if (ret)
1374 		printf("** Unable to read file %s **\n", filename);
1375 
1376 	return ret;
1377 }
1378 
1379 void fat_close(void)
1380 {
1381 }
1382