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