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