xref: /rk3399_rockchip-uboot/fs/fat/fat.c (revision 75eb82ec7cacb18d059d701b35677b93d2bb7596)
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>
3071f95118Swdenk #include <fat.h>
3171f95118Swdenk #include <asm/byteorder.h>
327205e407Swdenk #include <part.h>
3371f95118Swdenk 
3471f95118Swdenk /*
3571f95118Swdenk  * Convert a string to lowercase.
3671f95118Swdenk  */
3771f95118Swdenk static void
3871f95118Swdenk 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;
477205e407Swdenk static unsigned long part_offset = 0;
487205e407Swdenk static int cur_part = 1;
497205e407Swdenk 
507205e407Swdenk #define DOS_PART_TBL_OFFSET	0x1be
517205e407Swdenk #define DOS_PART_MAGIC_OFFSET	0x1fe
527205e407Swdenk #define DOS_FS_TYPE_OFFSET	0x36
5371f95118Swdenk 
5471f95118Swdenk int disk_read (__u32 startblock, __u32 getsize, __u8 * bufptr)
5571f95118Swdenk {
567205e407Swdenk 	startblock += part_offset;
577205e407Swdenk 	if (cur_dev == NULL)
587205e407Swdenk 		return -1;
597205e407Swdenk 	if (cur_dev->block_read) {
603e3b9569SPeter Pearse 		return cur_dev->block_read (cur_dev->dev
613e3b9569SPeter Pearse 			, startblock, getsize, (unsigned long *)bufptr);
6271f95118Swdenk 	}
6371f95118Swdenk 	return -1;
6471f95118Swdenk }
6571f95118Swdenk 
6671f95118Swdenk 
6771f95118Swdenk int
687205e407Swdenk fat_register_device(block_dev_desc_t *dev_desc, int part_no)
6971f95118Swdenk {
707205e407Swdenk 	unsigned char buffer[SECTOR_SIZE];
71566a494fSHeiko Schocher 	disk_partition_t info;
727205e407Swdenk 
737205e407Swdenk 	if (!dev_desc->block_read)
747205e407Swdenk 		return -1;
757205e407Swdenk 	cur_dev = dev_desc;
767205e407Swdenk 	/* check if we have a MBR (on floppies we have only a PBR) */
777205e407Swdenk 	if (dev_desc->block_read (dev_desc->dev, 0, 1, (ulong *) buffer) != 1) {
787205e407Swdenk 		printf ("** Can't read from device %d **\n", dev_desc->dev);
797205e407Swdenk 		return -1;
807205e407Swdenk 	}
817205e407Swdenk 	if (buffer[DOS_PART_MAGIC_OFFSET] != 0x55 ||
827205e407Swdenk 		buffer[DOS_PART_MAGIC_OFFSET + 1] != 0xaa) {
837205e407Swdenk 		/* no signature found */
847205e407Swdenk 		return -1;
857205e407Swdenk 	}
86dd60d122SJon Loeliger #if (defined(CONFIG_CMD_IDE) || \
87*75eb82ecSunsik Kim      defined(CONFIG_CMD_MG_DISK) || \
888c5170a7SSonic Zhang      defined(CONFIG_CMD_SATA) || \
89dd60d122SJon Loeliger      defined(CONFIG_CMD_SCSI) || \
90dd60d122SJon Loeliger      defined(CONFIG_CMD_USB) || \
9102df4a27SAndy Fleming      defined(CONFIG_MMC) || \
92b0d8f5bfSPeter Pearse      defined(CONFIG_SYSTEMACE) )
93566a494fSHeiko Schocher 	/* First we assume, there is a MBR */
947205e407Swdenk 	if (!get_partition_info (dev_desc, part_no, &info)) {
957205e407Swdenk 		part_offset = info.start;
967205e407Swdenk 		cur_part = part_no;
97566a494fSHeiko Schocher 	} else if (!strncmp((char *)&buffer[DOS_FS_TYPE_OFFSET], "FAT", 3)) {
98566a494fSHeiko Schocher 		/* ok, we assume we are on a PBR only */
99566a494fSHeiko Schocher 		cur_part = 1;
100566a494fSHeiko Schocher 		part_offset = 0;
101566a494fSHeiko Schocher 	} else {
102bf1060eaSWolfgang Denk 		printf ("** Partition %d not valid on device %d **\n",
103bf1060eaSWolfgang Denk 				part_no, dev_desc->dev);
1047205e407Swdenk 		return -1;
1057205e407Swdenk 	}
10602df4a27SAndy Fleming 
1077205e407Swdenk #else
108566a494fSHeiko Schocher 	if (!strncmp((char *)&buffer[DOS_FS_TYPE_OFFSET],"FAT",3)) {
109566a494fSHeiko Schocher 		/* ok, we assume we are on a PBR only */
110566a494fSHeiko Schocher 		cur_part = 1;
111566a494fSHeiko Schocher 		part_offset = 0;
112566a494fSHeiko Schocher 		info.start = part_offset;
113566a494fSHeiko Schocher 	} else {
1147205e407Swdenk 		/* FIXME we need to determine the start block of the
1157205e407Swdenk 		 * partition where the DOS FS resides. This can be done
1167205e407Swdenk 		 * by using the get_partition_info routine. For this
1177205e407Swdenk 		 * purpose the libpart must be included.
1187205e407Swdenk 		 */
1197205e407Swdenk 		part_offset = 32;
1207205e407Swdenk 		cur_part = 1;
1217205e407Swdenk 	}
122566a494fSHeiko Schocher #endif
12371f95118Swdenk 	return 0;
12471f95118Swdenk }
12571f95118Swdenk 
12671f95118Swdenk 
12771f95118Swdenk /*
12871f95118Swdenk  * Get the first occurence of a directory delimiter ('/' or '\') in a string.
12971f95118Swdenk  * Return index into string if found, -1 otherwise.
13071f95118Swdenk  */
13171f95118Swdenk static int
13271f95118Swdenk dirdelim(char *str)
13371f95118Swdenk {
13471f95118Swdenk 	char *start = str;
13571f95118Swdenk 
13671f95118Swdenk 	while (*str != '\0') {
13771f95118Swdenk 		if (ISDIRDELIM(*str)) return str - start;
13871f95118Swdenk 		str++;
13971f95118Swdenk 	}
14071f95118Swdenk 	return -1;
14171f95118Swdenk }
14271f95118Swdenk 
14371f95118Swdenk 
14471f95118Swdenk /*
14571f95118Swdenk  * Match volume_info fs_type strings.
14671f95118Swdenk  * Return 0 on match, -1 otherwise.
14771f95118Swdenk  */
14871f95118Swdenk static int
14971f95118Swdenk compare_sign(char *str1, char *str2)
15071f95118Swdenk {
15171f95118Swdenk 	char *end = str1+SIGNLEN;
15271f95118Swdenk 
15371f95118Swdenk 	while (str1 != end) {
15471f95118Swdenk 		if (*str1 != *str2) {
15571f95118Swdenk 			return -1;
15671f95118Swdenk 		}
15771f95118Swdenk 		str1++;
15871f95118Swdenk 		str2++;
15971f95118Swdenk 	}
16071f95118Swdenk 
16171f95118Swdenk 	return 0;
16271f95118Swdenk }
16371f95118Swdenk 
16471f95118Swdenk 
16571f95118Swdenk /*
16671f95118Swdenk  * Extract zero terminated short name from a directory entry.
16771f95118Swdenk  */
16871f95118Swdenk static void get_name (dir_entry *dirent, char *s_name)
16971f95118Swdenk {
17071f95118Swdenk 	char *ptr;
17171f95118Swdenk 
17271f95118Swdenk 	memcpy (s_name, dirent->name, 8);
17371f95118Swdenk 	s_name[8] = '\0';
17471f95118Swdenk 	ptr = s_name;
17571f95118Swdenk 	while (*ptr && *ptr != ' ')
17671f95118Swdenk 		ptr++;
17771f95118Swdenk 	if (dirent->ext[0] && dirent->ext[0] != ' ') {
17871f95118Swdenk 		*ptr = '.';
17971f95118Swdenk 		ptr++;
18071f95118Swdenk 		memcpy (ptr, dirent->ext, 3);
18171f95118Swdenk 		ptr[3] = '\0';
18271f95118Swdenk 		while (*ptr && *ptr != ' ')
18371f95118Swdenk 			ptr++;
18471f95118Swdenk 	}
18571f95118Swdenk 	*ptr = '\0';
18671f95118Swdenk 	if (*s_name == DELETED_FLAG)
18771f95118Swdenk 		*s_name = '\0';
18871f95118Swdenk 	else if (*s_name == aRING)
1893c2c2f42SRemy Bohmer 		*s_name = DELETED_FLAG;
19071f95118Swdenk 	downcase (s_name);
19171f95118Swdenk }
19271f95118Swdenk 
19371f95118Swdenk /*
19471f95118Swdenk  * Get the entry at index 'entry' in a FAT (12/16/32) table.
19571f95118Swdenk  * On failure 0x00 is returned.
19671f95118Swdenk  */
19771f95118Swdenk static __u32
19871f95118Swdenk get_fatent(fsdata *mydata, __u32 entry)
19971f95118Swdenk {
20071f95118Swdenk 	__u32 bufnum;
20171f95118Swdenk 	__u32 offset;
20271f95118Swdenk 	__u32 ret = 0x00;
20371f95118Swdenk 
20471f95118Swdenk 	switch (mydata->fatsize) {
20571f95118Swdenk 	case 32:
20671f95118Swdenk 		bufnum = entry / FAT32BUFSIZE;
20771f95118Swdenk 		offset = entry - bufnum * FAT32BUFSIZE;
20871f95118Swdenk 		break;
20971f95118Swdenk 	case 16:
21071f95118Swdenk 		bufnum = entry / FAT16BUFSIZE;
21171f95118Swdenk 		offset = entry - bufnum * FAT16BUFSIZE;
21271f95118Swdenk 		break;
21371f95118Swdenk 	case 12:
21471f95118Swdenk 		bufnum = entry / FAT12BUFSIZE;
21571f95118Swdenk 		offset = entry - bufnum * FAT12BUFSIZE;
21671f95118Swdenk 		break;
21771f95118Swdenk 
21871f95118Swdenk 	default:
21971f95118Swdenk 		/* Unsupported FAT size */
22071f95118Swdenk 		return ret;
22171f95118Swdenk 	}
22271f95118Swdenk 
22371f95118Swdenk 	/* Read a new block of FAT entries into the cache. */
22471f95118Swdenk 	if (bufnum != mydata->fatbufnum) {
22571f95118Swdenk 		int getsize = FATBUFSIZE/FS_BLOCK_SIZE;
22671f95118Swdenk 		__u8 *bufptr = mydata->fatbuf;
22771f95118Swdenk 		__u32 fatlength = mydata->fatlength;
22871f95118Swdenk 		__u32 startblock = bufnum * FATBUFBLOCKS;
22971f95118Swdenk 
23071f95118Swdenk 		fatlength *= SECTOR_SIZE;	/* We want it in bytes now */
23171f95118Swdenk 		startblock += mydata->fat_sect;	/* Offset from start of disk */
23271f95118Swdenk 
23371f95118Swdenk 		if (getsize > fatlength) getsize = fatlength;
23471f95118Swdenk 		if (disk_read(startblock, getsize, bufptr) < 0) {
23571f95118Swdenk 			FAT_DPRINT("Error reading FAT blocks\n");
23671f95118Swdenk 			return ret;
23771f95118Swdenk 		}
23871f95118Swdenk 		mydata->fatbufnum = bufnum;
23971f95118Swdenk 	}
24071f95118Swdenk 
24171f95118Swdenk 	/* Get the actual entry from the table */
24271f95118Swdenk 	switch (mydata->fatsize) {
24371f95118Swdenk 	case 32:
24471f95118Swdenk 		ret = FAT2CPU32(((__u32*)mydata->fatbuf)[offset]);
24571f95118Swdenk 		break;
24671f95118Swdenk 	case 16:
24771f95118Swdenk 		ret = FAT2CPU16(((__u16*)mydata->fatbuf)[offset]);
24871f95118Swdenk 		break;
24971f95118Swdenk 	case 12: {
25071f95118Swdenk 		__u32 off16 = (offset*3)/4;
25171f95118Swdenk 		__u16 val1, val2;
25271f95118Swdenk 
25371f95118Swdenk 		switch (offset & 0x3) {
25471f95118Swdenk 		case 0:
25571f95118Swdenk 			ret = FAT2CPU16(((__u16*)mydata->fatbuf)[off16]);
25671f95118Swdenk 			ret &= 0xfff;
25771f95118Swdenk 			break;
25871f95118Swdenk 		case 1:
25971f95118Swdenk 			val1 = FAT2CPU16(((__u16*)mydata->fatbuf)[off16]);
26071f95118Swdenk 			val1 &= 0xf000;
26171f95118Swdenk 			val2 = FAT2CPU16(((__u16*)mydata->fatbuf)[off16+1]);
26271f95118Swdenk 			val2 &= 0x00ff;
26371f95118Swdenk 			ret = (val2 << 4) | (val1 >> 12);
26471f95118Swdenk 			break;
26571f95118Swdenk 		case 2:
26671f95118Swdenk 			val1 = FAT2CPU16(((__u16*)mydata->fatbuf)[off16]);
26771f95118Swdenk 			val1 &= 0xff00;
26871f95118Swdenk 			val2 = FAT2CPU16(((__u16*)mydata->fatbuf)[off16+1]);
26971f95118Swdenk 			val2 &= 0x000f;
27071f95118Swdenk 			ret = (val2 << 8) | (val1 >> 8);
27171f95118Swdenk 			break;
27271f95118Swdenk 		case 3:
27371f95118Swdenk 			ret = FAT2CPU16(((__u16*)mydata->fatbuf)[off16]);;
27471f95118Swdenk 			ret = (ret & 0xfff0) >> 4;
27571f95118Swdenk 			break;
27671f95118Swdenk 		default:
27771f95118Swdenk 			break;
27871f95118Swdenk 		}
27971f95118Swdenk 	}
28071f95118Swdenk 	break;
28171f95118Swdenk 	}
28271f95118Swdenk 	FAT_DPRINT("ret: %d, offset: %d\n", ret, offset);
28371f95118Swdenk 
28471f95118Swdenk 	return ret;
28571f95118Swdenk }
28671f95118Swdenk 
28771f95118Swdenk 
28871f95118Swdenk /*
28971f95118Swdenk  * Read at most 'size' bytes from the specified cluster into 'buffer'.
29071f95118Swdenk  * Return 0 on success, -1 otherwise.
29171f95118Swdenk  */
29271f95118Swdenk static int
29371f95118Swdenk get_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size)
29471f95118Swdenk {
29571f95118Swdenk 	int idx = 0;
29671f95118Swdenk 	__u32 startsect;
29771f95118Swdenk 
29871f95118Swdenk 	if (clustnum > 0) {
29971f95118Swdenk 		startsect = mydata->data_begin + clustnum*mydata->clust_size;
30071f95118Swdenk 	} else {
30171f95118Swdenk 		startsect = mydata->rootdir_sect;
30271f95118Swdenk 	}
30371f95118Swdenk 
30471f95118Swdenk 	FAT_DPRINT("gc - clustnum: %d, startsect: %d\n", clustnum, startsect);
3057205e407Swdenk 	if (disk_read(startsect, size/FS_BLOCK_SIZE , buffer) < 0) {
30671f95118Swdenk 		FAT_DPRINT("Error reading data\n");
30771f95118Swdenk 		return -1;
30871f95118Swdenk 	}
3097205e407Swdenk 	if(size % FS_BLOCK_SIZE) {
31071f95118Swdenk 		__u8 tmpbuf[FS_BLOCK_SIZE];
3117205e407Swdenk 		idx= size/FS_BLOCK_SIZE;
31271f95118Swdenk 		if (disk_read(startsect + idx, 1, tmpbuf) < 0) {
31371f95118Swdenk 			FAT_DPRINT("Error reading data\n");
31471f95118Swdenk 			return -1;
31571f95118Swdenk 		}
3167205e407Swdenk 		buffer += idx*FS_BLOCK_SIZE;
31771f95118Swdenk 
3187205e407Swdenk 		memcpy(buffer, tmpbuf, size % FS_BLOCK_SIZE);
31971f95118Swdenk 		return 0;
32071f95118Swdenk 	}
32171f95118Swdenk 
32271f95118Swdenk 	return 0;
32371f95118Swdenk }
32471f95118Swdenk 
32571f95118Swdenk 
32671f95118Swdenk /*
32771f95118Swdenk  * Read at most 'maxsize' bytes from the file associated with 'dentptr'
32871f95118Swdenk  * into 'buffer'.
32971f95118Swdenk  * Return the number of bytes read or -1 on fatal errors.
33071f95118Swdenk  */
33171f95118Swdenk static long
33271f95118Swdenk get_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer,
33371f95118Swdenk 	     unsigned long maxsize)
33471f95118Swdenk {
33571f95118Swdenk 	unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0;
33671f95118Swdenk 	unsigned int bytesperclust = mydata->clust_size * SECTOR_SIZE;
33771f95118Swdenk 	__u32 curclust = START(dentptr);
3387205e407Swdenk 	__u32 endclust, newclust;
3397205e407Swdenk 	unsigned long actsize;
34071f95118Swdenk 
34171f95118Swdenk 	FAT_DPRINT("Filesize: %ld bytes\n", filesize);
34271f95118Swdenk 
34371f95118Swdenk 	if (maxsize > 0 && filesize > maxsize) filesize = maxsize;
34471f95118Swdenk 
34571f95118Swdenk 	FAT_DPRINT("Reading: %ld bytes\n", filesize);
34671f95118Swdenk 
3477205e407Swdenk 	actsize=bytesperclust;
3487205e407Swdenk 	endclust=curclust;
34971f95118Swdenk 	do {
3507205e407Swdenk 		/* search for consecutive clusters */
3517205e407Swdenk 		while(actsize < filesize) {
3527205e407Swdenk 			newclust = get_fatent(mydata, endclust);
3537205e407Swdenk 			if((newclust -1)!=endclust)
3547205e407Swdenk 				goto getit;
3558ce4e5c2Smichael 			if (CHECK_CLUST(newclust, mydata->fatsize)) {
3567205e407Swdenk 				FAT_DPRINT("curclust: 0x%x\n", newclust);
3577205e407Swdenk 				FAT_DPRINT("Invalid FAT entry\n");
3587205e407Swdenk 				return gotsize;
3597205e407Swdenk 			}
3607205e407Swdenk 			endclust=newclust;
3617205e407Swdenk 			actsize+= bytesperclust;
3627205e407Swdenk 		}
3637205e407Swdenk 		/* actsize >= file size */
3647205e407Swdenk 		actsize -= bytesperclust;
3657205e407Swdenk 		/* get remaining clusters */
3667205e407Swdenk 		if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
36771f95118Swdenk 			FAT_ERROR("Error reading cluster\n");
36871f95118Swdenk 			return -1;
36971f95118Swdenk 		}
3707205e407Swdenk 		/* get remaining bytes */
3717205e407Swdenk 		gotsize += (int)actsize;
3727205e407Swdenk 		filesize -= actsize;
3737205e407Swdenk 		buffer += actsize;
3747205e407Swdenk 		actsize= filesize;
3757205e407Swdenk 		if (get_cluster(mydata, endclust, buffer, (int)actsize) != 0) {
3767205e407Swdenk 			FAT_ERROR("Error reading cluster\n");
3777205e407Swdenk 			return -1;
3787205e407Swdenk 		}
3797205e407Swdenk 		gotsize+=actsize;
3807205e407Swdenk 		return gotsize;
3817205e407Swdenk getit:
3827205e407Swdenk 		if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
3837205e407Swdenk 			FAT_ERROR("Error reading cluster\n");
3847205e407Swdenk 			return -1;
3857205e407Swdenk 		}
3867205e407Swdenk 		gotsize += (int)actsize;
3877205e407Swdenk 		filesize -= actsize;
3887205e407Swdenk 		buffer += actsize;
3897205e407Swdenk 		curclust = get_fatent(mydata, endclust);
3908ce4e5c2Smichael 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
39171f95118Swdenk 			FAT_DPRINT("curclust: 0x%x\n", curclust);
39271f95118Swdenk 			FAT_ERROR("Invalid FAT entry\n");
39371f95118Swdenk 			return gotsize;
39471f95118Swdenk 		}
3957205e407Swdenk 		actsize=bytesperclust;
3967205e407Swdenk 		endclust=curclust;
39771f95118Swdenk 	} while (1);
39871f95118Swdenk }
39971f95118Swdenk 
40071f95118Swdenk 
40171f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT
40271f95118Swdenk /*
40371f95118Swdenk  * Extract the file name information from 'slotptr' into 'l_name',
40471f95118Swdenk  * starting at l_name[*idx].
40571f95118Swdenk  * Return 1 if terminator (zero byte) is found, 0 otherwise.
40671f95118Swdenk  */
40771f95118Swdenk static int
40871f95118Swdenk slot2str(dir_slot *slotptr, char *l_name, int *idx)
40971f95118Swdenk {
41071f95118Swdenk 	int j;
41171f95118Swdenk 
41271f95118Swdenk 	for (j = 0; j <= 8; j += 2) {
41371f95118Swdenk 		l_name[*idx] = slotptr->name0_4[j];
41471f95118Swdenk 		if (l_name[*idx] == 0x00) return 1;
41571f95118Swdenk 		(*idx)++;
41671f95118Swdenk 	}
41771f95118Swdenk 	for (j = 0; j <= 10; j += 2) {
41871f95118Swdenk 		l_name[*idx] = slotptr->name5_10[j];
41971f95118Swdenk 		if (l_name[*idx] == 0x00) return 1;
42071f95118Swdenk 		(*idx)++;
42171f95118Swdenk 	}
42271f95118Swdenk 	for (j = 0; j <= 2; j += 2) {
42371f95118Swdenk 		l_name[*idx] = slotptr->name11_12[j];
42471f95118Swdenk 		if (l_name[*idx] == 0x00) return 1;
42571f95118Swdenk 		(*idx)++;
42671f95118Swdenk 	}
42771f95118Swdenk 
42871f95118Swdenk 	return 0;
42971f95118Swdenk }
43071f95118Swdenk 
43171f95118Swdenk 
43271f95118Swdenk /*
43371f95118Swdenk  * Extract the full long filename starting at 'retdent' (which is really
43471f95118Swdenk  * a slot) into 'l_name'. If successful also copy the real directory entry
43571f95118Swdenk  * into 'retdent'
43671f95118Swdenk  * Return 0 on success, -1 otherwise.
43771f95118Swdenk  */
4387e4b9b4fSBryan Wu __attribute__ ((__aligned__(__alignof__(dir_entry))))
4395fa66df6Swdenk __u8 get_vfatname_block[MAX_CLUSTSIZE];
44071f95118Swdenk static int
44171f95118Swdenk get_vfatname(fsdata *mydata, int curclust, __u8 *cluster,
44271f95118Swdenk 	     dir_entry *retdent, char *l_name)
44371f95118Swdenk {
44471f95118Swdenk 	dir_entry *realdent;
44571f95118Swdenk 	dir_slot  *slotptr = (dir_slot*) retdent;
44671f95118Swdenk 	__u8	  *nextclust = cluster + mydata->clust_size * SECTOR_SIZE;
4472d1a537dSwdenk 	__u8	   counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff;
44871f95118Swdenk 	int idx = 0;
44971f95118Swdenk 
45071f95118Swdenk 	while ((__u8*)slotptr < nextclust) {
45171f95118Swdenk 		if (counter == 0) break;
4522d1a537dSwdenk 		if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter)
4532d1a537dSwdenk 			return -1;
45471f95118Swdenk 		slotptr++;
45571f95118Swdenk 		counter--;
45671f95118Swdenk 	}
45771f95118Swdenk 
45871f95118Swdenk 	if ((__u8*)slotptr >= nextclust) {
45971f95118Swdenk 		dir_slot *slotptr2;
46071f95118Swdenk 
46171f95118Swdenk 		slotptr--;
46271f95118Swdenk 		curclust = get_fatent(mydata, curclust);
4638ce4e5c2Smichael 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
46471f95118Swdenk 			FAT_DPRINT("curclust: 0x%x\n", curclust);
46571f95118Swdenk 			FAT_ERROR("Invalid FAT entry\n");
46671f95118Swdenk 			return -1;
46771f95118Swdenk 		}
4685fa66df6Swdenk 		if (get_cluster(mydata, curclust, get_vfatname_block,
46971f95118Swdenk 				mydata->clust_size * SECTOR_SIZE) != 0) {
47071f95118Swdenk 			FAT_DPRINT("Error: reading directory block\n");
47171f95118Swdenk 			return -1;
47271f95118Swdenk 		}
4735fa66df6Swdenk 		slotptr2 = (dir_slot*) get_vfatname_block;
47471f95118Swdenk 		while (slotptr2->id > 0x01) {
47571f95118Swdenk 			slotptr2++;
47671f95118Swdenk 		}
47771f95118Swdenk 		/* Save the real directory entry */
47871f95118Swdenk 		realdent = (dir_entry*)slotptr2 + 1;
4795fa66df6Swdenk 		while ((__u8*)slotptr2 >= get_vfatname_block) {
48071f95118Swdenk 			slot2str(slotptr2, l_name, &idx);
48171f95118Swdenk 			slotptr2--;
48271f95118Swdenk 		}
48371f95118Swdenk 	} else {
48471f95118Swdenk 		/* Save the real directory entry */
48571f95118Swdenk 		realdent = (dir_entry*)slotptr;
48671f95118Swdenk 	}
48771f95118Swdenk 
48871f95118Swdenk 	do {
48971f95118Swdenk 		slotptr--;
49071f95118Swdenk 		if (slot2str(slotptr, l_name, &idx)) break;
4912d1a537dSwdenk 	} while (!(slotptr->id & LAST_LONG_ENTRY_MASK));
49271f95118Swdenk 
49371f95118Swdenk 	l_name[idx] = '\0';
49471f95118Swdenk 	if (*l_name == DELETED_FLAG) *l_name = '\0';
4953c2c2f42SRemy Bohmer 	else if (*l_name == aRING) *l_name = DELETED_FLAG;
49671f95118Swdenk 	downcase(l_name);
49771f95118Swdenk 
49871f95118Swdenk 	/* Return the real directory entry */
49971f95118Swdenk 	memcpy(retdent, realdent, sizeof(dir_entry));
50071f95118Swdenk 
50171f95118Swdenk 	return 0;
50271f95118Swdenk }
50371f95118Swdenk 
50471f95118Swdenk 
50571f95118Swdenk /* Calculate short name checksum */
50671f95118Swdenk static __u8
50771f95118Swdenk mkcksum(const char *str)
50871f95118Swdenk {
50971f95118Swdenk 	int i;
51071f95118Swdenk 	__u8 ret = 0;
51171f95118Swdenk 
51271f95118Swdenk 	for (i = 0; i < 11; i++) {
51371f95118Swdenk 		ret = (((ret&1)<<7)|((ret&0xfe)>>1)) + str[i];
51471f95118Swdenk 	}
51571f95118Swdenk 
51671f95118Swdenk 	return ret;
51771f95118Swdenk }
51871f95118Swdenk #endif
51971f95118Swdenk 
52071f95118Swdenk 
52171f95118Swdenk /*
52271f95118Swdenk  * Get the directory entry associated with 'filename' from the directory
52371f95118Swdenk  * starting at 'startsect'
52471f95118Swdenk  */
5257e4b9b4fSBryan Wu __attribute__ ((__aligned__(__alignof__(dir_entry))))
5265fa66df6Swdenk __u8 get_dentfromdir_block[MAX_CLUSTSIZE];
52771f95118Swdenk static dir_entry *get_dentfromdir (fsdata * mydata, int startsect,
52871f95118Swdenk 				   char *filename, dir_entry * retdent,
52971f95118Swdenk 				   int dols)
53071f95118Swdenk {
53171f95118Swdenk     __u16 prevcksum = 0xffff;
53271f95118Swdenk     __u32 curclust = START (retdent);
53371f95118Swdenk     int files = 0, dirs = 0;
53471f95118Swdenk 
53571f95118Swdenk     FAT_DPRINT ("get_dentfromdir: %s\n", filename);
53671f95118Swdenk     while (1) {
53771f95118Swdenk 	dir_entry *dentptr;
53871f95118Swdenk 	int i;
53971f95118Swdenk 
5405fa66df6Swdenk 	if (get_cluster (mydata, curclust, get_dentfromdir_block,
54171f95118Swdenk 		 mydata->clust_size * SECTOR_SIZE) != 0) {
54271f95118Swdenk 	    FAT_DPRINT ("Error: reading directory block\n");
54371f95118Swdenk 	    return NULL;
54471f95118Swdenk 	}
5455fa66df6Swdenk 	dentptr = (dir_entry *) get_dentfromdir_block;
54671f95118Swdenk 	for (i = 0; i < DIRENTSPERCLUST; i++) {
54771f95118Swdenk 	    char s_name[14], l_name[256];
54871f95118Swdenk 
54971f95118Swdenk 	    l_name[0] = '\0';
550855a496fSwdenk 	    if (dentptr->name[0] == DELETED_FLAG) {
551855a496fSwdenk 		    dentptr++;
552855a496fSwdenk 		    continue;
553855a496fSwdenk 	    }
55471f95118Swdenk 	    if ((dentptr->attr & ATTR_VOLUME)) {
55571f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT
55671f95118Swdenk 		if ((dentptr->attr & ATTR_VFAT) &&
5572d1a537dSwdenk 		    (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
55871f95118Swdenk 		    prevcksum = ((dir_slot *) dentptr)
55971f95118Swdenk 			    ->alias_checksum;
5605fa66df6Swdenk 		    get_vfatname (mydata, curclust, get_dentfromdir_block,
56171f95118Swdenk 				  dentptr, l_name);
56271f95118Swdenk 		    if (dols) {
56371f95118Swdenk 			int isdir = (dentptr->attr & ATTR_DIR);
56471f95118Swdenk 			char dirc;
56571f95118Swdenk 			int doit = 0;
56671f95118Swdenk 
56771f95118Swdenk 			if (isdir) {
56871f95118Swdenk 			    dirs++;
56971f95118Swdenk 			    dirc = '/';
57071f95118Swdenk 			    doit = 1;
57171f95118Swdenk 			} else {
57271f95118Swdenk 			    dirc = ' ';
57371f95118Swdenk 			    if (l_name[0] != 0) {
57471f95118Swdenk 				files++;
57571f95118Swdenk 				doit = 1;
57671f95118Swdenk 			    }
57771f95118Swdenk 			}
57871f95118Swdenk 			if (doit) {
57971f95118Swdenk 			    if (dirc == ' ') {
58071f95118Swdenk 				printf (" %8ld   %s%c\n",
58171f95118Swdenk 					(long) FAT2CPU32 (dentptr->size),
58271f95118Swdenk 					l_name, dirc);
58371f95118Swdenk 			    } else {
58471f95118Swdenk 				printf ("            %s%c\n", l_name, dirc);
58571f95118Swdenk 			    }
58671f95118Swdenk 			}
58771f95118Swdenk 			dentptr++;
58871f95118Swdenk 			continue;
58971f95118Swdenk 		    }
59071f95118Swdenk 		    FAT_DPRINT ("vfatname: |%s|\n", l_name);
59171f95118Swdenk 		} else
59271f95118Swdenk #endif
59371f95118Swdenk 		{
59471f95118Swdenk 		    /* Volume label or VFAT entry */
59571f95118Swdenk 		    dentptr++;
59671f95118Swdenk 		    continue;
59771f95118Swdenk 		}
59871f95118Swdenk 	    }
59971f95118Swdenk 	    if (dentptr->name[0] == 0) {
60071f95118Swdenk 		if (dols) {
60171f95118Swdenk 		    printf ("\n%d file(s), %d dir(s)\n\n", files, dirs);
60271f95118Swdenk 		}
60371f95118Swdenk 		FAT_DPRINT ("Dentname == NULL - %d\n", i);
60471f95118Swdenk 		return NULL;
60571f95118Swdenk 	    }
60671f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT
60771f95118Swdenk 	    if (dols && mkcksum (dentptr->name) == prevcksum) {
60871f95118Swdenk 		dentptr++;
60971f95118Swdenk 		continue;
61071f95118Swdenk 	    }
61171f95118Swdenk #endif
61271f95118Swdenk 	    get_name (dentptr, s_name);
61371f95118Swdenk 	    if (dols) {
61471f95118Swdenk 		int isdir = (dentptr->attr & ATTR_DIR);
61571f95118Swdenk 		char dirc;
61671f95118Swdenk 		int doit = 0;
61771f95118Swdenk 
61871f95118Swdenk 		if (isdir) {
61971f95118Swdenk 		    dirs++;
62071f95118Swdenk 		    dirc = '/';
62171f95118Swdenk 		    doit = 1;
62271f95118Swdenk 		} else {
62371f95118Swdenk 		    dirc = ' ';
62471f95118Swdenk 		    if (s_name[0] != 0) {
62571f95118Swdenk 			files++;
62671f95118Swdenk 			doit = 1;
62771f95118Swdenk 		    }
62871f95118Swdenk 		}
62971f95118Swdenk 		if (doit) {
63071f95118Swdenk 		    if (dirc == ' ') {
63171f95118Swdenk 			printf (" %8ld   %s%c\n",
63271f95118Swdenk 				(long) FAT2CPU32 (dentptr->size), s_name,
63371f95118Swdenk 				dirc);
63471f95118Swdenk 		    } else {
63571f95118Swdenk 			printf ("            %s%c\n", s_name, dirc);
63671f95118Swdenk 		    }
63771f95118Swdenk 		}
63871f95118Swdenk 		dentptr++;
63971f95118Swdenk 		continue;
64071f95118Swdenk 	    }
64171f95118Swdenk 	    if (strcmp (filename, s_name) && strcmp (filename, l_name)) {
64271f95118Swdenk 		FAT_DPRINT ("Mismatch: |%s|%s|\n", s_name, l_name);
64371f95118Swdenk 		dentptr++;
64471f95118Swdenk 		continue;
64571f95118Swdenk 	    }
64671f95118Swdenk 	    memcpy (retdent, dentptr, sizeof (dir_entry));
64771f95118Swdenk 
64871f95118Swdenk 	    FAT_DPRINT ("DentName: %s", s_name);
64971f95118Swdenk 	    FAT_DPRINT (", start: 0x%x", START (dentptr));
65071f95118Swdenk 	    FAT_DPRINT (", size:  0x%x %s\n",
65171f95118Swdenk 			FAT2CPU32 (dentptr->size),
65271f95118Swdenk 			(dentptr->attr & ATTR_DIR) ? "(DIR)" : "");
65371f95118Swdenk 
65471f95118Swdenk 	    return retdent;
65571f95118Swdenk 	}
65671f95118Swdenk 	curclust = get_fatent (mydata, curclust);
6578ce4e5c2Smichael 	if (CHECK_CLUST(curclust, mydata->fatsize)) {
65871f95118Swdenk 	    FAT_DPRINT ("curclust: 0x%x\n", curclust);
65971f95118Swdenk 	    FAT_ERROR ("Invalid FAT entry\n");
66071f95118Swdenk 	    return NULL;
66171f95118Swdenk 	}
66271f95118Swdenk     }
66371f95118Swdenk 
66471f95118Swdenk     return NULL;
66571f95118Swdenk }
66671f95118Swdenk 
66771f95118Swdenk 
66871f95118Swdenk /*
66971f95118Swdenk  * Read boot sector and volume info from a FAT filesystem
67071f95118Swdenk  */
67171f95118Swdenk static int
67271f95118Swdenk read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize)
67371f95118Swdenk {
67471f95118Swdenk 	__u8 block[FS_BLOCK_SIZE];
67571f95118Swdenk 	volume_info *vistart;
67665f7d410SWolfgang Denk 	char *fstype;
67771f95118Swdenk 
67871f95118Swdenk 	if (disk_read(0, 1, block) < 0) {
67971f95118Swdenk 		FAT_DPRINT("Error: reading block\n");
68071f95118Swdenk 		return -1;
68171f95118Swdenk 	}
68271f95118Swdenk 
68371f95118Swdenk 	memcpy(bs, block, sizeof(boot_sector));
68471f95118Swdenk 	bs->reserved	= FAT2CPU16(bs->reserved);
68571f95118Swdenk 	bs->fat_length	= FAT2CPU16(bs->fat_length);
68671f95118Swdenk 	bs->secs_track	= FAT2CPU16(bs->secs_track);
68771f95118Swdenk 	bs->heads	= FAT2CPU16(bs->heads);
68871f95118Swdenk #if 0 /* UNUSED */
68971f95118Swdenk 	bs->hidden	= FAT2CPU32(bs->hidden);
69071f95118Swdenk #endif
69171f95118Swdenk 	bs->total_sect	= FAT2CPU32(bs->total_sect);
69271f95118Swdenk 
69371f95118Swdenk 	/* FAT32 entries */
69471f95118Swdenk 	if (bs->fat_length == 0) {
69571f95118Swdenk 		/* Assume FAT32 */
69671f95118Swdenk 		bs->fat32_length = FAT2CPU32(bs->fat32_length);
69771f95118Swdenk 		bs->flags	 = FAT2CPU16(bs->flags);
69871f95118Swdenk 		bs->root_cluster = FAT2CPU32(bs->root_cluster);
69971f95118Swdenk 		bs->info_sector  = FAT2CPU16(bs->info_sector);
70071f95118Swdenk 		bs->backup_boot  = FAT2CPU16(bs->backup_boot);
70171f95118Swdenk 		vistart = (volume_info*) (block + sizeof(boot_sector));
70271f95118Swdenk 		*fatsize = 32;
70371f95118Swdenk 	} else {
70471f95118Swdenk 		vistart = (volume_info*) &(bs->fat32_length);
70571f95118Swdenk 		*fatsize = 0;
70671f95118Swdenk 	}
70771f95118Swdenk 	memcpy(volinfo, vistart, sizeof(volume_info));
70871f95118Swdenk 
70965f7d410SWolfgang Denk 	/*
71065f7d410SWolfgang Denk 	 * Terminate fs_type string. Writing past the end of vistart
71165f7d410SWolfgang Denk 	 * is ok - it's just the buffer.
71265f7d410SWolfgang Denk 	 */
71365f7d410SWolfgang Denk 	fstype = vistart->fs_type;
71465f7d410SWolfgang Denk 	fstype[8] = '\0';
71571f95118Swdenk 
71671f95118Swdenk 	if (*fatsize == 32) {
71771f95118Swdenk 		if (compare_sign(FAT32_SIGN, vistart->fs_type) == 0) {
71871f95118Swdenk 			return 0;
71971f95118Swdenk 		}
72071f95118Swdenk 	} else {
72171f95118Swdenk 		if (compare_sign(FAT12_SIGN, vistart->fs_type) == 0) {
72271f95118Swdenk 			*fatsize = 12;
72371f95118Swdenk 			return 0;
72471f95118Swdenk 		}
72571f95118Swdenk 		if (compare_sign(FAT16_SIGN, vistart->fs_type) == 0) {
72671f95118Swdenk 			*fatsize = 16;
72771f95118Swdenk 			return 0;
72871f95118Swdenk 		}
72971f95118Swdenk 	}
73071f95118Swdenk 
73171f95118Swdenk 	FAT_DPRINT("Error: broken fs_type sign\n");
73271f95118Swdenk 	return -1;
73371f95118Swdenk }
73471f95118Swdenk 
7357e4b9b4fSBryan Wu __attribute__ ((__aligned__(__alignof__(dir_entry))))
7367e4b9b4fSBryan Wu __u8 do_fat_read_block[MAX_CLUSTSIZE];
73720cc00ddSstroese long
73871f95118Swdenk do_fat_read (const char *filename, void *buffer, unsigned long maxsize,
73971f95118Swdenk 	     int dols)
74071f95118Swdenk {
741d06a5f7eSWolfgang Denk #if CONFIG_NIOS /* NIOS CPU cannot access big automatic arrays */
742d06a5f7eSWolfgang Denk     static
743d06a5f7eSWolfgang Denk #endif
74471f95118Swdenk     char fnamecopy[2048];
74571f95118Swdenk     boot_sector bs;
74671f95118Swdenk     volume_info volinfo;
74771f95118Swdenk     fsdata datablock;
74871f95118Swdenk     fsdata *mydata = &datablock;
74971f95118Swdenk     dir_entry *dentptr;
75071f95118Swdenk     __u16 prevcksum = 0xffff;
75171f95118Swdenk     char *subname = "";
75271f95118Swdenk     int rootdir_size, cursect;
75371f95118Swdenk     int idx, isdir = 0;
75471f95118Swdenk     int files = 0, dirs = 0;
75571f95118Swdenk     long ret = 0;
75671f95118Swdenk     int firsttime;
75771f95118Swdenk 
75871f95118Swdenk     if (read_bootsectandvi (&bs, &volinfo, &mydata->fatsize)) {
75971f95118Swdenk 	FAT_DPRINT ("Error: reading boot sector\n");
76071f95118Swdenk 	return -1;
76171f95118Swdenk     }
76271f95118Swdenk     if (mydata->fatsize == 32) {
76371f95118Swdenk 	mydata->fatlength = bs.fat32_length;
76471f95118Swdenk     } else {
76571f95118Swdenk 	mydata->fatlength = bs.fat_length;
76671f95118Swdenk     }
76771f95118Swdenk     mydata->fat_sect = bs.reserved;
76871f95118Swdenk     cursect = mydata->rootdir_sect
76971f95118Swdenk 	    = mydata->fat_sect + mydata->fatlength * bs.fats;
77071f95118Swdenk     mydata->clust_size = bs.cluster_size;
77171f95118Swdenk     if (mydata->fatsize == 32) {
77271f95118Swdenk 	rootdir_size = mydata->clust_size;
77371f95118Swdenk 	mydata->data_begin = mydata->rootdir_sect   /* + rootdir_size */
77471f95118Swdenk 		- (mydata->clust_size * 2);
77571f95118Swdenk     } else {
77671f95118Swdenk 	rootdir_size = ((bs.dir_entries[1] * (int) 256 + bs.dir_entries[0])
77771f95118Swdenk 			* sizeof (dir_entry)) / SECTOR_SIZE;
77871f95118Swdenk 	mydata->data_begin = mydata->rootdir_sect + rootdir_size
77971f95118Swdenk 		- (mydata->clust_size * 2);
78071f95118Swdenk     }
78171f95118Swdenk     mydata->fatbufnum = -1;
78271f95118Swdenk 
78371f95118Swdenk     FAT_DPRINT ("FAT%d, fatlength: %d\n", mydata->fatsize,
78471f95118Swdenk 		mydata->fatlength);
78571f95118Swdenk     FAT_DPRINT ("Rootdir begins at sector: %d, offset: %x, size: %d\n"
78671f95118Swdenk 		"Data begins at: %d\n",
78771f95118Swdenk 		mydata->rootdir_sect, mydata->rootdir_sect * SECTOR_SIZE,
78871f95118Swdenk 		rootdir_size, mydata->data_begin);
78971f95118Swdenk     FAT_DPRINT ("Cluster size: %d\n", mydata->clust_size);
79071f95118Swdenk 
79171f95118Swdenk     /* "cwd" is always the root... */
79271f95118Swdenk     while (ISDIRDELIM (*filename))
79371f95118Swdenk 	filename++;
79471f95118Swdenk     /* Make a copy of the filename and convert it to lowercase */
79571f95118Swdenk     strcpy (fnamecopy, filename);
79671f95118Swdenk     downcase (fnamecopy);
79771f95118Swdenk     if (*fnamecopy == '\0') {
79871f95118Swdenk 	if (!dols)
79971f95118Swdenk 	    return -1;
80071f95118Swdenk 	dols = LS_ROOT;
80171f95118Swdenk     } else if ((idx = dirdelim (fnamecopy)) >= 0) {
80271f95118Swdenk 	isdir = 1;
80371f95118Swdenk 	fnamecopy[idx] = '\0';
80471f95118Swdenk 	subname = fnamecopy + idx + 1;
80571f95118Swdenk 	/* Handle multiple delimiters */
80671f95118Swdenk 	while (ISDIRDELIM (*subname))
80771f95118Swdenk 	    subname++;
80871f95118Swdenk     } else if (dols) {
80971f95118Swdenk 	isdir = 1;
81071f95118Swdenk     }
81171f95118Swdenk 
81271f95118Swdenk     while (1) {
81371f95118Swdenk 	int i;
81471f95118Swdenk 
8155fa66df6Swdenk 	if (disk_read (cursect, mydata->clust_size, do_fat_read_block) < 0) {
81671f95118Swdenk 	    FAT_DPRINT ("Error: reading rootdir block\n");
81771f95118Swdenk 	    return -1;
81871f95118Swdenk 	}
8195fa66df6Swdenk 	dentptr = (dir_entry *) do_fat_read_block;
82071f95118Swdenk 	for (i = 0; i < DIRENTSPERBLOCK; i++) {
82171f95118Swdenk 	    char s_name[14], l_name[256];
82271f95118Swdenk 
82371f95118Swdenk 	    l_name[0] = '\0';
82471f95118Swdenk 	    if ((dentptr->attr & ATTR_VOLUME)) {
82571f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT
82671f95118Swdenk 		if ((dentptr->attr & ATTR_VFAT) &&
8272d1a537dSwdenk 		    (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
82871f95118Swdenk 		    prevcksum = ((dir_slot *) dentptr)->alias_checksum;
8295fa66df6Swdenk 		    get_vfatname (mydata, 0, do_fat_read_block, dentptr, l_name);
83071f95118Swdenk 		    if (dols == LS_ROOT) {
83171f95118Swdenk 			int isdir = (dentptr->attr & ATTR_DIR);
83271f95118Swdenk 			char dirc;
83371f95118Swdenk 			int doit = 0;
83471f95118Swdenk 
83571f95118Swdenk 			if (isdir) {
83671f95118Swdenk 			    dirs++;
83771f95118Swdenk 			    dirc = '/';
83871f95118Swdenk 			    doit = 1;
83971f95118Swdenk 			} else {
84071f95118Swdenk 			    dirc = ' ';
84171f95118Swdenk 			    if (l_name[0] != 0) {
84271f95118Swdenk 				files++;
84371f95118Swdenk 				doit = 1;
84471f95118Swdenk 			    }
84571f95118Swdenk 			}
84671f95118Swdenk 			if (doit) {
84771f95118Swdenk 			    if (dirc == ' ') {
84871f95118Swdenk 				printf (" %8ld   %s%c\n",
84971f95118Swdenk 					(long) FAT2CPU32 (dentptr->size),
85071f95118Swdenk 					l_name, dirc);
85171f95118Swdenk 			    } else {
85271f95118Swdenk 				printf ("            %s%c\n", l_name, dirc);
85371f95118Swdenk 			    }
85471f95118Swdenk 			}
85571f95118Swdenk 			dentptr++;
85671f95118Swdenk 			continue;
85771f95118Swdenk 		    }
85871f95118Swdenk 		    FAT_DPRINT ("Rootvfatname: |%s|\n", l_name);
85971f95118Swdenk 		} else
86071f95118Swdenk #endif
86171f95118Swdenk 		{
86271f95118Swdenk 		    /* Volume label or VFAT entry */
86371f95118Swdenk 		    dentptr++;
86471f95118Swdenk 		    continue;
86571f95118Swdenk 		}
86671f95118Swdenk 	    } else if (dentptr->name[0] == 0) {
86771f95118Swdenk 		FAT_DPRINT ("RootDentname == NULL - %d\n", i);
86871f95118Swdenk 		if (dols == LS_ROOT) {
86971f95118Swdenk 		    printf ("\n%d file(s), %d dir(s)\n\n", files, dirs);
87071f95118Swdenk 		    return 0;
87171f95118Swdenk 		}
87271f95118Swdenk 		return -1;
87371f95118Swdenk 	    }
87471f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT
87571f95118Swdenk 	    else if (dols == LS_ROOT
87671f95118Swdenk 		     && mkcksum (dentptr->name) == prevcksum) {
87771f95118Swdenk 		dentptr++;
87871f95118Swdenk 		continue;
87971f95118Swdenk 	    }
88071f95118Swdenk #endif
88171f95118Swdenk 	    get_name (dentptr, s_name);
88271f95118Swdenk 	    if (dols == LS_ROOT) {
88371f95118Swdenk 		int isdir = (dentptr->attr & ATTR_DIR);
88471f95118Swdenk 		char dirc;
88571f95118Swdenk 		int doit = 0;
88671f95118Swdenk 
88771f95118Swdenk 		if (isdir) {
88871f95118Swdenk 		    dirc = '/';
889a43278a4Swdenk 		    if (s_name[0] != 0) {
890a43278a4Swdenk 			dirs++;
89171f95118Swdenk 			doit = 1;
892a43278a4Swdenk 		    }
89371f95118Swdenk 		} else {
89471f95118Swdenk 		    dirc = ' ';
89571f95118Swdenk 		    if (s_name[0] != 0) {
89671f95118Swdenk 			files++;
89771f95118Swdenk 			doit = 1;
89871f95118Swdenk 		    }
89971f95118Swdenk 		}
90071f95118Swdenk 		if (doit) {
90171f95118Swdenk 		    if (dirc == ' ') {
90271f95118Swdenk 			printf (" %8ld   %s%c\n",
90371f95118Swdenk 				(long) FAT2CPU32 (dentptr->size), s_name,
90471f95118Swdenk 				dirc);
90571f95118Swdenk 		    } else {
90671f95118Swdenk 			printf ("            %s%c\n", s_name, dirc);
90771f95118Swdenk 		    }
90871f95118Swdenk 		}
90971f95118Swdenk 		dentptr++;
91071f95118Swdenk 		continue;
91171f95118Swdenk 	    }
91271f95118Swdenk 	    if (strcmp (fnamecopy, s_name) && strcmp (fnamecopy, l_name)) {
91371f95118Swdenk 		FAT_DPRINT ("RootMismatch: |%s|%s|\n", s_name, l_name);
91471f95118Swdenk 		dentptr++;
91571f95118Swdenk 		continue;
91671f95118Swdenk 	    }
91771f95118Swdenk 	    if (isdir && !(dentptr->attr & ATTR_DIR))
91871f95118Swdenk 		return -1;
91971f95118Swdenk 
92071f95118Swdenk 	    FAT_DPRINT ("RootName: %s", s_name);
92171f95118Swdenk 	    FAT_DPRINT (", start: 0x%x", START (dentptr));
92271f95118Swdenk 	    FAT_DPRINT (", size:  0x%x %s\n",
92371f95118Swdenk 			FAT2CPU32 (dentptr->size), isdir ? "(DIR)" : "");
92471f95118Swdenk 
92571f95118Swdenk 	    goto rootdir_done;  /* We got a match */
92671f95118Swdenk 	}
92771f95118Swdenk 	cursect++;
92871f95118Swdenk     }
92971f95118Swdenk   rootdir_done:
93071f95118Swdenk 
93171f95118Swdenk     firsttime = 1;
93271f95118Swdenk     while (isdir) {
93371f95118Swdenk 	int startsect = mydata->data_begin
93471f95118Swdenk 		+ START (dentptr) * mydata->clust_size;
93571f95118Swdenk 	dir_entry dent;
93671f95118Swdenk 	char *nextname = NULL;
93771f95118Swdenk 
93871f95118Swdenk 	dent = *dentptr;
93971f95118Swdenk 	dentptr = &dent;
94071f95118Swdenk 
94171f95118Swdenk 	idx = dirdelim (subname);
94271f95118Swdenk 	if (idx >= 0) {
94371f95118Swdenk 	    subname[idx] = '\0';
94471f95118Swdenk 	    nextname = subname + idx + 1;
94571f95118Swdenk 	    /* Handle multiple delimiters */
94671f95118Swdenk 	    while (ISDIRDELIM (*nextname))
94771f95118Swdenk 		nextname++;
94871f95118Swdenk 	    if (dols && *nextname == '\0')
94971f95118Swdenk 		firsttime = 0;
95071f95118Swdenk 	} else {
95171f95118Swdenk 	    if (dols && firsttime) {
95271f95118Swdenk 		firsttime = 0;
95371f95118Swdenk 	    } else {
95471f95118Swdenk 		isdir = 0;
95571f95118Swdenk 	    }
95671f95118Swdenk 	}
95771f95118Swdenk 
95871f95118Swdenk 	if (get_dentfromdir (mydata, startsect, subname, dentptr,
95971f95118Swdenk 			     isdir ? 0 : dols) == NULL) {
96071f95118Swdenk 	    if (dols && !isdir)
96171f95118Swdenk 		return 0;
96271f95118Swdenk 	    return -1;
96371f95118Swdenk 	}
96471f95118Swdenk 
96571f95118Swdenk 	if (idx >= 0) {
96671f95118Swdenk 	    if (!(dentptr->attr & ATTR_DIR))
96771f95118Swdenk 		return -1;
96871f95118Swdenk 	    subname = nextname;
96971f95118Swdenk 	}
97071f95118Swdenk     }
97171f95118Swdenk     ret = get_contents (mydata, dentptr, buffer, maxsize);
97271f95118Swdenk     FAT_DPRINT ("Size: %d, got: %ld\n", FAT2CPU32 (dentptr->size), ret);
97371f95118Swdenk 
97471f95118Swdenk     return ret;
97571f95118Swdenk }
97671f95118Swdenk 
97771f95118Swdenk 
97871f95118Swdenk int
97971f95118Swdenk file_fat_detectfs(void)
98071f95118Swdenk {
98171f95118Swdenk 	boot_sector	bs;
98271f95118Swdenk 	volume_info	volinfo;
98371f95118Swdenk 	int		fatsize;
9847205e407Swdenk 	char	vol_label[12];
98571f95118Swdenk 
9867205e407Swdenk 	if(cur_dev==NULL) {
9877205e407Swdenk 		printf("No current device\n");
9887205e407Swdenk 		return 1;
9897205e407Swdenk 	}
990dd60d122SJon Loeliger #if defined(CONFIG_CMD_IDE) || \
991*75eb82ecSunsik Kim     defined(CONFIG_CMD_MG_DISK) || \
9928c5170a7SSonic Zhang     defined(CONFIG_CMD_SATA) || \
993dd60d122SJon Loeliger     defined(CONFIG_CMD_SCSI) || \
994dd60d122SJon Loeliger     defined(CONFIG_CMD_USB) || \
99521f6f963SAndy Fleming     defined(CONFIG_MMC)
9967205e407Swdenk 	printf("Interface:  ");
9977205e407Swdenk 	switch(cur_dev->if_type) {
9987205e407Swdenk 		case IF_TYPE_IDE :	printf("IDE"); break;
9998c5170a7SSonic Zhang 		case IF_TYPE_SATA :	printf("SATA"); break;
10007205e407Swdenk 		case IF_TYPE_SCSI :	printf("SCSI"); break;
10017205e407Swdenk 		case IF_TYPE_ATAPI :	printf("ATAPI"); break;
10027205e407Swdenk 		case IF_TYPE_USB :	printf("USB"); break;
10037205e407Swdenk 		case IF_TYPE_DOC :	printf("DOC"); break;
10047205e407Swdenk 		case IF_TYPE_MMC :	printf("MMC"); break;
10057205e407Swdenk 		default :		printf("Unknown");
10067205e407Swdenk 	}
10077205e407Swdenk 	printf("\n  Device %d: ",cur_dev->dev);
10087205e407Swdenk 	dev_print(cur_dev);
10097205e407Swdenk #endif
10107205e407Swdenk 	if(read_bootsectandvi(&bs, &volinfo, &fatsize)) {
10117205e407Swdenk 		printf("\nNo valid FAT fs found\n");
10127205e407Swdenk 		return 1;
10137205e407Swdenk 	}
10147205e407Swdenk 	memcpy (vol_label, volinfo.volume_label, 11);
10157205e407Swdenk 	vol_label[11] = '\0';
10167205e407Swdenk 	volinfo.fs_type[5]='\0';
10173e3b9569SPeter Pearse 	printf("Partition %d: Filesystem: %s \"%s\"\n"
10183e3b9569SPeter Pearse 			,cur_part,volinfo.fs_type,vol_label);
10197205e407Swdenk 	return 0;
102071f95118Swdenk }
102171f95118Swdenk 
102271f95118Swdenk 
102371f95118Swdenk int
102471f95118Swdenk file_fat_ls(const char *dir)
102571f95118Swdenk {
102671f95118Swdenk 	return do_fat_read(dir, NULL, 0, LS_YES);
102771f95118Swdenk }
102871f95118Swdenk 
102971f95118Swdenk 
103071f95118Swdenk long
103171f95118Swdenk file_fat_read(const char *filename, void *buffer, unsigned long maxsize)
103271f95118Swdenk {
10337205e407Swdenk 	printf("reading %s\n",filename);
103471f95118Swdenk 	return do_fat_read(filename, buffer, maxsize, LS_NO);
103571f95118Swdenk }
1036