xref: /openbmc/u-boot/fs/fat/fat.c (revision 62a05573)
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  * See file CREDITS for list of people who contributed to this
10  * project.
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License as
14  * published by the Free Software Foundation; either version 2 of
15  * the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
25  * MA 02111-1307 USA
26  */
27 
28 #include <common.h>
29 #include <config.h>
30 #include <fat.h>
31 #include <asm/byteorder.h>
32 #include <part.h>
33 
34 /*
35  * Convert a string to lowercase.
36  */
37 static void downcase (char *str)
38 {
39 	while (*str != '\0') {
40 		TOLOWER(*str);
41 		str++;
42 	}
43 }
44 
45 static block_dev_desc_t *cur_dev = NULL;
46 
47 static unsigned long part_offset = 0;
48 
49 static int cur_part = 1;
50 
51 #define DOS_PART_TBL_OFFSET	0x1be
52 #define DOS_PART_MAGIC_OFFSET	0x1fe
53 #define DOS_FS_TYPE_OFFSET	0x36
54 #define DOS_FS32_TYPE_OFFSET	0x52
55 
56 static int disk_read (__u32 startblock, __u32 getsize, __u8 * bufptr)
57 {
58 	if (cur_dev == NULL)
59 		return -1;
60 
61 	startblock += part_offset;
62 
63 	if (cur_dev->block_read) {
64 		return cur_dev->block_read(cur_dev->dev, startblock, getsize,
65 					   (unsigned long *) bufptr);
66 	}
67 	return -1;
68 }
69 
70 int fat_register_device (block_dev_desc_t * dev_desc, int part_no)
71 {
72 	unsigned char buffer[SECTOR_SIZE];
73 
74 	disk_partition_t info;
75 
76 	if (!dev_desc->block_read)
77 		return -1;
78 
79 	cur_dev = dev_desc;
80 	/* check if we have a MBR (on floppies we have only a PBR) */
81 	if (dev_desc->block_read(dev_desc->dev, 0, 1, (ulong *)buffer) != 1) {
82 		printf("** Can't read from device %d **\n",
83 			dev_desc->dev);
84 		return -1;
85 	}
86 	if (buffer[DOS_PART_MAGIC_OFFSET] != 0x55 ||
87 	    buffer[DOS_PART_MAGIC_OFFSET + 1] != 0xaa) {
88 		/* no signature found */
89 		return -1;
90 	}
91 #if (defined(CONFIG_CMD_IDE) || \
92      defined(CONFIG_CMD_MG_DISK) || \
93      defined(CONFIG_CMD_SATA) || \
94      defined(CONFIG_CMD_SCSI) || \
95      defined(CONFIG_CMD_USB) || \
96      defined(CONFIG_MMC) || \
97      defined(CONFIG_SYSTEMACE) )
98 	/* First we assume there is a MBR */
99 	if (!get_partition_info(dev_desc, part_no, &info)) {
100 		part_offset = info.start;
101 		cur_part = part_no;
102 	} else if ((strncmp((char *)&buffer[DOS_FS_TYPE_OFFSET], "FAT", 3) == 0) ||
103 		   (strncmp((char *)&buffer[DOS_FS32_TYPE_OFFSET], "FAT32", 5) == 0)) {
104 		/* ok, we assume we are on a PBR only */
105 		cur_part = 1;
106 		part_offset = 0;
107 	} else {
108 		printf("** Partition %d not valid on device %d **\n",
109 			part_no, dev_desc->dev);
110 		return -1;
111 	}
112 
113 #else
114 	if ((strncmp((char *)&buffer[DOS_FS_TYPE_OFFSET], "FAT", 3) == 0) ||
115 	    (strncmp((char *)&buffer[DOS_FS32_TYPE_OFFSET], "FAT32", 5) == 0)) {
116 		/* ok, we assume we are on a PBR only */
117 		cur_part = 1;
118 		part_offset = 0;
119 		info.start = part_offset;
120 	} else {
121 		/* FIXME we need to determine the start block of the
122 		 * partition where the DOS FS resides. This can be done
123 		 * by using the get_partition_info routine. For this
124 		 * purpose the libpart must be included.
125 		 */
126 		part_offset = 32;
127 		cur_part = 1;
128 	}
129 #endif
130 	return 0;
131 }
132 
133 /*
134  * Get the first occurence of a directory delimiter ('/' or '\') in a string.
135  * Return index into string if found, -1 otherwise.
136  */
137 static int dirdelim (char *str)
138 {
139 	char *start = str;
140 
141 	while (*str != '\0') {
142 		if (ISDIRDELIM(*str))
143 			return str - start;
144 		str++;
145 	}
146 	return -1;
147 }
148 
149 /*
150  * Extract zero terminated short name from a directory entry.
151  */
152 static void get_name (dir_entry *dirent, char *s_name)
153 {
154 	char *ptr;
155 
156 	memcpy(s_name, dirent->name, 8);
157 	s_name[8] = '\0';
158 	ptr = s_name;
159 	while (*ptr && *ptr != ' ')
160 		ptr++;
161 	if (dirent->ext[0] && dirent->ext[0] != ' ') {
162 		*ptr = '.';
163 		ptr++;
164 		memcpy(ptr, dirent->ext, 3);
165 		ptr[3] = '\0';
166 		while (*ptr && *ptr != ' ')
167 			ptr++;
168 	}
169 	*ptr = '\0';
170 	if (*s_name == DELETED_FLAG)
171 		*s_name = '\0';
172 	else if (*s_name == aRING)
173 		*s_name = DELETED_FLAG;
174 	downcase(s_name);
175 }
176 
177 /*
178  * Get the entry at index 'entry' in a FAT (12/16/32) table.
179  * On failure 0x00 is returned.
180  */
181 static __u32 get_fatent (fsdata *mydata, __u32 entry)
182 {
183 	__u32 bufnum;
184 	__u32 off16, offset;
185 	__u32 ret = 0x00;
186 	__u16 val1, val2;
187 
188 	switch (mydata->fatsize) {
189 	case 32:
190 		bufnum = entry / FAT32BUFSIZE;
191 		offset = entry - bufnum * FAT32BUFSIZE;
192 		break;
193 	case 16:
194 		bufnum = entry / FAT16BUFSIZE;
195 		offset = entry - bufnum * FAT16BUFSIZE;
196 		break;
197 	case 12:
198 		bufnum = entry / FAT12BUFSIZE;
199 		offset = entry - bufnum * FAT12BUFSIZE;
200 		break;
201 
202 	default:
203 		/* Unsupported FAT size */
204 		return ret;
205 	}
206 
207 	debug("FAT%d: entry: 0x%04x = %d, offset: 0x%04x = %d\n",
208 	       mydata->fatsize, entry, entry, offset, offset);
209 
210 	/* Read a new block of FAT entries into the cache. */
211 	if (bufnum != mydata->fatbufnum) {
212 		int getsize = FATBUFSIZE / FS_BLOCK_SIZE;
213 		__u8 *bufptr = mydata->fatbuf;
214 		__u32 fatlength = mydata->fatlength;
215 		__u32 startblock = bufnum * FATBUFBLOCKS;
216 
217 		fatlength *= SECTOR_SIZE;	/* We want it in bytes now */
218 		startblock += mydata->fat_sect;	/* Offset from start of disk */
219 
220 		if (getsize > fatlength)
221 			getsize = fatlength;
222 		if (disk_read(startblock, getsize, bufptr) < 0) {
223 			debug("Error reading FAT blocks\n");
224 			return ret;
225 		}
226 		mydata->fatbufnum = bufnum;
227 	}
228 
229 	/* Get the actual entry from the table */
230 	switch (mydata->fatsize) {
231 	case 32:
232 		ret = FAT2CPU32(((__u32 *) mydata->fatbuf)[offset]);
233 		break;
234 	case 16:
235 		ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[offset]);
236 		break;
237 	case 12:
238 		off16 = (offset * 3) / 4;
239 
240 		switch (offset & 0x3) {
241 		case 0:
242 			ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[off16]);
243 			ret &= 0xfff;
244 			break;
245 		case 1:
246 			val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
247 			val1 &= 0xf000;
248 			val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]);
249 			val2 &= 0x00ff;
250 			ret = (val2 << 4) | (val1 >> 12);
251 			break;
252 		case 2:
253 			val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
254 			val1 &= 0xff00;
255 			val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]);
256 			val2 &= 0x000f;
257 			ret = (val2 << 8) | (val1 >> 8);
258 			break;
259 		case 3:
260 			ret = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
261 			ret = (ret & 0xfff0) >> 4;
262 			break;
263 		default:
264 			break;
265 		}
266 		break;
267 	}
268 	debug("FAT%d: ret: %08x, offset: %04x\n",
269 	       mydata->fatsize, ret, offset);
270 
271 	return ret;
272 }
273 
274 /*
275  * Read at most 'size' bytes from the specified cluster into 'buffer'.
276  * Return 0 on success, -1 otherwise.
277  */
278 static int
279 get_cluster (fsdata *mydata, __u32 clustnum, __u8 *buffer,
280 	     unsigned long size)
281 {
282 	int idx = 0;
283 	__u32 startsect;
284 
285 	if (clustnum > 0) {
286 		startsect = mydata->data_begin +
287 				clustnum * mydata->clust_size;
288 	} else {
289 		startsect = mydata->rootdir_sect;
290 	}
291 
292 	debug("gc - clustnum: %d, startsect: %d\n", clustnum, startsect);
293 
294 	if (disk_read(startsect, size / FS_BLOCK_SIZE, buffer) < 0) {
295 		debug("Error reading data\n");
296 		return -1;
297 	}
298 	if (size % FS_BLOCK_SIZE) {
299 		__u8 tmpbuf[FS_BLOCK_SIZE];
300 
301 		idx = size / FS_BLOCK_SIZE;
302 		if (disk_read(startsect + idx, 1, tmpbuf) < 0) {
303 			debug("Error reading data\n");
304 			return -1;
305 		}
306 		buffer += idx * FS_BLOCK_SIZE;
307 
308 		memcpy(buffer, tmpbuf, size % FS_BLOCK_SIZE);
309 		return 0;
310 	}
311 
312 	return 0;
313 }
314 
315 /*
316  * Read at most 'maxsize' bytes from the file associated with 'dentptr'
317  * into 'buffer'.
318  * Return the number of bytes read or -1 on fatal errors.
319  */
320 static long
321 get_contents (fsdata *mydata, dir_entry *dentptr, __u8 *buffer,
322 	      unsigned long maxsize)
323 {
324 	unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0;
325 	unsigned int bytesperclust = mydata->clust_size * SECTOR_SIZE;
326 	__u32 curclust = START(dentptr);
327 	__u32 endclust, newclust;
328 	unsigned long actsize;
329 
330 	debug("Filesize: %ld bytes\n", filesize);
331 
332 	if (maxsize > 0 && filesize > maxsize)
333 		filesize = maxsize;
334 
335 	debug("%ld bytes\n", filesize);
336 
337 	actsize = bytesperclust;
338 	endclust = curclust;
339 
340 	do {
341 		/* search for consecutive clusters */
342 		while (actsize < filesize) {
343 			newclust = get_fatent(mydata, endclust);
344 			if ((newclust - 1) != endclust)
345 				goto getit;
346 			if (CHECK_CLUST(newclust, mydata->fatsize)) {
347 				debug("curclust: 0x%x\n", newclust);
348 				debug("Invalid FAT entry\n");
349 				return gotsize;
350 			}
351 			endclust = newclust;
352 			actsize += bytesperclust;
353 		}
354 
355 		/* actsize >= file size */
356 		actsize -= bytesperclust;
357 
358 		/* get remaining clusters */
359 		if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
360 			printf("Error reading cluster\n");
361 			return -1;
362 		}
363 
364 		/* get remaining bytes */
365 		gotsize += (int)actsize;
366 		filesize -= actsize;
367 		buffer += actsize;
368 		actsize = filesize;
369 		if (get_cluster(mydata, endclust, buffer, (int)actsize) != 0) {
370 			printf("Error reading cluster\n");
371 			return -1;
372 		}
373 		gotsize += actsize;
374 		return gotsize;
375 getit:
376 		if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
377 			printf("Error reading cluster\n");
378 			return -1;
379 		}
380 		gotsize += (int)actsize;
381 		filesize -= actsize;
382 		buffer += actsize;
383 
384 		curclust = get_fatent(mydata, endclust);
385 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
386 			debug("curclust: 0x%x\n", curclust);
387 			printf("Invalid FAT entry\n");
388 			return gotsize;
389 		}
390 		actsize = bytesperclust;
391 		endclust = curclust;
392 	} while (1);
393 }
394 
395 #ifdef CONFIG_SUPPORT_VFAT
396 /*
397  * Extract the file name information from 'slotptr' into 'l_name',
398  * starting at l_name[*idx].
399  * Return 1 if terminator (zero byte) is found, 0 otherwise.
400  */
401 static int slot2str (dir_slot *slotptr, char *l_name, int *idx)
402 {
403 	int j;
404 
405 	for (j = 0; j <= 8; j += 2) {
406 		l_name[*idx] = slotptr->name0_4[j];
407 		if (l_name[*idx] == 0x00)
408 			return 1;
409 		(*idx)++;
410 	}
411 	for (j = 0; j <= 10; j += 2) {
412 		l_name[*idx] = slotptr->name5_10[j];
413 		if (l_name[*idx] == 0x00)
414 			return 1;
415 		(*idx)++;
416 	}
417 	for (j = 0; j <= 2; j += 2) {
418 		l_name[*idx] = slotptr->name11_12[j];
419 		if (l_name[*idx] == 0x00)
420 			return 1;
421 		(*idx)++;
422 	}
423 
424 	return 0;
425 }
426 
427 /*
428  * Extract the full long filename starting at 'retdent' (which is really
429  * a slot) into 'l_name'. If successful also copy the real directory entry
430  * into 'retdent'
431  * Return 0 on success, -1 otherwise.
432  */
433 __attribute__ ((__aligned__ (__alignof__ (dir_entry))))
434 __u8 get_vfatname_block[MAX_CLUSTSIZE];
435 
436 static int
437 get_vfatname (fsdata *mydata, int curclust, __u8 *cluster,
438 	      dir_entry *retdent, char *l_name)
439 {
440 	dir_entry *realdent;
441 	dir_slot *slotptr = (dir_slot *)retdent;
442 	__u8 *nextclust = cluster + mydata->clust_size * SECTOR_SIZE;
443 	__u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff;
444 	int idx = 0;
445 
446 	while ((__u8 *)slotptr < nextclust) {
447 		if (counter == 0)
448 			break;
449 		if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter)
450 			return -1;
451 		slotptr++;
452 		counter--;
453 	}
454 
455 	if ((__u8 *)slotptr >= nextclust) {
456 		dir_slot *slotptr2;
457 
458 		slotptr--;
459 		curclust = get_fatent(mydata, curclust);
460 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
461 			debug("curclust: 0x%x\n", curclust);
462 			printf("Invalid FAT entry\n");
463 			return -1;
464 		}
465 
466 		if (get_cluster(mydata, curclust, get_vfatname_block,
467 				mydata->clust_size * SECTOR_SIZE) != 0) {
468 			debug("Error: reading directory block\n");
469 			return -1;
470 		}
471 
472 		slotptr2 = (dir_slot *)get_vfatname_block;
473 		while (slotptr2->id > 0x01)
474 			slotptr2++;
475 
476 		/* Save the real directory entry */
477 		realdent = (dir_entry *)slotptr2 + 1;
478 		while ((__u8 *)slotptr2 >= get_vfatname_block) {
479 			slot2str(slotptr2, l_name, &idx);
480 			slotptr2--;
481 		}
482 	} else {
483 		/* Save the real directory entry */
484 		realdent = (dir_entry *)slotptr;
485 	}
486 
487 	do {
488 		slotptr--;
489 		if (slot2str(slotptr, l_name, &idx))
490 			break;
491 	} while (!(slotptr->id & LAST_LONG_ENTRY_MASK));
492 
493 	l_name[idx] = '\0';
494 	if (*l_name == DELETED_FLAG)
495 		*l_name = '\0';
496 	else if (*l_name == aRING)
497 		*l_name = DELETED_FLAG;
498 	downcase(l_name);
499 
500 	/* Return the real directory entry */
501 	memcpy(retdent, realdent, sizeof(dir_entry));
502 
503 	return 0;
504 }
505 
506 /* Calculate short name checksum */
507 static __u8 mkcksum (const char *str)
508 {
509 	int i;
510 
511 	__u8 ret = 0;
512 
513 	for (i = 0; i < 11; i++) {
514 		ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + str[i];
515 	}
516 
517 	return ret;
518 }
519 #endif	/* CONFIG_SUPPORT_VFAT */
520 
521 /*
522  * Get the directory entry associated with 'filename' from the directory
523  * starting at 'startsect'
524  */
525 __attribute__ ((__aligned__ (__alignof__ (dir_entry))))
526 __u8 get_dentfromdir_block[MAX_CLUSTSIZE];
527 
528 static dir_entry *get_dentfromdir (fsdata *mydata, int startsect,
529 				   char *filename, dir_entry *retdent,
530 				   int dols)
531 {
532 	__u16 prevcksum = 0xffff;
533 	__u32 curclust = START(retdent);
534 	int files = 0, dirs = 0;
535 
536 	debug("get_dentfromdir: %s\n", filename);
537 
538 	while (1) {
539 		dir_entry *dentptr;
540 
541 		int i;
542 
543 		if (get_cluster(mydata, curclust, get_dentfromdir_block,
544 				mydata->clust_size * SECTOR_SIZE) != 0) {
545 			debug("Error: reading directory block\n");
546 			return NULL;
547 		}
548 
549 		dentptr = (dir_entry *)get_dentfromdir_block;
550 
551 		for (i = 0; i < DIRENTSPERCLUST; i++) {
552 			char s_name[14], l_name[256];
553 
554 			l_name[0] = '\0';
555 			if (dentptr->name[0] == DELETED_FLAG) {
556 				dentptr++;
557 				continue;
558 			}
559 			if ((dentptr->attr & ATTR_VOLUME)) {
560 #ifdef CONFIG_SUPPORT_VFAT
561 				if ((dentptr->attr & ATTR_VFAT) &&
562 				    (dentptr-> name[0] & LAST_LONG_ENTRY_MASK)) {
563 					prevcksum = ((dir_slot *)dentptr)->alias_checksum;
564 					get_vfatname(mydata, curclust,
565 						     get_dentfromdir_block,
566 						     dentptr, l_name);
567 					if (dols) {
568 						int isdir;
569 						char dirc;
570 						int doit = 0;
571 
572 						isdir = (dentptr->attr & ATTR_DIR);
573 
574 						if (isdir) {
575 							dirs++;
576 							dirc = '/';
577 							doit = 1;
578 						} else {
579 							dirc = ' ';
580 							if (l_name[0] != 0) {
581 								files++;
582 								doit = 1;
583 							}
584 						}
585 						if (doit) {
586 							if (dirc == ' ') {
587 								printf(" %8ld   %s%c\n",
588 									(long)FAT2CPU32(dentptr->size),
589 									l_name,
590 									dirc);
591 							} else {
592 								printf("            %s%c\n",
593 									l_name,
594 									dirc);
595 							}
596 						}
597 						dentptr++;
598 						continue;
599 					}
600 					debug("vfatname: |%s|\n", l_name);
601 				} else
602 #endif
603 				{
604 					/* Volume label or VFAT entry */
605 					dentptr++;
606 					continue;
607 				}
608 			}
609 			if (dentptr->name[0] == 0) {
610 				if (dols) {
611 					printf("\n%d file(s), %d dir(s)\n\n",
612 						files, dirs);
613 				}
614 				debug("Dentname == NULL - %d\n", i);
615 				return NULL;
616 			}
617 #ifdef CONFIG_SUPPORT_VFAT
618 			if (dols && mkcksum(dentptr->name) == prevcksum) {
619 				dentptr++;
620 				continue;
621 			}
622 #endif
623 			get_name(dentptr, s_name);
624 			if (dols) {
625 				int isdir = (dentptr->attr & ATTR_DIR);
626 				char dirc;
627 				int doit = 0;
628 
629 				if (isdir) {
630 					dirs++;
631 					dirc = '/';
632 					doit = 1;
633 				} else {
634 					dirc = ' ';
635 					if (s_name[0] != 0) {
636 						files++;
637 						doit = 1;
638 					}
639 				}
640 
641 				if (doit) {
642 					if (dirc == ' ') {
643 						printf(" %8ld   %s%c\n",
644 							(long)FAT2CPU32(dentptr->size),
645 							s_name, dirc);
646 					} else {
647 						printf("            %s%c\n",
648 							s_name, dirc);
649 					}
650 				}
651 
652 				dentptr++;
653 				continue;
654 			}
655 
656 			if (strcmp(filename, s_name)
657 			    && strcmp(filename, l_name)) {
658 				debug("Mismatch: |%s|%s|\n", s_name, l_name);
659 				dentptr++;
660 				continue;
661 			}
662 
663 			memcpy(retdent, dentptr, sizeof(dir_entry));
664 
665 			debug("DentName: %s", s_name);
666 			debug(", start: 0x%x", START(dentptr));
667 			debug(", size:  0x%x %s\n",
668 			      FAT2CPU32(dentptr->size),
669 			      (dentptr->attr & ATTR_DIR) ? "(DIR)" : "");
670 
671 			return retdent;
672 		}
673 
674 		curclust = get_fatent(mydata, curclust);
675 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
676 			debug("curclust: 0x%x\n", curclust);
677 			printf("Invalid FAT entry\n");
678 			return NULL;
679 		}
680 	}
681 
682 	return NULL;
683 }
684 
685 /*
686  * Read boot sector and volume info from a FAT filesystem
687  */
688 static int
689 read_bootsectandvi (boot_sector *bs, volume_info *volinfo, int *fatsize)
690 {
691 	__u8 block[FS_BLOCK_SIZE];
692 
693 	volume_info *vistart;
694 
695 	if (disk_read (0, 1, block) < 0) {
696 		debug("Error: reading block\n");
697 		return -1;
698 	}
699 
700 	memcpy(bs, block, sizeof(boot_sector));
701 	bs->reserved = FAT2CPU16(bs->reserved);
702 	bs->fat_length = FAT2CPU16(bs->fat_length);
703 	bs->secs_track = FAT2CPU16(bs->secs_track);
704 	bs->heads = FAT2CPU16(bs->heads);
705 	bs->total_sect = FAT2CPU32(bs->total_sect);
706 
707 	/* FAT32 entries */
708 	if (bs->fat_length == 0) {
709 		/* Assume FAT32 */
710 		bs->fat32_length = FAT2CPU32(bs->fat32_length);
711 		bs->flags = FAT2CPU16(bs->flags);
712 		bs->root_cluster = FAT2CPU32(bs->root_cluster);
713 		bs->info_sector = FAT2CPU16(bs->info_sector);
714 		bs->backup_boot = FAT2CPU16(bs->backup_boot);
715 		vistart = (volume_info *)(block + sizeof(boot_sector));
716 		*fatsize = 32;
717 	} else {
718 		vistart = (volume_info *)&(bs->fat32_length);
719 		*fatsize = 0;
720 	}
721 	memcpy(volinfo, vistart, sizeof(volume_info));
722 
723 	if (*fatsize == 32) {
724 		if (strncmp(FAT32_SIGN, vistart->fs_type, SIGNLEN) == 0)
725 			return 0;
726 	} else {
727 		if (strncmp(FAT12_SIGN, vistart->fs_type, SIGNLEN) == 0) {
728 			*fatsize = 12;
729 			return 0;
730 		}
731 		if (strncmp(FAT16_SIGN, vistart->fs_type, SIGNLEN) == 0) {
732 			*fatsize = 16;
733 			return 0;
734 		}
735 	}
736 
737 	debug("Error: broken fs_type sign\n");
738 	return -1;
739 }
740 
741 __attribute__ ((__aligned__ (__alignof__ (dir_entry))))
742 __u8 do_fat_read_block[MAX_CLUSTSIZE];
743 
744 long
745 do_fat_read (const char *filename, void *buffer, unsigned long maxsize,
746 	     int dols)
747 {
748 	char fnamecopy[2048];
749 	boot_sector bs;
750 	volume_info volinfo;
751 	fsdata datablock;
752 	fsdata *mydata = &datablock;
753 	dir_entry *dentptr;
754 	__u16 prevcksum = 0xffff;
755 	char *subname = "";
756 	int cursect;
757 	int idx, isdir = 0;
758 	int files = 0, dirs = 0;
759 	long ret = 0;
760 	int firsttime;
761 	int root_cluster;
762 	int j;
763 
764 	if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) {
765 		debug("Error: reading boot sector\n");
766 		return -1;
767 	}
768 
769 	root_cluster = bs.root_cluster;
770 
771 	if (mydata->fatsize == 32)
772 		mydata->fatlength = bs.fat32_length;
773 	else
774 		mydata->fatlength = bs.fat_length;
775 
776 	mydata->fat_sect = bs.reserved;
777 
778 	cursect = mydata->rootdir_sect
779 		= mydata->fat_sect + mydata->fatlength * bs.fats;
780 
781 	mydata->clust_size = bs.cluster_size;
782 
783 	if (mydata->fatsize == 32) {
784 		mydata->data_begin = mydata->rootdir_sect -
785 					(mydata->clust_size * 2);
786 	} else {
787 		int rootdir_size;
788 
789 		rootdir_size = ((bs.dir_entries[1]  * (int)256 +
790 				 bs.dir_entries[0]) *
791 				 sizeof(dir_entry)) /
792 				 SECTOR_SIZE;
793 		mydata->data_begin = mydata->rootdir_sect +
794 					rootdir_size -
795 					(mydata->clust_size * 2);
796 	}
797 
798 	mydata->fatbufnum = -1;
799 
800 #ifdef CONFIG_SUPPORT_VFAT
801 	debug("VFAT Support enabled\n");
802 #endif
803 	debug("FAT%d, fat_sect: %d, fatlength: %d\n",
804 	       mydata->fatsize, mydata->fat_sect, mydata->fatlength);
805 	debug("Rootdir begins at cluster: %d, sector: %d, offset: %x\n"
806 	       "Data begins at: %d\n",
807 	       root_cluster,
808 	       mydata->rootdir_sect,
809 	       mydata->rootdir_sect * SECTOR_SIZE, mydata->data_begin);
810 	debug("Cluster size: %d\n", mydata->clust_size);
811 
812 	/* "cwd" is always the root... */
813 	while (ISDIRDELIM(*filename))
814 		filename++;
815 
816 	/* Make a copy of the filename and convert it to lowercase */
817 	strcpy(fnamecopy, filename);
818 	downcase(fnamecopy);
819 
820 	if (*fnamecopy == '\0') {
821 		if (!dols)
822 			return -1;
823 
824 		dols = LS_ROOT;
825 	} else if ((idx = dirdelim(fnamecopy)) >= 0) {
826 		isdir = 1;
827 		fnamecopy[idx] = '\0';
828 		subname = fnamecopy + idx + 1;
829 
830 		/* Handle multiple delimiters */
831 		while (ISDIRDELIM(*subname))
832 			subname++;
833 	} else if (dols) {
834 		isdir = 1;
835 	}
836 
837 	j = 0;
838 	while (1) {
839 		int i;
840 
841 		debug("FAT read sect=%d, clust_size=%d, DIRENTSPERBLOCK=%d\n",
842 			cursect, mydata->clust_size, DIRENTSPERBLOCK);
843 
844 		if (disk_read(cursect, mydata->clust_size, do_fat_read_block) < 0) {
845 			debug("Error: reading rootdir block\n");
846 			return -1;
847 		}
848 
849 		dentptr = (dir_entry *) do_fat_read_block;
850 
851 		for (i = 0; i < DIRENTSPERBLOCK; i++) {
852 			char s_name[14], l_name[256];
853 
854 			l_name[0] = '\0';
855 			if ((dentptr->attr & ATTR_VOLUME)) {
856 #ifdef CONFIG_SUPPORT_VFAT
857 				if ((dentptr->attr & ATTR_VFAT) &&
858 				    (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
859 					prevcksum =
860 						((dir_slot *)dentptr)->alias_checksum;
861 
862 					get_vfatname(mydata, 0,
863 						     do_fat_read_block,
864 						     dentptr, l_name);
865 
866 					if (dols == LS_ROOT) {
867 						char dirc;
868 						int doit = 0;
869 						int isdir =
870 							(dentptr->attr & ATTR_DIR);
871 
872 						if (isdir) {
873 							dirs++;
874 							dirc = '/';
875 							doit = 1;
876 						} else {
877 							dirc = ' ';
878 							if (l_name[0] != 0) {
879 								files++;
880 								doit = 1;
881 							}
882 						}
883 						if (doit) {
884 							if (dirc == ' ') {
885 								printf(" %8ld   %s%c\n",
886 									(long)FAT2CPU32(dentptr->size),
887 									l_name,
888 									dirc);
889 							} else {
890 								printf("            %s%c\n",
891 									l_name,
892 									dirc);
893 							}
894 						}
895 						dentptr++;
896 						continue;
897 					}
898 					debug("Rootvfatname: |%s|\n",
899 					       l_name);
900 				} else
901 #endif
902 				{
903 					/* Volume label or VFAT entry */
904 					dentptr++;
905 					continue;
906 				}
907 			} else if (dentptr->name[0] == 0) {
908 				debug("RootDentname == NULL - %d\n", i);
909 				if (dols == LS_ROOT) {
910 					printf("\n%d file(s), %d dir(s)\n\n",
911 						files, dirs);
912 					return 0;
913 				}
914 				return -1;
915 			}
916 #ifdef CONFIG_SUPPORT_VFAT
917 			else if (dols == LS_ROOT &&
918 				 mkcksum(dentptr->name) == prevcksum) {
919 				dentptr++;
920 				continue;
921 			}
922 #endif
923 			get_name(dentptr, s_name);
924 
925 			if (dols == LS_ROOT) {
926 				int isdir = (dentptr->attr & ATTR_DIR);
927 				char dirc;
928 				int doit = 0;
929 
930 				if (isdir) {
931 					dirc = '/';
932 					if (s_name[0] != 0) {
933 						dirs++;
934 						doit = 1;
935 					}
936 				} else {
937 					dirc = ' ';
938 					if (s_name[0] != 0) {
939 						files++;
940 						doit = 1;
941 					}
942 				}
943 				if (doit) {
944 					if (dirc == ' ') {
945 						printf(" %8ld   %s%c\n",
946 							(long)FAT2CPU32(dentptr->size),
947 							s_name, dirc);
948 					} else {
949 						printf("            %s%c\n",
950 							s_name, dirc);
951 					}
952 				}
953 				dentptr++;
954 				continue;
955 			}
956 
957 			if (strcmp(fnamecopy, s_name)
958 			    && strcmp(fnamecopy, l_name)) {
959 				debug("RootMismatch: |%s|%s|\n", s_name,
960 				       l_name);
961 				dentptr++;
962 				continue;
963 			}
964 
965 			if (isdir && !(dentptr->attr & ATTR_DIR))
966 				return -1;
967 
968 			debug("RootName: %s", s_name);
969 			debug(", start: 0x%x", START(dentptr));
970 			debug(", size:  0x%x %s\n",
971 			       FAT2CPU32(dentptr->size),
972 			       isdir ? "(DIR)" : "");
973 
974 			goto rootdir_done;	/* We got a match */
975 		}
976 		debug("END LOOP: j=%d   clust_size=%d\n", j,
977 		       mydata->clust_size);
978 
979 		/*
980 		 * On FAT32 we must fetch the FAT entries for the next
981 		 * root directory clusters when a cluster has been
982 		 * completely processed.
983 		 */
984 		if ((mydata->fatsize == 32) && (++j == mydata->clust_size)) {
985 			int nxtsect;
986 			int nxt_clust;
987 
988 			nxt_clust = get_fatent(mydata, root_cluster);
989 
990 			nxtsect = mydata->data_begin +
991 				(nxt_clust * mydata->clust_size);
992 
993 			debug("END LOOP: sect=%d, root_clust=%d, "
994 			      "n_sect=%d, n_clust=%d\n",
995 			      cursect, root_cluster,
996 			      nxtsect, nxt_clust);
997 
998 			root_cluster = nxt_clust;
999 
1000 			cursect = nxtsect;
1001 			j = 0;
1002 		} else {
1003 			cursect++;
1004 		}
1005 	}
1006 rootdir_done:
1007 
1008 	firsttime = 1;
1009 
1010 	while (isdir) {
1011 		int startsect = mydata->data_begin
1012 			+ START(dentptr) * mydata->clust_size;
1013 		dir_entry dent;
1014 		char *nextname = NULL;
1015 
1016 		dent = *dentptr;
1017 		dentptr = &dent;
1018 
1019 		idx = dirdelim(subname);
1020 
1021 		if (idx >= 0) {
1022 			subname[idx] = '\0';
1023 			nextname = subname + idx + 1;
1024 			/* Handle multiple delimiters */
1025 			while (ISDIRDELIM(*nextname))
1026 				nextname++;
1027 			if (dols && *nextname == '\0')
1028 				firsttime = 0;
1029 		} else {
1030 			if (dols && firsttime) {
1031 				firsttime = 0;
1032 			} else {
1033 				isdir = 0;
1034 			}
1035 		}
1036 
1037 		if (get_dentfromdir(mydata, startsect, subname, dentptr,
1038 				     isdir ? 0 : dols) == NULL) {
1039 			if (dols && !isdir)
1040 				return 0;
1041 			return -1;
1042 		}
1043 
1044 		if (idx >= 0) {
1045 			if (!(dentptr->attr & ATTR_DIR))
1046 				return -1;
1047 			subname = nextname;
1048 		}
1049 	}
1050 
1051 	ret = get_contents(mydata, dentptr, buffer, maxsize);
1052 	debug("Size: %d, got: %ld\n", FAT2CPU32(dentptr->size), ret);
1053 
1054 	return ret;
1055 }
1056 
1057 int file_fat_detectfs (void)
1058 {
1059 	boot_sector bs;
1060 	volume_info volinfo;
1061 	int fatsize;
1062 	char vol_label[12];
1063 
1064 	if (cur_dev == NULL) {
1065 		printf("No current device\n");
1066 		return 1;
1067 	}
1068 
1069 #if defined(CONFIG_CMD_IDE) || \
1070     defined(CONFIG_CMD_MG_DISK) || \
1071     defined(CONFIG_CMD_SATA) || \
1072     defined(CONFIG_CMD_SCSI) || \
1073     defined(CONFIG_CMD_USB) || \
1074     defined(CONFIG_MMC)
1075 	printf("Interface:  ");
1076 	switch (cur_dev->if_type) {
1077 	case IF_TYPE_IDE:
1078 		printf("IDE");
1079 		break;
1080 	case IF_TYPE_SATA:
1081 		printf("SATA");
1082 		break;
1083 	case IF_TYPE_SCSI:
1084 		printf("SCSI");
1085 		break;
1086 	case IF_TYPE_ATAPI:
1087 		printf("ATAPI");
1088 		break;
1089 	case IF_TYPE_USB:
1090 		printf("USB");
1091 		break;
1092 	case IF_TYPE_DOC:
1093 		printf("DOC");
1094 		break;
1095 	case IF_TYPE_MMC:
1096 		printf("MMC");
1097 		break;
1098 	default:
1099 		printf("Unknown");
1100 	}
1101 
1102 	printf("\n  Device %d: ", cur_dev->dev);
1103 	dev_print(cur_dev);
1104 #endif
1105 
1106 	if (read_bootsectandvi(&bs, &volinfo, &fatsize)) {
1107 		printf("\nNo valid FAT fs found\n");
1108 		return 1;
1109 	}
1110 
1111 	memcpy(vol_label, volinfo.volume_label, 11);
1112 	vol_label[11] = '\0';
1113 	volinfo.fs_type[5] = '\0';
1114 
1115 	printf("Partition %d: Filesystem: %s \"%s\"\n", cur_part,
1116 		volinfo.fs_type, vol_label);
1117 
1118 	return 0;
1119 }
1120 
1121 int file_fat_ls (const char *dir)
1122 {
1123 	return do_fat_read(dir, NULL, 0, LS_YES);
1124 }
1125 
1126 long file_fat_read (const char *filename, void *buffer, unsigned long maxsize)
1127 {
1128 	printf("reading %s\n", filename);
1129 	return do_fat_read(filename, buffer, maxsize, LS_NO);
1130 }
1131