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