xref: /rk3399_rockchip-uboot/fs/fat/fat.c (revision 60b36f0fc7c77e98b9d55c237e455c42a1caa44b)
171f95118Swdenk /*
271f95118Swdenk  * fat.c
371f95118Swdenk  *
471f95118Swdenk  * R/O (V)FAT 12/16/32 filesystem implementation by Marcus Sundberg
571f95118Swdenk  *
671f95118Swdenk  * 2002-07-28 - rjones@nexus-tech.net - ported to ppcboot v1.1.6
771f95118Swdenk  * 2003-03-10 - kharris@nexus-tech.net - ported to uboot
871f95118Swdenk  *
971f95118Swdenk  * See file CREDITS for list of people who contributed to this
1071f95118Swdenk  * project.
1171f95118Swdenk  *
1271f95118Swdenk  * This program is free software; you can redistribute it and/or
1371f95118Swdenk  * modify it under the terms of the GNU General Public License as
1471f95118Swdenk  * published by the Free Software Foundation; either version 2 of
1571f95118Swdenk  * the License, or (at your option) any later version.
1671f95118Swdenk  *
1771f95118Swdenk  * This program is distributed in the hope that it will be useful,
1871f95118Swdenk  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1971f95118Swdenk  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2071f95118Swdenk  * GNU General Public License for more details.
2171f95118Swdenk  *
2271f95118Swdenk  * You should have received a copy of the GNU General Public License
2371f95118Swdenk  * along with this program; if not, write to the Free Software
2471f95118Swdenk  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
2571f95118Swdenk  * MA 02111-1307 USA
2671f95118Swdenk  */
2771f95118Swdenk 
2871f95118Swdenk #include <common.h>
2971f95118Swdenk #include <config.h>
30ac497771SSergei Shtylyov #include <exports.h>
3171f95118Swdenk #include <fat.h>
3271f95118Swdenk #include <asm/byteorder.h>
337205e407Swdenk #include <part.h>
3471f95118Swdenk 
3571f95118Swdenk /*
3671f95118Swdenk  * Convert a string to lowercase.
3771f95118Swdenk  */
387385c28eSWolfgang Denk static void downcase (char *str)
3971f95118Swdenk {
4071f95118Swdenk 	while (*str != '\0') {
4171f95118Swdenk 		TOLOWER(*str);
4271f95118Swdenk 		str++;
4371f95118Swdenk 	}
4471f95118Swdenk }
4571f95118Swdenk 
467205e407Swdenk static block_dev_desc_t *cur_dev = NULL;
477385c28eSWolfgang Denk 
487205e407Swdenk static unsigned long part_offset = 0;
497385c28eSWolfgang Denk 
507205e407Swdenk static int cur_part = 1;
517205e407Swdenk 
527205e407Swdenk #define DOS_PART_TBL_OFFSET	0x1be
537205e407Swdenk #define DOS_PART_MAGIC_OFFSET	0x1fe
547205e407Swdenk #define DOS_FS_TYPE_OFFSET	0x36
5566c2d73cSWolfgang Denk #define DOS_FS32_TYPE_OFFSET	0x52
5671f95118Swdenk 
577385c28eSWolfgang Denk static int disk_read (__u32 startblock, __u32 getsize, __u8 * bufptr)
5871f95118Swdenk {
597205e407Swdenk 	if (cur_dev == NULL)
607205e407Swdenk 		return -1;
617385c28eSWolfgang Denk 
627385c28eSWolfgang Denk 	startblock += part_offset;
637385c28eSWolfgang Denk 
647205e407Swdenk 	if (cur_dev->block_read) {
657385c28eSWolfgang Denk 		return cur_dev->block_read(cur_dev->dev, startblock, getsize,
667385c28eSWolfgang Denk 					   (unsigned long *) bufptr);
6771f95118Swdenk 	}
6871f95118Swdenk 	return -1;
6971f95118Swdenk }
7071f95118Swdenk 
717385c28eSWolfgang Denk int fat_register_device (block_dev_desc_t * dev_desc, int part_no)
7271f95118Swdenk {
73ac497771SSergei Shtylyov 	unsigned char buffer[dev_desc->blksz];
74566a494fSHeiko Schocher 	disk_partition_t info;
757205e407Swdenk 
767205e407Swdenk 	if (!dev_desc->block_read)
777205e407Swdenk 		return -1;
787385c28eSWolfgang Denk 
797205e407Swdenk 	cur_dev = dev_desc;
807205e407Swdenk 	/* check if we have a MBR (on floppies we have only a PBR) */
817205e407Swdenk 	if (dev_desc->block_read(dev_desc->dev, 0, 1, (ulong *)buffer) != 1) {
827385c28eSWolfgang Denk 		printf("** Can't read from device %d **\n",
837385c28eSWolfgang Denk 			dev_desc->dev);
847205e407Swdenk 		return -1;
857205e407Swdenk 	}
867205e407Swdenk 	if (buffer[DOS_PART_MAGIC_OFFSET] != 0x55 ||
877205e407Swdenk 	    buffer[DOS_PART_MAGIC_OFFSET + 1] != 0xaa) {
887205e407Swdenk 		/* no signature found */
897205e407Swdenk 		return -1;
907205e407Swdenk 	}
91dd60d122SJon Loeliger #if (defined(CONFIG_CMD_IDE) || \
9275eb82ecSunsik Kim      defined(CONFIG_CMD_MG_DISK) || \
938c5170a7SSonic Zhang      defined(CONFIG_CMD_SATA) || \
94dd60d122SJon Loeliger      defined(CONFIG_CMD_SCSI) || \
95dd60d122SJon Loeliger      defined(CONFIG_CMD_USB) || \
9602df4a27SAndy Fleming      defined(CONFIG_MMC) || \
97b0d8f5bfSPeter Pearse      defined(CONFIG_SYSTEMACE) )
987385c28eSWolfgang Denk 	/* First we assume there is a MBR */
997205e407Swdenk 	if (!get_partition_info(dev_desc, part_no, &info)) {
1007205e407Swdenk 		part_offset = info.start;
1017205e407Swdenk 		cur_part = part_no;
1027385c28eSWolfgang Denk 	} else if ((strncmp((char *)&buffer[DOS_FS_TYPE_OFFSET], "FAT", 3) == 0) ||
1037385c28eSWolfgang Denk 		   (strncmp((char *)&buffer[DOS_FS32_TYPE_OFFSET], "FAT32", 5) == 0)) {
104566a494fSHeiko Schocher 		/* ok, we assume we are on a PBR only */
105566a494fSHeiko Schocher 		cur_part = 1;
106566a494fSHeiko Schocher 		part_offset = 0;
107566a494fSHeiko Schocher 	} else {
108bf1060eaSWolfgang Denk 		printf("** Partition %d not valid on device %d **\n",
109bf1060eaSWolfgang Denk 			part_no, dev_desc->dev);
1107205e407Swdenk 		return -1;
1117205e407Swdenk 	}
11202df4a27SAndy Fleming 
1137205e407Swdenk #else
11466c2d73cSWolfgang Denk 	if ((strncmp((char *)&buffer[DOS_FS_TYPE_OFFSET], "FAT", 3) == 0) ||
11566c2d73cSWolfgang Denk 	    (strncmp((char *)&buffer[DOS_FS32_TYPE_OFFSET], "FAT32", 5) == 0)) {
116566a494fSHeiko Schocher 		/* ok, we assume we are on a PBR only */
117566a494fSHeiko Schocher 		cur_part = 1;
118566a494fSHeiko Schocher 		part_offset = 0;
119566a494fSHeiko Schocher 		info.start = part_offset;
120566a494fSHeiko Schocher 	} else {
1217205e407Swdenk 		/* FIXME we need to determine the start block of the
1227205e407Swdenk 		 * partition where the DOS FS resides. This can be done
1237205e407Swdenk 		 * by using the get_partition_info routine. For this
1247205e407Swdenk 		 * purpose the libpart must be included.
1257205e407Swdenk 		 */
1267205e407Swdenk 		part_offset = 32;
1277205e407Swdenk 		cur_part = 1;
1287205e407Swdenk 	}
129566a494fSHeiko Schocher #endif
13071f95118Swdenk 	return 0;
13171f95118Swdenk }
13271f95118Swdenk 
13371f95118Swdenk /*
13471f95118Swdenk  * Get the first occurence of a directory delimiter ('/' or '\') in a string.
13571f95118Swdenk  * Return index into string if found, -1 otherwise.
13671f95118Swdenk  */
1377385c28eSWolfgang Denk static int dirdelim (char *str)
13871f95118Swdenk {
13971f95118Swdenk 	char *start = str;
14071f95118Swdenk 
14171f95118Swdenk 	while (*str != '\0') {
1427385c28eSWolfgang Denk 		if (ISDIRDELIM(*str))
1437385c28eSWolfgang Denk 			return str - start;
14471f95118Swdenk 		str++;
14571f95118Swdenk 	}
14671f95118Swdenk 	return -1;
14771f95118Swdenk }
14871f95118Swdenk 
14971f95118Swdenk /*
15071f95118Swdenk  * Extract zero terminated short name from a directory entry.
15171f95118Swdenk  */
15271f95118Swdenk static void get_name (dir_entry *dirent, char *s_name)
15371f95118Swdenk {
15471f95118Swdenk 	char *ptr;
15571f95118Swdenk 
15671f95118Swdenk 	memcpy(s_name, dirent->name, 8);
15771f95118Swdenk 	s_name[8] = '\0';
15871f95118Swdenk 	ptr = s_name;
15971f95118Swdenk 	while (*ptr && *ptr != ' ')
16071f95118Swdenk 		ptr++;
16171f95118Swdenk 	if (dirent->ext[0] && dirent->ext[0] != ' ') {
16271f95118Swdenk 		*ptr = '.';
16371f95118Swdenk 		ptr++;
16471f95118Swdenk 		memcpy(ptr, dirent->ext, 3);
16571f95118Swdenk 		ptr[3] = '\0';
16671f95118Swdenk 		while (*ptr && *ptr != ' ')
16771f95118Swdenk 			ptr++;
16871f95118Swdenk 	}
16971f95118Swdenk 	*ptr = '\0';
17071f95118Swdenk 	if (*s_name == DELETED_FLAG)
17171f95118Swdenk 		*s_name = '\0';
17271f95118Swdenk 	else if (*s_name == aRING)
1733c2c2f42SRemy Bohmer 		*s_name = DELETED_FLAG;
17471f95118Swdenk 	downcase(s_name);
17571f95118Swdenk }
17671f95118Swdenk 
17771f95118Swdenk /*
17871f95118Swdenk  * Get the entry at index 'entry' in a FAT (12/16/32) table.
17971f95118Swdenk  * On failure 0x00 is returned.
18071f95118Swdenk  */
1817385c28eSWolfgang Denk static __u32 get_fatent (fsdata *mydata, __u32 entry)
18271f95118Swdenk {
18371f95118Swdenk 	__u32 bufnum;
1847385c28eSWolfgang Denk 	__u32 off16, offset;
18571f95118Swdenk 	__u32 ret = 0x00;
1867385c28eSWolfgang Denk 	__u16 val1, val2;
18771f95118Swdenk 
18871f95118Swdenk 	switch (mydata->fatsize) {
18971f95118Swdenk 	case 32:
19071f95118Swdenk 		bufnum = entry / FAT32BUFSIZE;
19171f95118Swdenk 		offset = entry - bufnum * FAT32BUFSIZE;
19271f95118Swdenk 		break;
19371f95118Swdenk 	case 16:
19471f95118Swdenk 		bufnum = entry / FAT16BUFSIZE;
19571f95118Swdenk 		offset = entry - bufnum * FAT16BUFSIZE;
19671f95118Swdenk 		break;
19771f95118Swdenk 	case 12:
19871f95118Swdenk 		bufnum = entry / FAT12BUFSIZE;
19971f95118Swdenk 		offset = entry - bufnum * FAT12BUFSIZE;
20071f95118Swdenk 		break;
20171f95118Swdenk 
20271f95118Swdenk 	default:
20371f95118Swdenk 		/* Unsupported FAT size */
20471f95118Swdenk 		return ret;
20571f95118Swdenk 	}
20671f95118Swdenk 
2077385c28eSWolfgang Denk 	debug("FAT%d: entry: 0x%04x = %d, offset: 0x%04x = %d\n",
2082aa98c66SWolfgang Denk 	       mydata->fatsize, entry, entry, offset, offset);
2092aa98c66SWolfgang Denk 
21071f95118Swdenk 	/* Read a new block of FAT entries into the cache. */
21171f95118Swdenk 	if (bufnum != mydata->fatbufnum) {
212*60b36f0fSSergei Shtylyov 		__u32 getsize = FATBUFBLOCKS;
21371f95118Swdenk 		__u8 *bufptr = mydata->fatbuf;
21471f95118Swdenk 		__u32 fatlength = mydata->fatlength;
21571f95118Swdenk 		__u32 startblock = bufnum * FATBUFBLOCKS;
21671f95118Swdenk 
217*60b36f0fSSergei Shtylyov 		if (getsize > fatlength)
218*60b36f0fSSergei Shtylyov 			getsize = fatlength;
219*60b36f0fSSergei Shtylyov 
220ac497771SSergei Shtylyov 		fatlength *= mydata->sect_size;	/* We want it in bytes now */
22171f95118Swdenk 		startblock += mydata->fat_sect;	/* Offset from start of disk */
22271f95118Swdenk 
22371f95118Swdenk 		if (disk_read(startblock, getsize, bufptr) < 0) {
2247385c28eSWolfgang Denk 			debug("Error reading FAT blocks\n");
22571f95118Swdenk 			return ret;
22671f95118Swdenk 		}
22771f95118Swdenk 		mydata->fatbufnum = bufnum;
22871f95118Swdenk 	}
22971f95118Swdenk 
23071f95118Swdenk 	/* Get the actual entry from the table */
23171f95118Swdenk 	switch (mydata->fatsize) {
23271f95118Swdenk 	case 32:
23371f95118Swdenk 		ret = FAT2CPU32(((__u32 *) mydata->fatbuf)[offset]);
23471f95118Swdenk 		break;
23571f95118Swdenk 	case 16:
23671f95118Swdenk 		ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[offset]);
23771f95118Swdenk 		break;
2387385c28eSWolfgang Denk 	case 12:
2397385c28eSWolfgang Denk 		off16 = (offset * 3) / 4;
24071f95118Swdenk 
24171f95118Swdenk 		switch (offset & 0x3) {
24271f95118Swdenk 		case 0:
24371f95118Swdenk 			ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[off16]);
24471f95118Swdenk 			ret &= 0xfff;
24571f95118Swdenk 			break;
24671f95118Swdenk 		case 1:
24771f95118Swdenk 			val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
24871f95118Swdenk 			val1 &= 0xf000;
24971f95118Swdenk 			val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]);
25071f95118Swdenk 			val2 &= 0x00ff;
25171f95118Swdenk 			ret = (val2 << 4) | (val1 >> 12);
25271f95118Swdenk 			break;
25371f95118Swdenk 		case 2:
25471f95118Swdenk 			val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
25571f95118Swdenk 			val1 &= 0xff00;
25671f95118Swdenk 			val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]);
25771f95118Swdenk 			val2 &= 0x000f;
25871f95118Swdenk 			ret = (val2 << 8) | (val1 >> 8);
25971f95118Swdenk 			break;
26071f95118Swdenk 		case 3:
2617385c28eSWolfgang Denk 			ret = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
26271f95118Swdenk 			ret = (ret & 0xfff0) >> 4;
26371f95118Swdenk 			break;
26471f95118Swdenk 		default:
26571f95118Swdenk 			break;
26671f95118Swdenk 		}
26771f95118Swdenk 		break;
26871f95118Swdenk 	}
2697385c28eSWolfgang Denk 	debug("FAT%d: ret: %08x, offset: %04x\n",
2702aa98c66SWolfgang Denk 	       mydata->fatsize, ret, offset);
27171f95118Swdenk 
27271f95118Swdenk 	return ret;
27371f95118Swdenk }
27471f95118Swdenk 
27571f95118Swdenk /*
27671f95118Swdenk  * Read at most 'size' bytes from the specified cluster into 'buffer'.
27771f95118Swdenk  * Return 0 on success, -1 otherwise.
27871f95118Swdenk  */
27971f95118Swdenk static int
2807385c28eSWolfgang Denk get_cluster (fsdata *mydata, __u32 clustnum, __u8 *buffer,
2817385c28eSWolfgang Denk 	     unsigned long size)
28271f95118Swdenk {
2833f270f42SErik Hansen 	__u32 idx = 0;
28471f95118Swdenk 	__u32 startsect;
28571f95118Swdenk 
28671f95118Swdenk 	if (clustnum > 0) {
2877385c28eSWolfgang Denk 		startsect = mydata->data_begin +
2887385c28eSWolfgang Denk 				clustnum * mydata->clust_size;
28971f95118Swdenk 	} else {
29071f95118Swdenk 		startsect = mydata->rootdir_sect;
29171f95118Swdenk 	}
29271f95118Swdenk 
2937385c28eSWolfgang Denk 	debug("gc - clustnum: %d, startsect: %d\n", clustnum, startsect);
2947385c28eSWolfgang Denk 
295ac497771SSergei Shtylyov 	if (disk_read(startsect, size / mydata->sect_size, buffer) < 0) {
2967385c28eSWolfgang Denk 		debug("Error reading data\n");
29771f95118Swdenk 		return -1;
29871f95118Swdenk 	}
299ac497771SSergei Shtylyov 	if (size % mydata->sect_size) {
300ac497771SSergei Shtylyov 		__u8 tmpbuf[mydata->sect_size];
3017385c28eSWolfgang Denk 
302ac497771SSergei Shtylyov 		idx = size / mydata->sect_size;
30371f95118Swdenk 		if (disk_read(startsect + idx, 1, tmpbuf) < 0) {
3047385c28eSWolfgang Denk 			debug("Error reading data\n");
30571f95118Swdenk 			return -1;
30671f95118Swdenk 		}
307ac497771SSergei Shtylyov 		buffer += idx * mydata->sect_size;
30871f95118Swdenk 
309ac497771SSergei Shtylyov 		memcpy(buffer, tmpbuf, size % mydata->sect_size);
31071f95118Swdenk 		return 0;
31171f95118Swdenk 	}
31271f95118Swdenk 
31371f95118Swdenk 	return 0;
31471f95118Swdenk }
31571f95118Swdenk 
31671f95118Swdenk /*
31771f95118Swdenk  * Read at most 'maxsize' bytes from the file associated with 'dentptr'
31871f95118Swdenk  * into 'buffer'.
31971f95118Swdenk  * Return the number of bytes read or -1 on fatal errors.
32071f95118Swdenk  */
32171f95118Swdenk static long
32271f95118Swdenk get_contents (fsdata *mydata, dir_entry *dentptr, __u8 *buffer,
32371f95118Swdenk 	      unsigned long maxsize)
32471f95118Swdenk {
32571f95118Swdenk 	unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0;
326ac497771SSergei Shtylyov 	unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
32771f95118Swdenk 	__u32 curclust = START(dentptr);
3287205e407Swdenk 	__u32 endclust, newclust;
3297205e407Swdenk 	unsigned long actsize;
33071f95118Swdenk 
3317385c28eSWolfgang Denk 	debug("Filesize: %ld bytes\n", filesize);
33271f95118Swdenk 
3337385c28eSWolfgang Denk 	if (maxsize > 0 && filesize > maxsize)
3347385c28eSWolfgang Denk 		filesize = maxsize;
33571f95118Swdenk 
3367385c28eSWolfgang Denk 	debug("%ld bytes\n", filesize);
33771f95118Swdenk 
3387205e407Swdenk 	actsize = bytesperclust;
3397205e407Swdenk 	endclust = curclust;
3407385c28eSWolfgang Denk 
34171f95118Swdenk 	do {
3427205e407Swdenk 		/* search for consecutive clusters */
3437205e407Swdenk 		while (actsize < filesize) {
3447205e407Swdenk 			newclust = get_fatent(mydata, endclust);
3457205e407Swdenk 			if ((newclust - 1) != endclust)
3467205e407Swdenk 				goto getit;
3478ce4e5c2Smichael 			if (CHECK_CLUST(newclust, mydata->fatsize)) {
3487385c28eSWolfgang Denk 				debug("curclust: 0x%x\n", newclust);
3497385c28eSWolfgang Denk 				debug("Invalid FAT entry\n");
3507205e407Swdenk 				return gotsize;
3517205e407Swdenk 			}
3527205e407Swdenk 			endclust = newclust;
3537205e407Swdenk 			actsize += bytesperclust;
3547205e407Swdenk 		}
3557385c28eSWolfgang Denk 
3567205e407Swdenk 		/* actsize >= file size */
3577205e407Swdenk 		actsize -= bytesperclust;
3587385c28eSWolfgang Denk 
3597205e407Swdenk 		/* get remaining clusters */
3607205e407Swdenk 		if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
3617385c28eSWolfgang Denk 			printf("Error reading cluster\n");
36271f95118Swdenk 			return -1;
36371f95118Swdenk 		}
3647385c28eSWolfgang Denk 
3657205e407Swdenk 		/* get remaining bytes */
3667205e407Swdenk 		gotsize += (int)actsize;
3677205e407Swdenk 		filesize -= actsize;
3687205e407Swdenk 		buffer += actsize;
3697205e407Swdenk 		actsize = filesize;
3707205e407Swdenk 		if (get_cluster(mydata, endclust, buffer, (int)actsize) != 0) {
3717385c28eSWolfgang Denk 			printf("Error reading cluster\n");
3727205e407Swdenk 			return -1;
3737205e407Swdenk 		}
3747205e407Swdenk 		gotsize += actsize;
3757205e407Swdenk 		return gotsize;
3767205e407Swdenk getit:
3777205e407Swdenk 		if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
3787385c28eSWolfgang Denk 			printf("Error reading cluster\n");
3797205e407Swdenk 			return -1;
3807205e407Swdenk 		}
3817205e407Swdenk 		gotsize += (int)actsize;
3827205e407Swdenk 		filesize -= actsize;
3837205e407Swdenk 		buffer += actsize;
3847385c28eSWolfgang Denk 
3857205e407Swdenk 		curclust = get_fatent(mydata, endclust);
3868ce4e5c2Smichael 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
3877385c28eSWolfgang Denk 			debug("curclust: 0x%x\n", curclust);
3887385c28eSWolfgang Denk 			printf("Invalid FAT entry\n");
38971f95118Swdenk 			return gotsize;
39071f95118Swdenk 		}
3917205e407Swdenk 		actsize = bytesperclust;
3927205e407Swdenk 		endclust = curclust;
39371f95118Swdenk 	} while (1);
39471f95118Swdenk }
39571f95118Swdenk 
39671f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT
39771f95118Swdenk /*
39871f95118Swdenk  * Extract the file name information from 'slotptr' into 'l_name',
39971f95118Swdenk  * starting at l_name[*idx].
40071f95118Swdenk  * Return 1 if terminator (zero byte) is found, 0 otherwise.
40171f95118Swdenk  */
4027385c28eSWolfgang Denk static int slot2str (dir_slot *slotptr, char *l_name, int *idx)
40371f95118Swdenk {
40471f95118Swdenk 	int j;
40571f95118Swdenk 
40671f95118Swdenk 	for (j = 0; j <= 8; j += 2) {
40771f95118Swdenk 		l_name[*idx] = slotptr->name0_4[j];
4087385c28eSWolfgang Denk 		if (l_name[*idx] == 0x00)
4097385c28eSWolfgang Denk 			return 1;
41071f95118Swdenk 		(*idx)++;
41171f95118Swdenk 	}
41271f95118Swdenk 	for (j = 0; j <= 10; j += 2) {
41371f95118Swdenk 		l_name[*idx] = slotptr->name5_10[j];
4147385c28eSWolfgang Denk 		if (l_name[*idx] == 0x00)
4157385c28eSWolfgang Denk 			return 1;
41671f95118Swdenk 		(*idx)++;
41771f95118Swdenk 	}
41871f95118Swdenk 	for (j = 0; j <= 2; j += 2) {
41971f95118Swdenk 		l_name[*idx] = slotptr->name11_12[j];
4207385c28eSWolfgang Denk 		if (l_name[*idx] == 0x00)
4217385c28eSWolfgang Denk 			return 1;
42271f95118Swdenk 		(*idx)++;
42371f95118Swdenk 	}
42471f95118Swdenk 
42571f95118Swdenk 	return 0;
42671f95118Swdenk }
42771f95118Swdenk 
42871f95118Swdenk /*
42971f95118Swdenk  * Extract the full long filename starting at 'retdent' (which is really
43071f95118Swdenk  * a slot) into 'l_name'. If successful also copy the real directory entry
43171f95118Swdenk  * into 'retdent'
43271f95118Swdenk  * Return 0 on success, -1 otherwise.
43371f95118Swdenk  */
4347e4b9b4fSBryan Wu __attribute__ ((__aligned__ (__alignof__ (dir_entry))))
4355fa66df6Swdenk __u8 get_vfatname_block[MAX_CLUSTSIZE];
4367385c28eSWolfgang Denk 
43771f95118Swdenk static int
43871f95118Swdenk get_vfatname (fsdata *mydata, int curclust, __u8 *cluster,
43971f95118Swdenk 	      dir_entry *retdent, char *l_name)
44071f95118Swdenk {
44171f95118Swdenk 	dir_entry *realdent;
44271f95118Swdenk 	dir_slot *slotptr = (dir_slot *)retdent;
4433831530dSMikhail Zolotaryov 	__u8 *buflimit = cluster + ((curclust == 0) ?
4443831530dSMikhail Zolotaryov 					LINEAR_PREFETCH_SIZE :
445ac497771SSergei Shtylyov 					(mydata->clust_size * mydata->sect_size)
4463831530dSMikhail Zolotaryov 				   );
4472d1a537dSwdenk 	__u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff;
44871f95118Swdenk 	int idx = 0;
44971f95118Swdenk 
4503831530dSMikhail Zolotaryov 	if (counter > VFAT_MAXSEQ) {
4513831530dSMikhail Zolotaryov 		debug("Error: VFAT name is too long\n");
4523831530dSMikhail Zolotaryov 		return -1;
4533831530dSMikhail Zolotaryov 	}
4543831530dSMikhail Zolotaryov 
4553831530dSMikhail Zolotaryov 	while ((__u8 *)slotptr < buflimit) {
4567385c28eSWolfgang Denk 		if (counter == 0)
4577385c28eSWolfgang Denk 			break;
4582d1a537dSwdenk 		if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter)
4592d1a537dSwdenk 			return -1;
46071f95118Swdenk 		slotptr++;
46171f95118Swdenk 		counter--;
46271f95118Swdenk 	}
46371f95118Swdenk 
4643831530dSMikhail Zolotaryov 	if ((__u8 *)slotptr >= buflimit) {
46571f95118Swdenk 		dir_slot *slotptr2;
46671f95118Swdenk 
4673831530dSMikhail Zolotaryov 		if (curclust == 0)
4683831530dSMikhail Zolotaryov 			return -1;
46971f95118Swdenk 		curclust = get_fatent(mydata, curclust);
4708ce4e5c2Smichael 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
4717385c28eSWolfgang Denk 			debug("curclust: 0x%x\n", curclust);
4727385c28eSWolfgang Denk 			printf("Invalid FAT entry\n");
47371f95118Swdenk 			return -1;
47471f95118Swdenk 		}
4757385c28eSWolfgang Denk 
4765fa66df6Swdenk 		if (get_cluster(mydata, curclust, get_vfatname_block,
477ac497771SSergei Shtylyov 				mydata->clust_size * mydata->sect_size) != 0) {
4787385c28eSWolfgang Denk 			debug("Error: reading directory block\n");
47971f95118Swdenk 			return -1;
48071f95118Swdenk 		}
4817385c28eSWolfgang Denk 
4825fa66df6Swdenk 		slotptr2 = (dir_slot *)get_vfatname_block;
4833831530dSMikhail Zolotaryov 		while (counter > 0) {
4843831530dSMikhail Zolotaryov 			if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK)
4853831530dSMikhail Zolotaryov 			    & 0xff) != counter)
4863831530dSMikhail Zolotaryov 				return -1;
48771f95118Swdenk 			slotptr2++;
4883831530dSMikhail Zolotaryov 			counter--;
4893831530dSMikhail Zolotaryov 		}
4907385c28eSWolfgang Denk 
49171f95118Swdenk 		/* Save the real directory entry */
4923831530dSMikhail Zolotaryov 		realdent = (dir_entry *)slotptr2;
4933831530dSMikhail Zolotaryov 		while ((__u8 *)slotptr2 > get_vfatname_block) {
49471f95118Swdenk 			slotptr2--;
4953831530dSMikhail Zolotaryov 			slot2str(slotptr2, l_name, &idx);
49671f95118Swdenk 		}
49771f95118Swdenk 	} else {
49871f95118Swdenk 		/* Save the real directory entry */
49971f95118Swdenk 		realdent = (dir_entry *)slotptr;
50071f95118Swdenk 	}
50171f95118Swdenk 
50271f95118Swdenk 	do {
50371f95118Swdenk 		slotptr--;
5047385c28eSWolfgang Denk 		if (slot2str(slotptr, l_name, &idx))
5057385c28eSWolfgang Denk 			break;
5062d1a537dSwdenk 	} while (!(slotptr->id & LAST_LONG_ENTRY_MASK));
50771f95118Swdenk 
50871f95118Swdenk 	l_name[idx] = '\0';
5097385c28eSWolfgang Denk 	if (*l_name == DELETED_FLAG)
5107385c28eSWolfgang Denk 		*l_name = '\0';
5117385c28eSWolfgang Denk 	else if (*l_name == aRING)
5127385c28eSWolfgang Denk 		*l_name = DELETED_FLAG;
51371f95118Swdenk 	downcase(l_name);
51471f95118Swdenk 
51571f95118Swdenk 	/* Return the real directory entry */
51671f95118Swdenk 	memcpy(retdent, realdent, sizeof(dir_entry));
51771f95118Swdenk 
51871f95118Swdenk 	return 0;
51971f95118Swdenk }
52071f95118Swdenk 
52171f95118Swdenk /* Calculate short name checksum */
5227385c28eSWolfgang Denk static __u8 mkcksum (const char *str)
52371f95118Swdenk {
52471f95118Swdenk 	int i;
5257385c28eSWolfgang Denk 
52671f95118Swdenk 	__u8 ret = 0;
52771f95118Swdenk 
52871f95118Swdenk 	for (i = 0; i < 11; i++) {
52971f95118Swdenk 		ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + str[i];
53071f95118Swdenk 	}
53171f95118Swdenk 
53271f95118Swdenk 	return ret;
53371f95118Swdenk }
5347385c28eSWolfgang Denk #endif	/* CONFIG_SUPPORT_VFAT */
53571f95118Swdenk 
53671f95118Swdenk /*
53771f95118Swdenk  * Get the directory entry associated with 'filename' from the directory
53871f95118Swdenk  * starting at 'startsect'
53971f95118Swdenk  */
5407e4b9b4fSBryan Wu __attribute__ ((__aligned__ (__alignof__ (dir_entry))))
5415fa66df6Swdenk __u8 get_dentfromdir_block[MAX_CLUSTSIZE];
5427385c28eSWolfgang Denk 
54371f95118Swdenk static dir_entry *get_dentfromdir (fsdata *mydata, int startsect,
54471f95118Swdenk 				   char *filename, dir_entry *retdent,
54571f95118Swdenk 				   int dols)
54671f95118Swdenk {
54771f95118Swdenk 	__u16 prevcksum = 0xffff;
54871f95118Swdenk 	__u32 curclust = START(retdent);
54971f95118Swdenk 	int files = 0, dirs = 0;
55071f95118Swdenk 
5517385c28eSWolfgang Denk 	debug("get_dentfromdir: %s\n", filename);
5527385c28eSWolfgang Denk 
55371f95118Swdenk 	while (1) {
55471f95118Swdenk 		dir_entry *dentptr;
5557385c28eSWolfgang Denk 
55671f95118Swdenk 		int i;
55771f95118Swdenk 
5585fa66df6Swdenk 		if (get_cluster(mydata, curclust, get_dentfromdir_block,
559ac497771SSergei Shtylyov 				mydata->clust_size * mydata->sect_size) != 0) {
5607385c28eSWolfgang Denk 			debug("Error: reading directory block\n");
56171f95118Swdenk 			return NULL;
56271f95118Swdenk 		}
5637385c28eSWolfgang Denk 
5645fa66df6Swdenk 		dentptr = (dir_entry *)get_dentfromdir_block;
5657385c28eSWolfgang Denk 
56671f95118Swdenk 		for (i = 0; i < DIRENTSPERCLUST; i++) {
5673831530dSMikhail Zolotaryov 			char s_name[14], l_name[VFAT_MAXLEN_BYTES];
56871f95118Swdenk 
56971f95118Swdenk 			l_name[0] = '\0';
570855a496fSwdenk 			if (dentptr->name[0] == DELETED_FLAG) {
571855a496fSwdenk 				dentptr++;
572855a496fSwdenk 				continue;
573855a496fSwdenk 			}
57471f95118Swdenk 			if ((dentptr->attr & ATTR_VOLUME)) {
57571f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT
57671f95118Swdenk 				if ((dentptr->attr & ATTR_VFAT) &&
5772d1a537dSwdenk 				    (dentptr-> name[0] & LAST_LONG_ENTRY_MASK)) {
5787385c28eSWolfgang Denk 					prevcksum = ((dir_slot *)dentptr)->alias_checksum;
5797385c28eSWolfgang Denk 					get_vfatname(mydata, curclust,
5807385c28eSWolfgang Denk 						     get_dentfromdir_block,
58171f95118Swdenk 						     dentptr, l_name);
58271f95118Swdenk 					if (dols) {
5837385c28eSWolfgang Denk 						int isdir;
58471f95118Swdenk 						char dirc;
58571f95118Swdenk 						int doit = 0;
58671f95118Swdenk 
5877385c28eSWolfgang Denk 						isdir = (dentptr->attr & ATTR_DIR);
5887385c28eSWolfgang Denk 
58971f95118Swdenk 						if (isdir) {
59071f95118Swdenk 							dirs++;
59171f95118Swdenk 							dirc = '/';
59271f95118Swdenk 							doit = 1;
59371f95118Swdenk 						} else {
59471f95118Swdenk 							dirc = ' ';
59571f95118Swdenk 							if (l_name[0] != 0) {
59671f95118Swdenk 								files++;
59771f95118Swdenk 								doit = 1;
59871f95118Swdenk 							}
59971f95118Swdenk 						}
60071f95118Swdenk 						if (doit) {
60171f95118Swdenk 							if (dirc == ' ') {
60271f95118Swdenk 								printf(" %8ld   %s%c\n",
60371f95118Swdenk 									(long)FAT2CPU32(dentptr->size),
6047385c28eSWolfgang Denk 									l_name,
6057385c28eSWolfgang Denk 									dirc);
60671f95118Swdenk 							} else {
6077385c28eSWolfgang Denk 								printf("            %s%c\n",
6087385c28eSWolfgang Denk 									l_name,
6097385c28eSWolfgang Denk 									dirc);
61071f95118Swdenk 							}
61171f95118Swdenk 						}
61271f95118Swdenk 						dentptr++;
61371f95118Swdenk 						continue;
61471f95118Swdenk 					}
6157385c28eSWolfgang Denk 					debug("vfatname: |%s|\n", l_name);
61671f95118Swdenk 				} else
61771f95118Swdenk #endif
61871f95118Swdenk 				{
61971f95118Swdenk 					/* Volume label or VFAT entry */
62071f95118Swdenk 					dentptr++;
62171f95118Swdenk 					continue;
62271f95118Swdenk 				}
62371f95118Swdenk 			}
62471f95118Swdenk 			if (dentptr->name[0] == 0) {
62571f95118Swdenk 				if (dols) {
6267385c28eSWolfgang Denk 					printf("\n%d file(s), %d dir(s)\n\n",
6277385c28eSWolfgang Denk 						files, dirs);
62871f95118Swdenk 				}
6297385c28eSWolfgang Denk 				debug("Dentname == NULL - %d\n", i);
63071f95118Swdenk 				return NULL;
63171f95118Swdenk 			}
63271f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT
63371f95118Swdenk 			if (dols && mkcksum(dentptr->name) == prevcksum) {
63471f95118Swdenk 				dentptr++;
63571f95118Swdenk 				continue;
63671f95118Swdenk 			}
63771f95118Swdenk #endif
63871f95118Swdenk 			get_name(dentptr, s_name);
63971f95118Swdenk 			if (dols) {
64071f95118Swdenk 				int isdir = (dentptr->attr & ATTR_DIR);
64171f95118Swdenk 				char dirc;
64271f95118Swdenk 				int doit = 0;
64371f95118Swdenk 
64471f95118Swdenk 				if (isdir) {
64571f95118Swdenk 					dirs++;
64671f95118Swdenk 					dirc = '/';
64771f95118Swdenk 					doit = 1;
64871f95118Swdenk 				} else {
64971f95118Swdenk 					dirc = ' ';
65071f95118Swdenk 					if (s_name[0] != 0) {
65171f95118Swdenk 						files++;
65271f95118Swdenk 						doit = 1;
65371f95118Swdenk 					}
65471f95118Swdenk 				}
6557385c28eSWolfgang Denk 
65671f95118Swdenk 				if (doit) {
65771f95118Swdenk 					if (dirc == ' ') {
65871f95118Swdenk 						printf(" %8ld   %s%c\n",
6597385c28eSWolfgang Denk 							(long)FAT2CPU32(dentptr->size),
6607385c28eSWolfgang Denk 							s_name, dirc);
66171f95118Swdenk 					} else {
6627385c28eSWolfgang Denk 						printf("            %s%c\n",
6637385c28eSWolfgang Denk 							s_name, dirc);
66471f95118Swdenk 					}
66571f95118Swdenk 				}
6667385c28eSWolfgang Denk 
66771f95118Swdenk 				dentptr++;
66871f95118Swdenk 				continue;
66971f95118Swdenk 			}
6707385c28eSWolfgang Denk 
6717385c28eSWolfgang Denk 			if (strcmp(filename, s_name)
6727385c28eSWolfgang Denk 			    && strcmp(filename, l_name)) {
6737385c28eSWolfgang Denk 				debug("Mismatch: |%s|%s|\n", s_name, l_name);
67471f95118Swdenk 				dentptr++;
67571f95118Swdenk 				continue;
67671f95118Swdenk 			}
6777385c28eSWolfgang Denk 
67871f95118Swdenk 			memcpy(retdent, dentptr, sizeof(dir_entry));
67971f95118Swdenk 
6807385c28eSWolfgang Denk 			debug("DentName: %s", s_name);
6817385c28eSWolfgang Denk 			debug(", start: 0x%x", START(dentptr));
6827385c28eSWolfgang Denk 			debug(", size:  0x%x %s\n",
68371f95118Swdenk 			      FAT2CPU32(dentptr->size),
68471f95118Swdenk 			      (dentptr->attr & ATTR_DIR) ? "(DIR)" : "");
68571f95118Swdenk 
68671f95118Swdenk 			return retdent;
68771f95118Swdenk 		}
6887385c28eSWolfgang Denk 
68971f95118Swdenk 		curclust = get_fatent(mydata, curclust);
6908ce4e5c2Smichael 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
6917385c28eSWolfgang Denk 			debug("curclust: 0x%x\n", curclust);
6927385c28eSWolfgang Denk 			printf("Invalid FAT entry\n");
69371f95118Swdenk 			return NULL;
69471f95118Swdenk 		}
69571f95118Swdenk 	}
69671f95118Swdenk 
69771f95118Swdenk 	return NULL;
69871f95118Swdenk }
69971f95118Swdenk 
70071f95118Swdenk /*
70171f95118Swdenk  * Read boot sector and volume info from a FAT filesystem
70271f95118Swdenk  */
70371f95118Swdenk static int
70471f95118Swdenk read_bootsectandvi (boot_sector *bs, volume_info *volinfo, int *fatsize)
70571f95118Swdenk {
706ac497771SSergei Shtylyov 	__u8 *block;
70771f95118Swdenk 	volume_info *vistart;
708ac497771SSergei Shtylyov 	int ret = 0;
709ac497771SSergei Shtylyov 
710ac497771SSergei Shtylyov 	if (cur_dev == NULL) {
711ac497771SSergei Shtylyov 		debug("Error: no device selected\n");
712ac497771SSergei Shtylyov 		return -1;
713ac497771SSergei Shtylyov 	}
714ac497771SSergei Shtylyov 
715ac497771SSergei Shtylyov 	block = malloc(cur_dev->blksz);
716ac497771SSergei Shtylyov 	if (block == NULL) {
717ac497771SSergei Shtylyov 		debug("Error: allocating block\n");
718ac497771SSergei Shtylyov 		return -1;
719ac497771SSergei Shtylyov 	}
72071f95118Swdenk 
72171f95118Swdenk 	if (disk_read (0, 1, block) < 0) {
7227385c28eSWolfgang Denk 		debug("Error: reading block\n");
723ac497771SSergei Shtylyov 		goto fail;
72471f95118Swdenk 	}
72571f95118Swdenk 
72671f95118Swdenk 	memcpy(bs, block, sizeof(boot_sector));
72771f95118Swdenk 	bs->reserved = FAT2CPU16(bs->reserved);
72871f95118Swdenk 	bs->fat_length = FAT2CPU16(bs->fat_length);
72971f95118Swdenk 	bs->secs_track = FAT2CPU16(bs->secs_track);
73071f95118Swdenk 	bs->heads = FAT2CPU16(bs->heads);
73171f95118Swdenk 	bs->total_sect = FAT2CPU32(bs->total_sect);
73271f95118Swdenk 
73371f95118Swdenk 	/* FAT32 entries */
73471f95118Swdenk 	if (bs->fat_length == 0) {
73571f95118Swdenk 		/* Assume FAT32 */
73671f95118Swdenk 		bs->fat32_length = FAT2CPU32(bs->fat32_length);
73771f95118Swdenk 		bs->flags = FAT2CPU16(bs->flags);
73871f95118Swdenk 		bs->root_cluster = FAT2CPU32(bs->root_cluster);
73971f95118Swdenk 		bs->info_sector = FAT2CPU16(bs->info_sector);
74071f95118Swdenk 		bs->backup_boot = FAT2CPU16(bs->backup_boot);
74171f95118Swdenk 		vistart = (volume_info *)(block + sizeof(boot_sector));
74271f95118Swdenk 		*fatsize = 32;
74371f95118Swdenk 	} else {
74471f95118Swdenk 		vistart = (volume_info *)&(bs->fat32_length);
74571f95118Swdenk 		*fatsize = 0;
74671f95118Swdenk 	}
74771f95118Swdenk 	memcpy(volinfo, vistart, sizeof(volume_info));
74871f95118Swdenk 
74971f95118Swdenk 	if (*fatsize == 32) {
7507385c28eSWolfgang Denk 		if (strncmp(FAT32_SIGN, vistart->fs_type, SIGNLEN) == 0)
751ac497771SSergei Shtylyov 			goto exit;
75271f95118Swdenk 	} else {
753651351feSTom Rix 		if (strncmp(FAT12_SIGN, vistart->fs_type, SIGNLEN) == 0) {
75471f95118Swdenk 			*fatsize = 12;
755ac497771SSergei Shtylyov 			goto exit;
75671f95118Swdenk 		}
757651351feSTom Rix 		if (strncmp(FAT16_SIGN, vistart->fs_type, SIGNLEN) == 0) {
75871f95118Swdenk 			*fatsize = 16;
759ac497771SSergei Shtylyov 			goto exit;
76071f95118Swdenk 		}
76171f95118Swdenk 	}
76271f95118Swdenk 
7637385c28eSWolfgang Denk 	debug("Error: broken fs_type sign\n");
764ac497771SSergei Shtylyov fail:
765ac497771SSergei Shtylyov 	ret = -1;
766ac497771SSergei Shtylyov exit:
767ac497771SSergei Shtylyov 	free(block);
768ac497771SSergei Shtylyov 	return ret;
76971f95118Swdenk }
77071f95118Swdenk 
7717e4b9b4fSBryan Wu __attribute__ ((__aligned__ (__alignof__ (dir_entry))))
7727e4b9b4fSBryan Wu __u8 do_fat_read_block[MAX_CLUSTSIZE];
7737385c28eSWolfgang Denk 
77420cc00ddSstroese long
77571f95118Swdenk do_fat_read (const char *filename, void *buffer, unsigned long maxsize,
77671f95118Swdenk 	     int dols)
77771f95118Swdenk {
77871f95118Swdenk 	char fnamecopy[2048];
77971f95118Swdenk 	boot_sector bs;
78071f95118Swdenk 	volume_info volinfo;
78171f95118Swdenk 	fsdata datablock;
78271f95118Swdenk 	fsdata *mydata = &datablock;
78371f95118Swdenk 	dir_entry *dentptr;
78471f95118Swdenk 	__u16 prevcksum = 0xffff;
78571f95118Swdenk 	char *subname = "";
7863f270f42SErik Hansen 	__u32 cursect;
78771f95118Swdenk 	int idx, isdir = 0;
78871f95118Swdenk 	int files = 0, dirs = 0;
789ac497771SSergei Shtylyov 	long ret = -1;
79071f95118Swdenk 	int firsttime;
7913f270f42SErik Hansen 	__u32 root_cluster;
7923f270f42SErik Hansen 	int rootdir_size = 0;
7932aa98c66SWolfgang Denk 	int j;
79471f95118Swdenk 
79571f95118Swdenk 	if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) {
7967385c28eSWolfgang Denk 		debug("Error: reading boot sector\n");
79771f95118Swdenk 		return -1;
79871f95118Swdenk 	}
7997385c28eSWolfgang Denk 
8002aa98c66SWolfgang Denk 	root_cluster = bs.root_cluster;
8017385c28eSWolfgang Denk 
8027385c28eSWolfgang Denk 	if (mydata->fatsize == 32)
80371f95118Swdenk 		mydata->fatlength = bs.fat32_length;
8047385c28eSWolfgang Denk 	else
80571f95118Swdenk 		mydata->fatlength = bs.fat_length;
8067385c28eSWolfgang Denk 
80771f95118Swdenk 	mydata->fat_sect = bs.reserved;
8087385c28eSWolfgang Denk 
80971f95118Swdenk 	cursect = mydata->rootdir_sect
81071f95118Swdenk 		= mydata->fat_sect + mydata->fatlength * bs.fats;
8117385c28eSWolfgang Denk 
812ac497771SSergei Shtylyov 	mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0];
81371f95118Swdenk 	mydata->clust_size = bs.cluster_size;
8147385c28eSWolfgang Denk 
81571f95118Swdenk 	if (mydata->fatsize == 32) {
8167385c28eSWolfgang Denk 		mydata->data_begin = mydata->rootdir_sect -
8177385c28eSWolfgang Denk 					(mydata->clust_size * 2);
81871f95118Swdenk 	} else {
8197385c28eSWolfgang Denk 		rootdir_size = ((bs.dir_entries[1]  * (int)256 +
8207385c28eSWolfgang Denk 				 bs.dir_entries[0]) *
8217385c28eSWolfgang Denk 				 sizeof(dir_entry)) /
822ac497771SSergei Shtylyov 				 mydata->sect_size;
8237385c28eSWolfgang Denk 		mydata->data_begin = mydata->rootdir_sect +
8247385c28eSWolfgang Denk 					rootdir_size -
8257385c28eSWolfgang Denk 					(mydata->clust_size * 2);
82671f95118Swdenk 	}
8277385c28eSWolfgang Denk 
82871f95118Swdenk 	mydata->fatbufnum = -1;
829ac497771SSergei Shtylyov 	mydata->fatbuf = malloc(FATBUFSIZE);
830ac497771SSergei Shtylyov 	if (mydata->fatbuf == NULL) {
831ac497771SSergei Shtylyov 		debug("Error: allocating memory\n");
832ac497771SSergei Shtylyov 		return -1;
833ac497771SSergei Shtylyov 	}
83471f95118Swdenk 
8352aa98c66SWolfgang Denk #ifdef CONFIG_SUPPORT_VFAT
8367385c28eSWolfgang Denk 	debug("VFAT Support enabled\n");
8372aa98c66SWolfgang Denk #endif
8387385c28eSWolfgang Denk 	debug("FAT%d, fat_sect: %d, fatlength: %d\n",
8397385c28eSWolfgang Denk 	       mydata->fatsize, mydata->fat_sect, mydata->fatlength);
8407385c28eSWolfgang Denk 	debug("Rootdir begins at cluster: %d, sector: %d, offset: %x\n"
84171f95118Swdenk 	       "Data begins at: %d\n",
8422aa98c66SWolfgang Denk 	       root_cluster,
8432aa98c66SWolfgang Denk 	       mydata->rootdir_sect,
844ac497771SSergei Shtylyov 	       mydata->rootdir_sect * mydata->sect_size, mydata->data_begin);
845ac497771SSergei Shtylyov 	debug("Sector size: %d, cluster size: %d\n", mydata->sect_size,
846ac497771SSergei Shtylyov 	      mydata->clust_size);
84771f95118Swdenk 
84871f95118Swdenk 	/* "cwd" is always the root... */
84971f95118Swdenk 	while (ISDIRDELIM(*filename))
85071f95118Swdenk 		filename++;
8517385c28eSWolfgang Denk 
85271f95118Swdenk 	/* Make a copy of the filename and convert it to lowercase */
85371f95118Swdenk 	strcpy(fnamecopy, filename);
85471f95118Swdenk 	downcase(fnamecopy);
8557385c28eSWolfgang Denk 
85671f95118Swdenk 	if (*fnamecopy == '\0') {
85771f95118Swdenk 		if (!dols)
858ac497771SSergei Shtylyov 			goto exit;
8597385c28eSWolfgang Denk 
86071f95118Swdenk 		dols = LS_ROOT;
86171f95118Swdenk 	} else if ((idx = dirdelim(fnamecopy)) >= 0) {
86271f95118Swdenk 		isdir = 1;
86371f95118Swdenk 		fnamecopy[idx] = '\0';
86471f95118Swdenk 		subname = fnamecopy + idx + 1;
8657385c28eSWolfgang Denk 
86671f95118Swdenk 		/* Handle multiple delimiters */
86771f95118Swdenk 		while (ISDIRDELIM(*subname))
86871f95118Swdenk 			subname++;
86971f95118Swdenk 	} else if (dols) {
87071f95118Swdenk 		isdir = 1;
87171f95118Swdenk 	}
87271f95118Swdenk 
8732aa98c66SWolfgang Denk 	j = 0;
87471f95118Swdenk 	while (1) {
87571f95118Swdenk 		int i;
87671f95118Swdenk 
8777385c28eSWolfgang Denk 		debug("FAT read sect=%d, clust_size=%d, DIRENTSPERBLOCK=%d\n",
8782aa98c66SWolfgang Denk 			cursect, mydata->clust_size, DIRENTSPERBLOCK);
8797385c28eSWolfgang Denk 
8803831530dSMikhail Zolotaryov 		if (disk_read(cursect,
8813831530dSMikhail Zolotaryov 				(mydata->fatsize == 32) ?
8823831530dSMikhail Zolotaryov 				(mydata->clust_size) :
883ac497771SSergei Shtylyov 				LINEAR_PREFETCH_SIZE / mydata->sect_size,
8843831530dSMikhail Zolotaryov 				do_fat_read_block) < 0) {
8857385c28eSWolfgang Denk 			debug("Error: reading rootdir block\n");
886ac497771SSergei Shtylyov 			goto exit;
88771f95118Swdenk 		}
8887385c28eSWolfgang Denk 
8895fa66df6Swdenk 		dentptr = (dir_entry *) do_fat_read_block;
8907385c28eSWolfgang Denk 
89171f95118Swdenk 		for (i = 0; i < DIRENTSPERBLOCK; i++) {
8923831530dSMikhail Zolotaryov 			char s_name[14], l_name[VFAT_MAXLEN_BYTES];
89371f95118Swdenk 
89471f95118Swdenk 			l_name[0] = '\0';
8953831530dSMikhail Zolotaryov 			if (dentptr->name[0] == DELETED_FLAG) {
8963831530dSMikhail Zolotaryov 				dentptr++;
8973831530dSMikhail Zolotaryov 				continue;
8983831530dSMikhail Zolotaryov 			}
89971f95118Swdenk 			if ((dentptr->attr & ATTR_VOLUME)) {
90071f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT
90171f95118Swdenk 				if ((dentptr->attr & ATTR_VFAT) &&
9022d1a537dSwdenk 				    (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
9037385c28eSWolfgang Denk 					prevcksum =
9047385c28eSWolfgang Denk 						((dir_slot *)dentptr)->alias_checksum;
9057385c28eSWolfgang Denk 
9063831530dSMikhail Zolotaryov 					get_vfatname(mydata,
9073831530dSMikhail Zolotaryov 						     (mydata->fatsize == 32) ?
9083831530dSMikhail Zolotaryov 						     root_cluster :
9093831530dSMikhail Zolotaryov 						     0,
9107385c28eSWolfgang Denk 						     do_fat_read_block,
9117385c28eSWolfgang Denk 						     dentptr, l_name);
9127385c28eSWolfgang Denk 
91371f95118Swdenk 					if (dols == LS_ROOT) {
91471f95118Swdenk 						char dirc;
91571f95118Swdenk 						int doit = 0;
9167385c28eSWolfgang Denk 						int isdir =
9177385c28eSWolfgang Denk 							(dentptr->attr & ATTR_DIR);
91871f95118Swdenk 
91971f95118Swdenk 						if (isdir) {
92071f95118Swdenk 							dirs++;
92171f95118Swdenk 							dirc = '/';
92271f95118Swdenk 							doit = 1;
92371f95118Swdenk 						} else {
92471f95118Swdenk 							dirc = ' ';
92571f95118Swdenk 							if (l_name[0] != 0) {
92671f95118Swdenk 								files++;
92771f95118Swdenk 								doit = 1;
92871f95118Swdenk 							}
92971f95118Swdenk 						}
93071f95118Swdenk 						if (doit) {
93171f95118Swdenk 							if (dirc == ' ') {
93271f95118Swdenk 								printf(" %8ld   %s%c\n",
93371f95118Swdenk 									(long)FAT2CPU32(dentptr->size),
9347385c28eSWolfgang Denk 									l_name,
9357385c28eSWolfgang Denk 									dirc);
93671f95118Swdenk 							} else {
9377385c28eSWolfgang Denk 								printf("            %s%c\n",
9387385c28eSWolfgang Denk 									l_name,
9397385c28eSWolfgang Denk 									dirc);
94071f95118Swdenk 							}
94171f95118Swdenk 						}
94271f95118Swdenk 						dentptr++;
94371f95118Swdenk 						continue;
94471f95118Swdenk 					}
9457385c28eSWolfgang Denk 					debug("Rootvfatname: |%s|\n",
9467385c28eSWolfgang Denk 					       l_name);
94771f95118Swdenk 				} else
94871f95118Swdenk #endif
94971f95118Swdenk 				{
95071f95118Swdenk 					/* Volume label or VFAT entry */
95171f95118Swdenk 					dentptr++;
95271f95118Swdenk 					continue;
95371f95118Swdenk 				}
95471f95118Swdenk 			} else if (dentptr->name[0] == 0) {
9557385c28eSWolfgang Denk 				debug("RootDentname == NULL - %d\n", i);
95671f95118Swdenk 				if (dols == LS_ROOT) {
9577385c28eSWolfgang Denk 					printf("\n%d file(s), %d dir(s)\n\n",
9587385c28eSWolfgang Denk 						files, dirs);
959ac497771SSergei Shtylyov 					ret = 0;
96071f95118Swdenk 				}
961ac497771SSergei Shtylyov 				goto exit;
96271f95118Swdenk 			}
96371f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT
9647385c28eSWolfgang Denk 			else if (dols == LS_ROOT &&
9657385c28eSWolfgang Denk 				 mkcksum(dentptr->name) == prevcksum) {
96671f95118Swdenk 				dentptr++;
96771f95118Swdenk 				continue;
96871f95118Swdenk 			}
96971f95118Swdenk #endif
97071f95118Swdenk 			get_name(dentptr, s_name);
9717385c28eSWolfgang Denk 
97271f95118Swdenk 			if (dols == LS_ROOT) {
97371f95118Swdenk 				int isdir = (dentptr->attr & ATTR_DIR);
97471f95118Swdenk 				char dirc;
97571f95118Swdenk 				int doit = 0;
97671f95118Swdenk 
97771f95118Swdenk 				if (isdir) {
97871f95118Swdenk 					dirc = '/';
979a43278a4Swdenk 					if (s_name[0] != 0) {
980a43278a4Swdenk 						dirs++;
98171f95118Swdenk 						doit = 1;
982a43278a4Swdenk 					}
98371f95118Swdenk 				} else {
98471f95118Swdenk 					dirc = ' ';
98571f95118Swdenk 					if (s_name[0] != 0) {
98671f95118Swdenk 						files++;
98771f95118Swdenk 						doit = 1;
98871f95118Swdenk 					}
98971f95118Swdenk 				}
99071f95118Swdenk 				if (doit) {
99171f95118Swdenk 					if (dirc == ' ') {
99271f95118Swdenk 						printf(" %8ld   %s%c\n",
9937385c28eSWolfgang Denk 							(long)FAT2CPU32(dentptr->size),
9947385c28eSWolfgang Denk 							s_name, dirc);
99571f95118Swdenk 					} else {
9967385c28eSWolfgang Denk 						printf("            %s%c\n",
9977385c28eSWolfgang Denk 							s_name, dirc);
99871f95118Swdenk 					}
99971f95118Swdenk 				}
100071f95118Swdenk 				dentptr++;
100171f95118Swdenk 				continue;
100271f95118Swdenk 			}
10037385c28eSWolfgang Denk 
10047385c28eSWolfgang Denk 			if (strcmp(fnamecopy, s_name)
10057385c28eSWolfgang Denk 			    && strcmp(fnamecopy, l_name)) {
10067385c28eSWolfgang Denk 				debug("RootMismatch: |%s|%s|\n", s_name,
10077385c28eSWolfgang Denk 				       l_name);
100871f95118Swdenk 				dentptr++;
100971f95118Swdenk 				continue;
101071f95118Swdenk 			}
10117385c28eSWolfgang Denk 
101271f95118Swdenk 			if (isdir && !(dentptr->attr & ATTR_DIR))
1013ac497771SSergei Shtylyov 				goto exit;
101471f95118Swdenk 
10157385c28eSWolfgang Denk 			debug("RootName: %s", s_name);
10167385c28eSWolfgang Denk 			debug(", start: 0x%x", START(dentptr));
10177385c28eSWolfgang Denk 			debug(", size:  0x%x %s\n",
10187385c28eSWolfgang Denk 			       FAT2CPU32(dentptr->size),
10197385c28eSWolfgang Denk 			       isdir ? "(DIR)" : "");
102071f95118Swdenk 
102171f95118Swdenk 			goto rootdir_done;	/* We got a match */
102271f95118Swdenk 		}
10237385c28eSWolfgang Denk 		debug("END LOOP: j=%d   clust_size=%d\n", j,
10247385c28eSWolfgang Denk 		       mydata->clust_size);
10252aa98c66SWolfgang Denk 
10262aa98c66SWolfgang Denk 		/*
10272aa98c66SWolfgang Denk 		 * On FAT32 we must fetch the FAT entries for the next
10282aa98c66SWolfgang Denk 		 * root directory clusters when a cluster has been
10292aa98c66SWolfgang Denk 		 * completely processed.
10302aa98c66SWolfgang Denk 		 */
10313f270f42SErik Hansen 		++j;
10323f270f42SErik Hansen 		int fat32_end = 0;
10333f270f42SErik Hansen 		if ((mydata->fatsize == 32) && (j == mydata->clust_size)) {
10343f270f42SErik Hansen 			int nxtsect = 0;
10353f270f42SErik Hansen 			int nxt_clust = 0;
10362aa98c66SWolfgang Denk 
10372aa98c66SWolfgang Denk 			nxt_clust = get_fatent(mydata, root_cluster);
10383f270f42SErik Hansen 			fat32_end = CHECK_CLUST(nxt_clust, 32);
10397385c28eSWolfgang Denk 
10407385c28eSWolfgang Denk 			nxtsect = mydata->data_begin +
10417385c28eSWolfgang Denk 				(nxt_clust * mydata->clust_size);
10427385c28eSWolfgang Denk 
10432aa98c66SWolfgang Denk 			root_cluster = nxt_clust;
10442aa98c66SWolfgang Denk 
10452aa98c66SWolfgang Denk 			cursect = nxtsect;
10462aa98c66SWolfgang Denk 			j = 0;
10472aa98c66SWolfgang Denk 		} else {
104871f95118Swdenk 			cursect++;
104971f95118Swdenk 		}
10503f270f42SErik Hansen 
10513f270f42SErik Hansen 		/* If end of rootdir reached */
10523f270f42SErik Hansen 		if ((mydata->fatsize == 32 && fat32_end) ||
10533f270f42SErik Hansen 		    (mydata->fatsize != 32 && j == rootdir_size)) {
10543f270f42SErik Hansen 			if (dols == LS_ROOT) {
10553f270f42SErik Hansen 				printf("\n%d file(s), %d dir(s)\n\n",
10563f270f42SErik Hansen 				       files, dirs);
1057ac497771SSergei Shtylyov 				ret = 0;
10583f270f42SErik Hansen 			}
1059ac497771SSergei Shtylyov 			goto exit;
10603f270f42SErik Hansen 		}
10612aa98c66SWolfgang Denk 	}
106271f95118Swdenk rootdir_done:
106371f95118Swdenk 
106471f95118Swdenk 	firsttime = 1;
10657385c28eSWolfgang Denk 
106671f95118Swdenk 	while (isdir) {
106771f95118Swdenk 		int startsect = mydata->data_begin
106871f95118Swdenk 			+ START(dentptr) * mydata->clust_size;
106971f95118Swdenk 		dir_entry dent;
107071f95118Swdenk 		char *nextname = NULL;
107171f95118Swdenk 
107271f95118Swdenk 		dent = *dentptr;
107371f95118Swdenk 		dentptr = &dent;
107471f95118Swdenk 
107571f95118Swdenk 		idx = dirdelim(subname);
10767385c28eSWolfgang Denk 
107771f95118Swdenk 		if (idx >= 0) {
107871f95118Swdenk 			subname[idx] = '\0';
107971f95118Swdenk 			nextname = subname + idx + 1;
108071f95118Swdenk 			/* Handle multiple delimiters */
108171f95118Swdenk 			while (ISDIRDELIM(*nextname))
108271f95118Swdenk 				nextname++;
108371f95118Swdenk 			if (dols && *nextname == '\0')
108471f95118Swdenk 				firsttime = 0;
108571f95118Swdenk 		} else {
108671f95118Swdenk 			if (dols && firsttime) {
108771f95118Swdenk 				firsttime = 0;
108871f95118Swdenk 			} else {
108971f95118Swdenk 				isdir = 0;
109071f95118Swdenk 			}
109171f95118Swdenk 		}
109271f95118Swdenk 
109371f95118Swdenk 		if (get_dentfromdir(mydata, startsect, subname, dentptr,
109471f95118Swdenk 				     isdir ? 0 : dols) == NULL) {
109571f95118Swdenk 			if (dols && !isdir)
1096ac497771SSergei Shtylyov 				ret = 0;
1097ac497771SSergei Shtylyov 			goto exit;
109871f95118Swdenk 		}
109971f95118Swdenk 
110071f95118Swdenk 		if (idx >= 0) {
110171f95118Swdenk 			if (!(dentptr->attr & ATTR_DIR))
1102ac497771SSergei Shtylyov 				goto exit;
110371f95118Swdenk 			subname = nextname;
110471f95118Swdenk 		}
110571f95118Swdenk 	}
11067385c28eSWolfgang Denk 
110771f95118Swdenk 	ret = get_contents(mydata, dentptr, buffer, maxsize);
11087385c28eSWolfgang Denk 	debug("Size: %d, got: %ld\n", FAT2CPU32(dentptr->size), ret);
110971f95118Swdenk 
1110ac497771SSergei Shtylyov exit:
1111ac497771SSergei Shtylyov 	free(mydata->fatbuf);
111271f95118Swdenk 	return ret;
111371f95118Swdenk }
111471f95118Swdenk 
11157385c28eSWolfgang Denk int file_fat_detectfs (void)
111671f95118Swdenk {
111771f95118Swdenk 	boot_sector bs;
111871f95118Swdenk 	volume_info volinfo;
111971f95118Swdenk 	int fatsize;
11207205e407Swdenk 	char vol_label[12];
112171f95118Swdenk 
11227205e407Swdenk 	if (cur_dev == NULL) {
11237205e407Swdenk 		printf("No current device\n");
11247205e407Swdenk 		return 1;
11257205e407Swdenk 	}
11267385c28eSWolfgang Denk 
1127dd60d122SJon Loeliger #if defined(CONFIG_CMD_IDE) || \
112875eb82ecSunsik Kim     defined(CONFIG_CMD_MG_DISK) || \
11298c5170a7SSonic Zhang     defined(CONFIG_CMD_SATA) || \
1130dd60d122SJon Loeliger     defined(CONFIG_CMD_SCSI) || \
1131dd60d122SJon Loeliger     defined(CONFIG_CMD_USB) || \
113221f6f963SAndy Fleming     defined(CONFIG_MMC)
11337205e407Swdenk 	printf("Interface:  ");
11347205e407Swdenk 	switch (cur_dev->if_type) {
11357385c28eSWolfgang Denk 	case IF_TYPE_IDE:
11367385c28eSWolfgang Denk 		printf("IDE");
11377385c28eSWolfgang Denk 		break;
11387385c28eSWolfgang Denk 	case IF_TYPE_SATA:
11397385c28eSWolfgang Denk 		printf("SATA");
11407385c28eSWolfgang Denk 		break;
11417385c28eSWolfgang Denk 	case IF_TYPE_SCSI:
11427385c28eSWolfgang Denk 		printf("SCSI");
11437385c28eSWolfgang Denk 		break;
11447385c28eSWolfgang Denk 	case IF_TYPE_ATAPI:
11457385c28eSWolfgang Denk 		printf("ATAPI");
11467385c28eSWolfgang Denk 		break;
11477385c28eSWolfgang Denk 	case IF_TYPE_USB:
11487385c28eSWolfgang Denk 		printf("USB");
11497385c28eSWolfgang Denk 		break;
11507385c28eSWolfgang Denk 	case IF_TYPE_DOC:
11517385c28eSWolfgang Denk 		printf("DOC");
11527385c28eSWolfgang Denk 		break;
11537385c28eSWolfgang Denk 	case IF_TYPE_MMC:
11547385c28eSWolfgang Denk 		printf("MMC");
11557385c28eSWolfgang Denk 		break;
11567385c28eSWolfgang Denk 	default:
11577385c28eSWolfgang Denk 		printf("Unknown");
11587205e407Swdenk 	}
11597385c28eSWolfgang Denk 
11607205e407Swdenk 	printf("\n  Device %d: ", cur_dev->dev);
11617205e407Swdenk 	dev_print(cur_dev);
11627205e407Swdenk #endif
11637385c28eSWolfgang Denk 
11647205e407Swdenk 	if (read_bootsectandvi(&bs, &volinfo, &fatsize)) {
11657205e407Swdenk 		printf("\nNo valid FAT fs found\n");
11667205e407Swdenk 		return 1;
11677205e407Swdenk 	}
11687385c28eSWolfgang Denk 
11697205e407Swdenk 	memcpy(vol_label, volinfo.volume_label, 11);
11707205e407Swdenk 	vol_label[11] = '\0';
11717205e407Swdenk 	volinfo.fs_type[5] = '\0';
11727385c28eSWolfgang Denk 
11737385c28eSWolfgang Denk 	printf("Partition %d: Filesystem: %s \"%s\"\n", cur_part,
11747385c28eSWolfgang Denk 		volinfo.fs_type, vol_label);
11757385c28eSWolfgang Denk 
11767205e407Swdenk 	return 0;
117771f95118Swdenk }
117871f95118Swdenk 
11797385c28eSWolfgang Denk int file_fat_ls (const char *dir)
118071f95118Swdenk {
118171f95118Swdenk 	return do_fat_read(dir, NULL, 0, LS_YES);
118271f95118Swdenk }
118371f95118Swdenk 
11847385c28eSWolfgang Denk long file_fat_read (const char *filename, void *buffer, unsigned long maxsize)
118571f95118Swdenk {
11867205e407Swdenk 	printf("reading %s\n", filename);
118771f95118Swdenk 	return do_fat_read(filename, buffer, maxsize, LS_NO);
118871f95118Swdenk }
1189