xref: /rk3399_rockchip-uboot/fs/fat/fat.c (revision d06a5f7ebfdc6c5d27dc0e6acf441c0916eee176)
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 #if (CONFIG_COMMANDS & CFG_CMD_FAT)
3571f95118Swdenk 
3671f95118Swdenk /*
3771f95118Swdenk  * Convert a string to lowercase.
3871f95118Swdenk  */
3971f95118Swdenk static void
4071f95118Swdenk downcase(char *str)
4171f95118Swdenk {
4271f95118Swdenk 	while (*str != '\0') {
4371f95118Swdenk 		TOLOWER(*str);
4471f95118Swdenk 		str++;
4571f95118Swdenk 	}
4671f95118Swdenk }
4771f95118Swdenk 
487205e407Swdenk static  block_dev_desc_t *cur_dev = NULL;
497205e407Swdenk static unsigned long part_offset = 0;
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
5571f95118Swdenk 
5671f95118Swdenk int disk_read (__u32 startblock, __u32 getsize, __u8 * bufptr)
5771f95118Swdenk {
587205e407Swdenk 	startblock += part_offset;
597205e407Swdenk 	if (cur_dev == NULL)
607205e407Swdenk 		return -1;
617205e407Swdenk 	if (cur_dev->block_read) {
627205e407Swdenk 		return cur_dev->block_read (cur_dev->dev, startblock, getsize, (unsigned long *)bufptr);
6371f95118Swdenk 	}
6471f95118Swdenk 	return -1;
6571f95118Swdenk }
6671f95118Swdenk 
6771f95118Swdenk 
6871f95118Swdenk int
697205e407Swdenk fat_register_device(block_dev_desc_t *dev_desc, int part_no)
7071f95118Swdenk {
717205e407Swdenk 	unsigned char buffer[SECTOR_SIZE];
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 	}
867205e407Swdenk 	if(!strncmp(&buffer[DOS_FS_TYPE_OFFSET],"FAT",3)) {
877205e407Swdenk 		/* ok, we assume we are on a PBR only */
887205e407Swdenk 		cur_part = 1;
897205e407Swdenk 		part_offset=0;
907205e407Swdenk 	}
917205e407Swdenk 	else {
9280885a9dSwdenk #if (CONFIG_COMMANDS & CFG_CMD_IDE) || (CONFIG_COMMANDS & CFG_CMD_SCSI) || \
93a5bbcc3cSwdenk     (CONFIG_COMMANDS & CFG_CMD_USB) || defined(CONFIG_SYSTEMACE)
947205e407Swdenk 		disk_partition_t info;
957205e407Swdenk 		if(!get_partition_info(dev_desc, part_no, &info)) {
967205e407Swdenk 			part_offset = info.start;
977205e407Swdenk 			cur_part = part_no;
987205e407Swdenk 		}
997205e407Swdenk 		else {
1007205e407Swdenk 			printf ("** Partition %d not valid on device %d **\n",part_no,dev_desc->dev);
1017205e407Swdenk 			return -1;
1027205e407Swdenk 		}
1037205e407Swdenk #else
1047205e407Swdenk 		/* FIXME we need to determine the start block of the
1057205e407Swdenk 		 * partition where the DOS FS resides. This can be done
1067205e407Swdenk 		 * by using the get_partition_info routine. For this
1077205e407Swdenk 		 * purpose the libpart must be included.
1087205e407Swdenk 		 */
1097205e407Swdenk 		part_offset=32;
1107205e407Swdenk 		cur_part = 1;
1117205e407Swdenk #endif
1127205e407Swdenk 	}
11371f95118Swdenk 	return 0;
11471f95118Swdenk }
11571f95118Swdenk 
11671f95118Swdenk 
11771f95118Swdenk /*
11871f95118Swdenk  * Get the first occurence of a directory delimiter ('/' or '\') in a string.
11971f95118Swdenk  * Return index into string if found, -1 otherwise.
12071f95118Swdenk  */
12171f95118Swdenk static int
12271f95118Swdenk dirdelim(char *str)
12371f95118Swdenk {
12471f95118Swdenk 	char *start = str;
12571f95118Swdenk 
12671f95118Swdenk 	while (*str != '\0') {
12771f95118Swdenk 		if (ISDIRDELIM(*str)) return str - start;
12871f95118Swdenk 		str++;
12971f95118Swdenk 	}
13071f95118Swdenk 	return -1;
13171f95118Swdenk }
13271f95118Swdenk 
13371f95118Swdenk 
13471f95118Swdenk /*
13571f95118Swdenk  * Match volume_info fs_type strings.
13671f95118Swdenk  * Return 0 on match, -1 otherwise.
13771f95118Swdenk  */
13871f95118Swdenk static int
13971f95118Swdenk compare_sign(char *str1, char *str2)
14071f95118Swdenk {
14171f95118Swdenk 	char *end = str1+SIGNLEN;
14271f95118Swdenk 
14371f95118Swdenk 	while (str1 != end) {
14471f95118Swdenk 		if (*str1 != *str2) {
14571f95118Swdenk 			return -1;
14671f95118Swdenk 		}
14771f95118Swdenk 		str1++;
14871f95118Swdenk 		str2++;
14971f95118Swdenk 	}
15071f95118Swdenk 
15171f95118Swdenk 	return 0;
15271f95118Swdenk }
15371f95118Swdenk 
15471f95118Swdenk 
15571f95118Swdenk /*
15671f95118Swdenk  * Extract zero terminated short name from a directory entry.
15771f95118Swdenk  */
15871f95118Swdenk static void get_name (dir_entry *dirent, char *s_name)
15971f95118Swdenk {
16071f95118Swdenk 	char *ptr;
16171f95118Swdenk 
16271f95118Swdenk 	memcpy (s_name, dirent->name, 8);
16371f95118Swdenk 	s_name[8] = '\0';
16471f95118Swdenk 	ptr = s_name;
16571f95118Swdenk 	while (*ptr && *ptr != ' ')
16671f95118Swdenk 		ptr++;
16771f95118Swdenk 	if (dirent->ext[0] && dirent->ext[0] != ' ') {
16871f95118Swdenk 		*ptr = '.';
16971f95118Swdenk 		ptr++;
17071f95118Swdenk 		memcpy (ptr, dirent->ext, 3);
17171f95118Swdenk 		ptr[3] = '\0';
17271f95118Swdenk 		while (*ptr && *ptr != ' ')
17371f95118Swdenk 			ptr++;
17471f95118Swdenk 	}
17571f95118Swdenk 	*ptr = '\0';
17671f95118Swdenk 	if (*s_name == DELETED_FLAG)
17771f95118Swdenk 		*s_name = '\0';
17871f95118Swdenk 	else if (*s_name == aRING)
17971f95118Swdenk 		*s_name = '�';
18071f95118Swdenk 	downcase (s_name);
18171f95118Swdenk }
18271f95118Swdenk 
18371f95118Swdenk /*
18471f95118Swdenk  * Get the entry at index 'entry' in a FAT (12/16/32) table.
18571f95118Swdenk  * On failure 0x00 is returned.
18671f95118Swdenk  */
18771f95118Swdenk static __u32
18871f95118Swdenk get_fatent(fsdata *mydata, __u32 entry)
18971f95118Swdenk {
19071f95118Swdenk 	__u32 bufnum;
19171f95118Swdenk 	__u32 offset;
19271f95118Swdenk 	__u32 ret = 0x00;
19371f95118Swdenk 
19471f95118Swdenk 	switch (mydata->fatsize) {
19571f95118Swdenk 	case 32:
19671f95118Swdenk 		bufnum = entry / FAT32BUFSIZE;
19771f95118Swdenk 		offset = entry - bufnum * FAT32BUFSIZE;
19871f95118Swdenk 		break;
19971f95118Swdenk 	case 16:
20071f95118Swdenk 		bufnum = entry / FAT16BUFSIZE;
20171f95118Swdenk 		offset = entry - bufnum * FAT16BUFSIZE;
20271f95118Swdenk 		break;
20371f95118Swdenk 	case 12:
20471f95118Swdenk 		bufnum = entry / FAT12BUFSIZE;
20571f95118Swdenk 		offset = entry - bufnum * FAT12BUFSIZE;
20671f95118Swdenk 		break;
20771f95118Swdenk 
20871f95118Swdenk 	default:
20971f95118Swdenk 		/* Unsupported FAT size */
21071f95118Swdenk 		return ret;
21171f95118Swdenk 	}
21271f95118Swdenk 
21371f95118Swdenk 	/* Read a new block of FAT entries into the cache. */
21471f95118Swdenk 	if (bufnum != mydata->fatbufnum) {
21571f95118Swdenk 		int getsize = FATBUFSIZE/FS_BLOCK_SIZE;
21671f95118Swdenk 		__u8 *bufptr = mydata->fatbuf;
21771f95118Swdenk 		__u32 fatlength = mydata->fatlength;
21871f95118Swdenk 		__u32 startblock = bufnum * FATBUFBLOCKS;
21971f95118Swdenk 
22071f95118Swdenk 		fatlength *= SECTOR_SIZE;	/* We want it in bytes now */
22171f95118Swdenk 		startblock += mydata->fat_sect;	/* Offset from start of disk */
22271f95118Swdenk 
22371f95118Swdenk 		if (getsize > fatlength) getsize = fatlength;
22471f95118Swdenk 		if (disk_read(startblock, getsize, bufptr) < 0) {
22571f95118Swdenk 			FAT_DPRINT("Error reading FAT blocks\n");
22671f95118Swdenk 			return ret;
22771f95118Swdenk 		}
22871f95118Swdenk 		mydata->fatbufnum = bufnum;
22971f95118Swdenk 	}
23071f95118Swdenk 
23171f95118Swdenk 	/* Get the actual entry from the table */
23271f95118Swdenk 	switch (mydata->fatsize) {
23371f95118Swdenk 	case 32:
23471f95118Swdenk 		ret = FAT2CPU32(((__u32*)mydata->fatbuf)[offset]);
23571f95118Swdenk 		break;
23671f95118Swdenk 	case 16:
23771f95118Swdenk 		ret = FAT2CPU16(((__u16*)mydata->fatbuf)[offset]);
23871f95118Swdenk 		break;
23971f95118Swdenk 	case 12: {
24071f95118Swdenk 		__u32 off16 = (offset*3)/4;
24171f95118Swdenk 		__u16 val1, val2;
24271f95118Swdenk 
24371f95118Swdenk 		switch (offset & 0x3) {
24471f95118Swdenk 		case 0:
24571f95118Swdenk 			ret = FAT2CPU16(((__u16*)mydata->fatbuf)[off16]);
24671f95118Swdenk 			ret &= 0xfff;
24771f95118Swdenk 			break;
24871f95118Swdenk 		case 1:
24971f95118Swdenk 			val1 = FAT2CPU16(((__u16*)mydata->fatbuf)[off16]);
25071f95118Swdenk 			val1 &= 0xf000;
25171f95118Swdenk 			val2 = FAT2CPU16(((__u16*)mydata->fatbuf)[off16+1]);
25271f95118Swdenk 			val2 &= 0x00ff;
25371f95118Swdenk 			ret = (val2 << 4) | (val1 >> 12);
25471f95118Swdenk 			break;
25571f95118Swdenk 		case 2:
25671f95118Swdenk 			val1 = FAT2CPU16(((__u16*)mydata->fatbuf)[off16]);
25771f95118Swdenk 			val1 &= 0xff00;
25871f95118Swdenk 			val2 = FAT2CPU16(((__u16*)mydata->fatbuf)[off16+1]);
25971f95118Swdenk 			val2 &= 0x000f;
26071f95118Swdenk 			ret = (val2 << 8) | (val1 >> 8);
26171f95118Swdenk 			break;
26271f95118Swdenk 		case 3:
26371f95118Swdenk 			ret = FAT2CPU16(((__u16*)mydata->fatbuf)[off16]);;
26471f95118Swdenk 			ret = (ret & 0xfff0) >> 4;
26571f95118Swdenk 			break;
26671f95118Swdenk 		default:
26771f95118Swdenk 			break;
26871f95118Swdenk 		}
26971f95118Swdenk 	}
27071f95118Swdenk 	break;
27171f95118Swdenk 	}
27271f95118Swdenk 	FAT_DPRINT("ret: %d, offset: %d\n", ret, offset);
27371f95118Swdenk 
27471f95118Swdenk 	return ret;
27571f95118Swdenk }
27671f95118Swdenk 
27771f95118Swdenk 
27871f95118Swdenk /*
27971f95118Swdenk  * Read at most 'size' bytes from the specified cluster into 'buffer'.
28071f95118Swdenk  * Return 0 on success, -1 otherwise.
28171f95118Swdenk  */
28271f95118Swdenk static int
28371f95118Swdenk get_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size)
28471f95118Swdenk {
28571f95118Swdenk 	int idx = 0;
28671f95118Swdenk 	__u32 startsect;
28771f95118Swdenk 
28871f95118Swdenk 	if (clustnum > 0) {
28971f95118Swdenk 		startsect = mydata->data_begin + clustnum*mydata->clust_size;
29071f95118Swdenk 	} else {
29171f95118Swdenk 		startsect = mydata->rootdir_sect;
29271f95118Swdenk 	}
29371f95118Swdenk 
29471f95118Swdenk 	FAT_DPRINT("gc - clustnum: %d, startsect: %d\n", clustnum, startsect);
2957205e407Swdenk 	if (disk_read(startsect, size/FS_BLOCK_SIZE , buffer) < 0) {
29671f95118Swdenk 		FAT_DPRINT("Error reading data\n");
29771f95118Swdenk 		return -1;
29871f95118Swdenk 	}
2997205e407Swdenk 	if(size % FS_BLOCK_SIZE) {
30071f95118Swdenk 		__u8 tmpbuf[FS_BLOCK_SIZE];
3017205e407Swdenk 		idx= size/FS_BLOCK_SIZE;
30271f95118Swdenk 		if (disk_read(startsect + idx, 1, tmpbuf) < 0) {
30371f95118Swdenk 			FAT_DPRINT("Error reading data\n");
30471f95118Swdenk 			return -1;
30571f95118Swdenk 		}
3067205e407Swdenk 		buffer += idx*FS_BLOCK_SIZE;
30771f95118Swdenk 
3087205e407Swdenk 		memcpy(buffer, tmpbuf, size % FS_BLOCK_SIZE);
30971f95118Swdenk 		return 0;
31071f95118Swdenk 	}
31171f95118Swdenk 
31271f95118Swdenk 	return 0;
31371f95118Swdenk }
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;
32671f95118Swdenk 	unsigned int bytesperclust = mydata->clust_size * SECTOR_SIZE;
32771f95118Swdenk 	__u32 curclust = START(dentptr);
3287205e407Swdenk 	__u32 endclust, newclust;
3297205e407Swdenk 	unsigned long actsize;
33071f95118Swdenk 
33171f95118Swdenk 	FAT_DPRINT("Filesize: %ld bytes\n", filesize);
33271f95118Swdenk 
33371f95118Swdenk 	if (maxsize > 0 && filesize > maxsize) filesize = maxsize;
33471f95118Swdenk 
33571f95118Swdenk 	FAT_DPRINT("Reading: %ld bytes\n", filesize);
33671f95118Swdenk 
3377205e407Swdenk 	actsize=bytesperclust;
3387205e407Swdenk 	endclust=curclust;
33971f95118Swdenk 	do {
3407205e407Swdenk 		/* search for consecutive clusters */
3417205e407Swdenk 		while(actsize < filesize) {
3427205e407Swdenk 			newclust = get_fatent(mydata, endclust);
3437205e407Swdenk 			if((newclust -1)!=endclust)
3447205e407Swdenk 				goto getit;
3457205e407Swdenk 			if (newclust <= 0x0001 || newclust >= 0xfff0) {
3467205e407Swdenk 				FAT_DPRINT("curclust: 0x%x\n", newclust);
3477205e407Swdenk 				FAT_DPRINT("Invalid FAT entry\n");
3487205e407Swdenk 				return gotsize;
3497205e407Swdenk 			}
3507205e407Swdenk 			endclust=newclust;
3517205e407Swdenk 			actsize+= bytesperclust;
3527205e407Swdenk 		}
3537205e407Swdenk 		/* actsize >= file size */
3547205e407Swdenk 		actsize -= bytesperclust;
3557205e407Swdenk 		/* get remaining clusters */
3567205e407Swdenk 		if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
35771f95118Swdenk 			FAT_ERROR("Error reading cluster\n");
35871f95118Swdenk 			return -1;
35971f95118Swdenk 		}
3607205e407Swdenk 		/* get remaining bytes */
3617205e407Swdenk 		gotsize += (int)actsize;
3627205e407Swdenk 		filesize -= actsize;
3637205e407Swdenk 		buffer += actsize;
3647205e407Swdenk 		actsize= filesize;
3657205e407Swdenk 		if (get_cluster(mydata, endclust, buffer, (int)actsize) != 0) {
3667205e407Swdenk 			FAT_ERROR("Error reading cluster\n");
3677205e407Swdenk 			return -1;
3687205e407Swdenk 		}
3697205e407Swdenk 		gotsize+=actsize;
3707205e407Swdenk 		return gotsize;
3717205e407Swdenk getit:
3727205e407Swdenk 		if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
3737205e407Swdenk 			FAT_ERROR("Error reading cluster\n");
3747205e407Swdenk 			return -1;
3757205e407Swdenk 		}
3767205e407Swdenk 		gotsize += (int)actsize;
3777205e407Swdenk 		filesize -= actsize;
3787205e407Swdenk 		buffer += actsize;
3797205e407Swdenk 		curclust = get_fatent(mydata, endclust);
38071f95118Swdenk 		if (curclust <= 0x0001 || curclust >= 0xfff0) {
38171f95118Swdenk 			FAT_DPRINT("curclust: 0x%x\n", curclust);
38271f95118Swdenk 			FAT_ERROR("Invalid FAT entry\n");
38371f95118Swdenk 			return gotsize;
38471f95118Swdenk 		}
3857205e407Swdenk 		actsize=bytesperclust;
3867205e407Swdenk 		endclust=curclust;
38771f95118Swdenk 	} while (1);
38871f95118Swdenk }
38971f95118Swdenk 
39071f95118Swdenk 
39171f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT
39271f95118Swdenk /*
39371f95118Swdenk  * Extract the file name information from 'slotptr' into 'l_name',
39471f95118Swdenk  * starting at l_name[*idx].
39571f95118Swdenk  * Return 1 if terminator (zero byte) is found, 0 otherwise.
39671f95118Swdenk  */
39771f95118Swdenk static int
39871f95118Swdenk slot2str(dir_slot *slotptr, char *l_name, int *idx)
39971f95118Swdenk {
40071f95118Swdenk 	int j;
40171f95118Swdenk 
40271f95118Swdenk 	for (j = 0; j <= 8; j += 2) {
40371f95118Swdenk 		l_name[*idx] = slotptr->name0_4[j];
40471f95118Swdenk 		if (l_name[*idx] == 0x00) return 1;
40571f95118Swdenk 		(*idx)++;
40671f95118Swdenk 	}
40771f95118Swdenk 	for (j = 0; j <= 10; j += 2) {
40871f95118Swdenk 		l_name[*idx] = slotptr->name5_10[j];
40971f95118Swdenk 		if (l_name[*idx] == 0x00) return 1;
41071f95118Swdenk 		(*idx)++;
41171f95118Swdenk 	}
41271f95118Swdenk 	for (j = 0; j <= 2; j += 2) {
41371f95118Swdenk 		l_name[*idx] = slotptr->name11_12[j];
41471f95118Swdenk 		if (l_name[*idx] == 0x00) return 1;
41571f95118Swdenk 		(*idx)++;
41671f95118Swdenk 	}
41771f95118Swdenk 
41871f95118Swdenk 	return 0;
41971f95118Swdenk }
42071f95118Swdenk 
42171f95118Swdenk 
42271f95118Swdenk /*
42371f95118Swdenk  * Extract the full long filename starting at 'retdent' (which is really
42471f95118Swdenk  * a slot) into 'l_name'. If successful also copy the real directory entry
42571f95118Swdenk  * into 'retdent'
42671f95118Swdenk  * Return 0 on success, -1 otherwise.
42771f95118Swdenk  */
4285fa66df6Swdenk __u8	 get_vfatname_block[MAX_CLUSTSIZE];
42971f95118Swdenk static int
43071f95118Swdenk get_vfatname(fsdata *mydata, int curclust, __u8 *cluster,
43171f95118Swdenk 	     dir_entry *retdent, char *l_name)
43271f95118Swdenk {
43371f95118Swdenk 	dir_entry *realdent;
43471f95118Swdenk 	dir_slot  *slotptr = (dir_slot*) retdent;
43571f95118Swdenk 	__u8	  *nextclust = cluster + mydata->clust_size * SECTOR_SIZE;
4362d1a537dSwdenk 	__u8	   counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff;
43771f95118Swdenk 	int idx = 0;
43871f95118Swdenk 
43971f95118Swdenk 	while ((__u8*)slotptr < nextclust) {
44071f95118Swdenk 		if (counter == 0) break;
4412d1a537dSwdenk 		if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter)
4422d1a537dSwdenk 			return -1;
44371f95118Swdenk 		slotptr++;
44471f95118Swdenk 		counter--;
44571f95118Swdenk 	}
44671f95118Swdenk 
44771f95118Swdenk 	if ((__u8*)slotptr >= nextclust) {
44871f95118Swdenk 		dir_slot *slotptr2;
44971f95118Swdenk 
45071f95118Swdenk 		slotptr--;
45171f95118Swdenk 		curclust = get_fatent(mydata, curclust);
45271f95118Swdenk 		if (curclust <= 0x0001 || curclust >= 0xfff0) {
45371f95118Swdenk 			FAT_DPRINT("curclust: 0x%x\n", curclust);
45471f95118Swdenk 			FAT_ERROR("Invalid FAT entry\n");
45571f95118Swdenk 			return -1;
45671f95118Swdenk 		}
4575fa66df6Swdenk 		if (get_cluster(mydata, curclust, get_vfatname_block,
45871f95118Swdenk 				mydata->clust_size * SECTOR_SIZE) != 0) {
45971f95118Swdenk 			FAT_DPRINT("Error: reading directory block\n");
46071f95118Swdenk 			return -1;
46171f95118Swdenk 		}
4625fa66df6Swdenk 		slotptr2 = (dir_slot*) get_vfatname_block;
46371f95118Swdenk 		while (slotptr2->id > 0x01) {
46471f95118Swdenk 			slotptr2++;
46571f95118Swdenk 		}
46671f95118Swdenk 		/* Save the real directory entry */
46771f95118Swdenk 		realdent = (dir_entry*)slotptr2 + 1;
4685fa66df6Swdenk 		while ((__u8*)slotptr2 >= get_vfatname_block) {
46971f95118Swdenk 			slot2str(slotptr2, l_name, &idx);
47071f95118Swdenk 			slotptr2--;
47171f95118Swdenk 		}
47271f95118Swdenk 	} else {
47371f95118Swdenk 		/* Save the real directory entry */
47471f95118Swdenk 		realdent = (dir_entry*)slotptr;
47571f95118Swdenk 	}
47671f95118Swdenk 
47771f95118Swdenk 	do {
47871f95118Swdenk 		slotptr--;
47971f95118Swdenk 		if (slot2str(slotptr, l_name, &idx)) break;
4802d1a537dSwdenk 	} while (!(slotptr->id & LAST_LONG_ENTRY_MASK));
48171f95118Swdenk 
48271f95118Swdenk 	l_name[idx] = '\0';
48371f95118Swdenk 	if (*l_name == DELETED_FLAG) *l_name = '\0';
48471f95118Swdenk 	else if (*l_name == aRING) *l_name = '�';
48571f95118Swdenk 	downcase(l_name);
48671f95118Swdenk 
48771f95118Swdenk 	/* Return the real directory entry */
48871f95118Swdenk 	memcpy(retdent, realdent, sizeof(dir_entry));
48971f95118Swdenk 
49071f95118Swdenk 	return 0;
49171f95118Swdenk }
49271f95118Swdenk 
49371f95118Swdenk 
49471f95118Swdenk /* Calculate short name checksum */
49571f95118Swdenk static __u8
49671f95118Swdenk mkcksum(const char *str)
49771f95118Swdenk {
49871f95118Swdenk 	int i;
49971f95118Swdenk 	__u8 ret = 0;
50071f95118Swdenk 
50171f95118Swdenk 	for (i = 0; i < 11; i++) {
50271f95118Swdenk 		ret = (((ret&1)<<7)|((ret&0xfe)>>1)) + str[i];
50371f95118Swdenk 	}
50471f95118Swdenk 
50571f95118Swdenk 	return ret;
50671f95118Swdenk }
50771f95118Swdenk #endif
50871f95118Swdenk 
50971f95118Swdenk 
51071f95118Swdenk /*
51171f95118Swdenk  * Get the directory entry associated with 'filename' from the directory
51271f95118Swdenk  * starting at 'startsect'
51371f95118Swdenk  */
5145fa66df6Swdenk __u8 get_dentfromdir_block[MAX_CLUSTSIZE];
51571f95118Swdenk static dir_entry *get_dentfromdir (fsdata * mydata, int startsect,
51671f95118Swdenk 				   char *filename, dir_entry * retdent,
51771f95118Swdenk 				   int dols)
51871f95118Swdenk {
51971f95118Swdenk     __u16 prevcksum = 0xffff;
52071f95118Swdenk     __u32 curclust = START (retdent);
52171f95118Swdenk     int files = 0, dirs = 0;
52271f95118Swdenk 
52371f95118Swdenk     FAT_DPRINT ("get_dentfromdir: %s\n", filename);
52471f95118Swdenk     while (1) {
52571f95118Swdenk 	dir_entry *dentptr;
52671f95118Swdenk 	int i;
52771f95118Swdenk 
5285fa66df6Swdenk 	if (get_cluster (mydata, curclust, get_dentfromdir_block,
52971f95118Swdenk 		 mydata->clust_size * SECTOR_SIZE) != 0) {
53071f95118Swdenk 	    FAT_DPRINT ("Error: reading directory block\n");
53171f95118Swdenk 	    return NULL;
53271f95118Swdenk 	}
5335fa66df6Swdenk 	dentptr = (dir_entry *) get_dentfromdir_block;
53471f95118Swdenk 	for (i = 0; i < DIRENTSPERCLUST; i++) {
53571f95118Swdenk 	    char s_name[14], l_name[256];
53671f95118Swdenk 
53771f95118Swdenk 	    l_name[0] = '\0';
538855a496fSwdenk 	    if (dentptr->name[0] == DELETED_FLAG) {
539855a496fSwdenk 		    dentptr++;
540855a496fSwdenk 		    continue;
541855a496fSwdenk 	    }
54271f95118Swdenk 	    if ((dentptr->attr & ATTR_VOLUME)) {
54371f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT
54471f95118Swdenk 		if ((dentptr->attr & ATTR_VFAT) &&
5452d1a537dSwdenk 		    (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
54671f95118Swdenk 		    prevcksum = ((dir_slot *) dentptr)
54771f95118Swdenk 			    ->alias_checksum;
5485fa66df6Swdenk 		    get_vfatname (mydata, curclust, get_dentfromdir_block,
54971f95118Swdenk 				  dentptr, l_name);
55071f95118Swdenk 		    if (dols) {
55171f95118Swdenk 			int isdir = (dentptr->attr & ATTR_DIR);
55271f95118Swdenk 			char dirc;
55371f95118Swdenk 			int doit = 0;
55471f95118Swdenk 
55571f95118Swdenk 			if (isdir) {
55671f95118Swdenk 			    dirs++;
55771f95118Swdenk 			    dirc = '/';
55871f95118Swdenk 			    doit = 1;
55971f95118Swdenk 			} else {
56071f95118Swdenk 			    dirc = ' ';
56171f95118Swdenk 			    if (l_name[0] != 0) {
56271f95118Swdenk 				files++;
56371f95118Swdenk 				doit = 1;
56471f95118Swdenk 			    }
56571f95118Swdenk 			}
56671f95118Swdenk 			if (doit) {
56771f95118Swdenk 			    if (dirc == ' ') {
56871f95118Swdenk 				printf (" %8ld   %s%c\n",
56971f95118Swdenk 					(long) FAT2CPU32 (dentptr->size),
57071f95118Swdenk 					l_name, dirc);
57171f95118Swdenk 			    } else {
57271f95118Swdenk 				printf ("            %s%c\n", l_name, dirc);
57371f95118Swdenk 			    }
57471f95118Swdenk 			}
57571f95118Swdenk 			dentptr++;
57671f95118Swdenk 			continue;
57771f95118Swdenk 		    }
57871f95118Swdenk 		    FAT_DPRINT ("vfatname: |%s|\n", l_name);
57971f95118Swdenk 		} else
58071f95118Swdenk #endif
58171f95118Swdenk 		{
58271f95118Swdenk 		    /* Volume label or VFAT entry */
58371f95118Swdenk 		    dentptr++;
58471f95118Swdenk 		    continue;
58571f95118Swdenk 		}
58671f95118Swdenk 	    }
58771f95118Swdenk 	    if (dentptr->name[0] == 0) {
58871f95118Swdenk 		if (dols) {
58971f95118Swdenk 		    printf ("\n%d file(s), %d dir(s)\n\n", files, dirs);
59071f95118Swdenk 		}
59171f95118Swdenk 		FAT_DPRINT ("Dentname == NULL - %d\n", i);
59271f95118Swdenk 		return NULL;
59371f95118Swdenk 	    }
59471f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT
59571f95118Swdenk 	    if (dols && mkcksum (dentptr->name) == prevcksum) {
59671f95118Swdenk 		dentptr++;
59771f95118Swdenk 		continue;
59871f95118Swdenk 	    }
59971f95118Swdenk #endif
60071f95118Swdenk 	    get_name (dentptr, s_name);
60171f95118Swdenk 	    if (dols) {
60271f95118Swdenk 		int isdir = (dentptr->attr & ATTR_DIR);
60371f95118Swdenk 		char dirc;
60471f95118Swdenk 		int doit = 0;
60571f95118Swdenk 
60671f95118Swdenk 		if (isdir) {
60771f95118Swdenk 		    dirs++;
60871f95118Swdenk 		    dirc = '/';
60971f95118Swdenk 		    doit = 1;
61071f95118Swdenk 		} else {
61171f95118Swdenk 		    dirc = ' ';
61271f95118Swdenk 		    if (s_name[0] != 0) {
61371f95118Swdenk 			files++;
61471f95118Swdenk 			doit = 1;
61571f95118Swdenk 		    }
61671f95118Swdenk 		}
61771f95118Swdenk 		if (doit) {
61871f95118Swdenk 		    if (dirc == ' ') {
61971f95118Swdenk 			printf (" %8ld   %s%c\n",
62071f95118Swdenk 				(long) FAT2CPU32 (dentptr->size), s_name,
62171f95118Swdenk 				dirc);
62271f95118Swdenk 		    } else {
62371f95118Swdenk 			printf ("            %s%c\n", s_name, dirc);
62471f95118Swdenk 		    }
62571f95118Swdenk 		}
62671f95118Swdenk 		dentptr++;
62771f95118Swdenk 		continue;
62871f95118Swdenk 	    }
62971f95118Swdenk 	    if (strcmp (filename, s_name) && strcmp (filename, l_name)) {
63071f95118Swdenk 		FAT_DPRINT ("Mismatch: |%s|%s|\n", s_name, l_name);
63171f95118Swdenk 		dentptr++;
63271f95118Swdenk 		continue;
63371f95118Swdenk 	    }
63471f95118Swdenk 	    memcpy (retdent, dentptr, sizeof (dir_entry));
63571f95118Swdenk 
63671f95118Swdenk 	    FAT_DPRINT ("DentName: %s", s_name);
63771f95118Swdenk 	    FAT_DPRINT (", start: 0x%x", START (dentptr));
63871f95118Swdenk 	    FAT_DPRINT (", size:  0x%x %s\n",
63971f95118Swdenk 			FAT2CPU32 (dentptr->size),
64071f95118Swdenk 			(dentptr->attr & ATTR_DIR) ? "(DIR)" : "");
64171f95118Swdenk 
64271f95118Swdenk 	    return retdent;
64371f95118Swdenk 	}
64471f95118Swdenk 	curclust = get_fatent (mydata, curclust);
64571f95118Swdenk 	if (curclust <= 0x0001 || curclust >= 0xfff0) {
64671f95118Swdenk 	    FAT_DPRINT ("curclust: 0x%x\n", curclust);
64771f95118Swdenk 	    FAT_ERROR ("Invalid FAT entry\n");
64871f95118Swdenk 	    return NULL;
64971f95118Swdenk 	}
65071f95118Swdenk     }
65171f95118Swdenk 
65271f95118Swdenk     return NULL;
65371f95118Swdenk }
65471f95118Swdenk 
65571f95118Swdenk 
65671f95118Swdenk /*
65771f95118Swdenk  * Read boot sector and volume info from a FAT filesystem
65871f95118Swdenk  */
65971f95118Swdenk static int
66071f95118Swdenk read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize)
66171f95118Swdenk {
66271f95118Swdenk 	__u8 block[FS_BLOCK_SIZE];
66371f95118Swdenk 	volume_info *vistart;
66471f95118Swdenk 
66571f95118Swdenk 	if (disk_read(0, 1, block) < 0) {
66671f95118Swdenk 		FAT_DPRINT("Error: reading block\n");
66771f95118Swdenk 		return -1;
66871f95118Swdenk 	}
66971f95118Swdenk 
67071f95118Swdenk 	memcpy(bs, block, sizeof(boot_sector));
67171f95118Swdenk 	bs->reserved	= FAT2CPU16(bs->reserved);
67271f95118Swdenk 	bs->fat_length	= FAT2CPU16(bs->fat_length);
67371f95118Swdenk 	bs->secs_track	= FAT2CPU16(bs->secs_track);
67471f95118Swdenk 	bs->heads	= FAT2CPU16(bs->heads);
67571f95118Swdenk #if 0 /* UNUSED */
67671f95118Swdenk 	bs->hidden	= FAT2CPU32(bs->hidden);
67771f95118Swdenk #endif
67871f95118Swdenk 	bs->total_sect	= FAT2CPU32(bs->total_sect);
67971f95118Swdenk 
68071f95118Swdenk 	/* FAT32 entries */
68171f95118Swdenk 	if (bs->fat_length == 0) {
68271f95118Swdenk 		/* Assume FAT32 */
68371f95118Swdenk 		bs->fat32_length = FAT2CPU32(bs->fat32_length);
68471f95118Swdenk 		bs->flags	 = FAT2CPU16(bs->flags);
68571f95118Swdenk 		bs->root_cluster = FAT2CPU32(bs->root_cluster);
68671f95118Swdenk 		bs->info_sector  = FAT2CPU16(bs->info_sector);
68771f95118Swdenk 		bs->backup_boot  = FAT2CPU16(bs->backup_boot);
68871f95118Swdenk 		vistart = (volume_info*) (block + sizeof(boot_sector));
68971f95118Swdenk 		*fatsize = 32;
69071f95118Swdenk 	} else {
69171f95118Swdenk 		vistart = (volume_info*) &(bs->fat32_length);
69271f95118Swdenk 		*fatsize = 0;
69371f95118Swdenk 	}
69471f95118Swdenk 	memcpy(volinfo, vistart, sizeof(volume_info));
69571f95118Swdenk 
69671f95118Swdenk 	/* Terminate fs_type string. Writing past the end of vistart
69771f95118Swdenk 	   is ok - it's just the buffer. */
69871f95118Swdenk 	vistart->fs_type[8] = '\0';
69971f95118Swdenk 
70071f95118Swdenk 	if (*fatsize == 32) {
70171f95118Swdenk 		if (compare_sign(FAT32_SIGN, vistart->fs_type) == 0) {
70271f95118Swdenk 			return 0;
70371f95118Swdenk 		}
70471f95118Swdenk 	} else {
70571f95118Swdenk 		if (compare_sign(FAT12_SIGN, vistart->fs_type) == 0) {
70671f95118Swdenk 			*fatsize = 12;
70771f95118Swdenk 			return 0;
70871f95118Swdenk 		}
70971f95118Swdenk 		if (compare_sign(FAT16_SIGN, vistart->fs_type) == 0) {
71071f95118Swdenk 			*fatsize = 16;
71171f95118Swdenk 			return 0;
71271f95118Swdenk 		}
71371f95118Swdenk 	}
71471f95118Swdenk 
71571f95118Swdenk 	FAT_DPRINT("Error: broken fs_type sign\n");
71671f95118Swdenk 	return -1;
71771f95118Swdenk }
71871f95118Swdenk 
71971f95118Swdenk 
7205fa66df6Swdenk __u8 do_fat_read_block[MAX_CLUSTSIZE];  /* Block buffer */
72120cc00ddSstroese long
72271f95118Swdenk do_fat_read (const char *filename, void *buffer, unsigned long maxsize,
72371f95118Swdenk 	     int dols)
72471f95118Swdenk {
725*d06a5f7eSWolfgang Denk #if CONFIG_NIOS /* NIOS CPU cannot access big automatic arrays */
726*d06a5f7eSWolfgang Denk     static
727*d06a5f7eSWolfgang Denk #endif
72871f95118Swdenk     char fnamecopy[2048];
72971f95118Swdenk     boot_sector bs;
73071f95118Swdenk     volume_info volinfo;
73171f95118Swdenk     fsdata datablock;
73271f95118Swdenk     fsdata *mydata = &datablock;
73371f95118Swdenk     dir_entry *dentptr;
73471f95118Swdenk     __u16 prevcksum = 0xffff;
73571f95118Swdenk     char *subname = "";
73671f95118Swdenk     int rootdir_size, cursect;
73771f95118Swdenk     int idx, isdir = 0;
73871f95118Swdenk     int files = 0, dirs = 0;
73971f95118Swdenk     long ret = 0;
74071f95118Swdenk     int firsttime;
74171f95118Swdenk 
74271f95118Swdenk     if (read_bootsectandvi (&bs, &volinfo, &mydata->fatsize)) {
74371f95118Swdenk 	FAT_DPRINT ("Error: reading boot sector\n");
74471f95118Swdenk 	return -1;
74571f95118Swdenk     }
74671f95118Swdenk     if (mydata->fatsize == 32) {
74771f95118Swdenk 	mydata->fatlength = bs.fat32_length;
74871f95118Swdenk     } else {
74971f95118Swdenk 	mydata->fatlength = bs.fat_length;
75071f95118Swdenk     }
75171f95118Swdenk     mydata->fat_sect = bs.reserved;
75271f95118Swdenk     cursect = mydata->rootdir_sect
75371f95118Swdenk 	    = mydata->fat_sect + mydata->fatlength * bs.fats;
75471f95118Swdenk     mydata->clust_size = bs.cluster_size;
75571f95118Swdenk     if (mydata->fatsize == 32) {
75671f95118Swdenk 	rootdir_size = mydata->clust_size;
75771f95118Swdenk 	mydata->data_begin = mydata->rootdir_sect   /* + rootdir_size */
75871f95118Swdenk 		- (mydata->clust_size * 2);
75971f95118Swdenk     } else {
76071f95118Swdenk 	rootdir_size = ((bs.dir_entries[1] * (int) 256 + bs.dir_entries[0])
76171f95118Swdenk 			* sizeof (dir_entry)) / SECTOR_SIZE;
76271f95118Swdenk 	mydata->data_begin = mydata->rootdir_sect + rootdir_size
76371f95118Swdenk 		- (mydata->clust_size * 2);
76471f95118Swdenk     }
76571f95118Swdenk     mydata->fatbufnum = -1;
76671f95118Swdenk 
76771f95118Swdenk     FAT_DPRINT ("FAT%d, fatlength: %d\n", mydata->fatsize,
76871f95118Swdenk 		mydata->fatlength);
76971f95118Swdenk     FAT_DPRINT ("Rootdir begins at sector: %d, offset: %x, size: %d\n"
77071f95118Swdenk 		"Data begins at: %d\n",
77171f95118Swdenk 		mydata->rootdir_sect, mydata->rootdir_sect * SECTOR_SIZE,
77271f95118Swdenk 		rootdir_size, mydata->data_begin);
77371f95118Swdenk     FAT_DPRINT ("Cluster size: %d\n", mydata->clust_size);
77471f95118Swdenk 
77571f95118Swdenk     /* "cwd" is always the root... */
77671f95118Swdenk     while (ISDIRDELIM (*filename))
77771f95118Swdenk 	filename++;
77871f95118Swdenk     /* Make a copy of the filename and convert it to lowercase */
77971f95118Swdenk     strcpy (fnamecopy, filename);
78071f95118Swdenk     downcase (fnamecopy);
78171f95118Swdenk     if (*fnamecopy == '\0') {
78271f95118Swdenk 	if (!dols)
78371f95118Swdenk 	    return -1;
78471f95118Swdenk 	dols = LS_ROOT;
78571f95118Swdenk     } else if ((idx = dirdelim (fnamecopy)) >= 0) {
78671f95118Swdenk 	isdir = 1;
78771f95118Swdenk 	fnamecopy[idx] = '\0';
78871f95118Swdenk 	subname = fnamecopy + idx + 1;
78971f95118Swdenk 	/* Handle multiple delimiters */
79071f95118Swdenk 	while (ISDIRDELIM (*subname))
79171f95118Swdenk 	    subname++;
79271f95118Swdenk     } else if (dols) {
79371f95118Swdenk 	isdir = 1;
79471f95118Swdenk     }
79571f95118Swdenk 
79671f95118Swdenk     while (1) {
79771f95118Swdenk 	int i;
79871f95118Swdenk 
7995fa66df6Swdenk 	if (disk_read (cursect, mydata->clust_size, do_fat_read_block) < 0) {
80071f95118Swdenk 	    FAT_DPRINT ("Error: reading rootdir block\n");
80171f95118Swdenk 	    return -1;
80271f95118Swdenk 	}
8035fa66df6Swdenk 	dentptr = (dir_entry *) do_fat_read_block;
80471f95118Swdenk 	for (i = 0; i < DIRENTSPERBLOCK; i++) {
80571f95118Swdenk 	    char s_name[14], l_name[256];
80671f95118Swdenk 
80771f95118Swdenk 	    l_name[0] = '\0';
80871f95118Swdenk 	    if ((dentptr->attr & ATTR_VOLUME)) {
80971f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT
81071f95118Swdenk 		if ((dentptr->attr & ATTR_VFAT) &&
8112d1a537dSwdenk 		    (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
81271f95118Swdenk 		    prevcksum = ((dir_slot *) dentptr)->alias_checksum;
8135fa66df6Swdenk 		    get_vfatname (mydata, 0, do_fat_read_block, dentptr, l_name);
81471f95118Swdenk 		    if (dols == LS_ROOT) {
81571f95118Swdenk 			int isdir = (dentptr->attr & ATTR_DIR);
81671f95118Swdenk 			char dirc;
81771f95118Swdenk 			int doit = 0;
81871f95118Swdenk 
81971f95118Swdenk 			if (isdir) {
82071f95118Swdenk 			    dirs++;
82171f95118Swdenk 			    dirc = '/';
82271f95118Swdenk 			    doit = 1;
82371f95118Swdenk 			} else {
82471f95118Swdenk 			    dirc = ' ';
82571f95118Swdenk 			    if (l_name[0] != 0) {
82671f95118Swdenk 				files++;
82771f95118Swdenk 				doit = 1;
82871f95118Swdenk 			    }
82971f95118Swdenk 			}
83071f95118Swdenk 			if (doit) {
83171f95118Swdenk 			    if (dirc == ' ') {
83271f95118Swdenk 				printf (" %8ld   %s%c\n",
83371f95118Swdenk 					(long) FAT2CPU32 (dentptr->size),
83471f95118Swdenk 					l_name, dirc);
83571f95118Swdenk 			    } else {
83671f95118Swdenk 				printf ("            %s%c\n", l_name, dirc);
83771f95118Swdenk 			    }
83871f95118Swdenk 			}
83971f95118Swdenk 			dentptr++;
84071f95118Swdenk 			continue;
84171f95118Swdenk 		    }
84271f95118Swdenk 		    FAT_DPRINT ("Rootvfatname: |%s|\n", l_name);
84371f95118Swdenk 		} else
84471f95118Swdenk #endif
84571f95118Swdenk 		{
84671f95118Swdenk 		    /* Volume label or VFAT entry */
84771f95118Swdenk 		    dentptr++;
84871f95118Swdenk 		    continue;
84971f95118Swdenk 		}
85071f95118Swdenk 	    } else if (dentptr->name[0] == 0) {
85171f95118Swdenk 		FAT_DPRINT ("RootDentname == NULL - %d\n", i);
85271f95118Swdenk 		if (dols == LS_ROOT) {
85371f95118Swdenk 		    printf ("\n%d file(s), %d dir(s)\n\n", files, dirs);
85471f95118Swdenk 		    return 0;
85571f95118Swdenk 		}
85671f95118Swdenk 		return -1;
85771f95118Swdenk 	    }
85871f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT
85971f95118Swdenk 	    else if (dols == LS_ROOT
86071f95118Swdenk 		     && mkcksum (dentptr->name) == prevcksum) {
86171f95118Swdenk 		dentptr++;
86271f95118Swdenk 		continue;
86371f95118Swdenk 	    }
86471f95118Swdenk #endif
86571f95118Swdenk 	    get_name (dentptr, s_name);
86671f95118Swdenk 	    if (dols == LS_ROOT) {
86771f95118Swdenk 		int isdir = (dentptr->attr & ATTR_DIR);
86871f95118Swdenk 		char dirc;
86971f95118Swdenk 		int doit = 0;
87071f95118Swdenk 
87171f95118Swdenk 		if (isdir) {
87271f95118Swdenk 		    dirc = '/';
873a43278a4Swdenk 		    if (s_name[0] != 0) {
874a43278a4Swdenk 			dirs++;
87571f95118Swdenk 			doit = 1;
876a43278a4Swdenk 		    }
87771f95118Swdenk 		} else {
87871f95118Swdenk 		    dirc = ' ';
87971f95118Swdenk 		    if (s_name[0] != 0) {
88071f95118Swdenk 			files++;
88171f95118Swdenk 			doit = 1;
88271f95118Swdenk 		    }
88371f95118Swdenk 		}
88471f95118Swdenk 		if (doit) {
88571f95118Swdenk 		    if (dirc == ' ') {
88671f95118Swdenk 			printf (" %8ld   %s%c\n",
88771f95118Swdenk 				(long) FAT2CPU32 (dentptr->size), s_name,
88871f95118Swdenk 				dirc);
88971f95118Swdenk 		    } else {
89071f95118Swdenk 			printf ("            %s%c\n", s_name, dirc);
89171f95118Swdenk 		    }
89271f95118Swdenk 		}
89371f95118Swdenk 		dentptr++;
89471f95118Swdenk 		continue;
89571f95118Swdenk 	    }
89671f95118Swdenk 	    if (strcmp (fnamecopy, s_name) && strcmp (fnamecopy, l_name)) {
89771f95118Swdenk 		FAT_DPRINT ("RootMismatch: |%s|%s|\n", s_name, l_name);
89871f95118Swdenk 		dentptr++;
89971f95118Swdenk 		continue;
90071f95118Swdenk 	    }
90171f95118Swdenk 	    if (isdir && !(dentptr->attr & ATTR_DIR))
90271f95118Swdenk 		return -1;
90371f95118Swdenk 
90471f95118Swdenk 	    FAT_DPRINT ("RootName: %s", s_name);
90571f95118Swdenk 	    FAT_DPRINT (", start: 0x%x", START (dentptr));
90671f95118Swdenk 	    FAT_DPRINT (", size:  0x%x %s\n",
90771f95118Swdenk 			FAT2CPU32 (dentptr->size), isdir ? "(DIR)" : "");
90871f95118Swdenk 
90971f95118Swdenk 	    goto rootdir_done;  /* We got a match */
91071f95118Swdenk 	}
91171f95118Swdenk 	cursect++;
91271f95118Swdenk     }
91371f95118Swdenk   rootdir_done:
91471f95118Swdenk 
91571f95118Swdenk     firsttime = 1;
91671f95118Swdenk     while (isdir) {
91771f95118Swdenk 	int startsect = mydata->data_begin
91871f95118Swdenk 		+ START (dentptr) * mydata->clust_size;
91971f95118Swdenk 	dir_entry dent;
92071f95118Swdenk 	char *nextname = NULL;
92171f95118Swdenk 
92271f95118Swdenk 	dent = *dentptr;
92371f95118Swdenk 	dentptr = &dent;
92471f95118Swdenk 
92571f95118Swdenk 	idx = dirdelim (subname);
92671f95118Swdenk 	if (idx >= 0) {
92771f95118Swdenk 	    subname[idx] = '\0';
92871f95118Swdenk 	    nextname = subname + idx + 1;
92971f95118Swdenk 	    /* Handle multiple delimiters */
93071f95118Swdenk 	    while (ISDIRDELIM (*nextname))
93171f95118Swdenk 		nextname++;
93271f95118Swdenk 	    if (dols && *nextname == '\0')
93371f95118Swdenk 		firsttime = 0;
93471f95118Swdenk 	} else {
93571f95118Swdenk 	    if (dols && firsttime) {
93671f95118Swdenk 		firsttime = 0;
93771f95118Swdenk 	    } else {
93871f95118Swdenk 		isdir = 0;
93971f95118Swdenk 	    }
94071f95118Swdenk 	}
94171f95118Swdenk 
94271f95118Swdenk 	if (get_dentfromdir (mydata, startsect, subname, dentptr,
94371f95118Swdenk 			     isdir ? 0 : dols) == NULL) {
94471f95118Swdenk 	    if (dols && !isdir)
94571f95118Swdenk 		return 0;
94671f95118Swdenk 	    return -1;
94771f95118Swdenk 	}
94871f95118Swdenk 
94971f95118Swdenk 	if (idx >= 0) {
95071f95118Swdenk 	    if (!(dentptr->attr & ATTR_DIR))
95171f95118Swdenk 		return -1;
95271f95118Swdenk 	    subname = nextname;
95371f95118Swdenk 	}
95471f95118Swdenk     }
95571f95118Swdenk     ret = get_contents (mydata, dentptr, buffer, maxsize);
95671f95118Swdenk     FAT_DPRINT ("Size: %d, got: %ld\n", FAT2CPU32 (dentptr->size), ret);
95771f95118Swdenk 
95871f95118Swdenk     return ret;
95971f95118Swdenk }
96071f95118Swdenk 
96171f95118Swdenk 
96271f95118Swdenk int
96371f95118Swdenk file_fat_detectfs(void)
96471f95118Swdenk {
96571f95118Swdenk 	boot_sector	bs;
96671f95118Swdenk 	volume_info	volinfo;
96771f95118Swdenk 	int		fatsize;
9687205e407Swdenk 	char	vol_label[12];
96971f95118Swdenk 
9707205e407Swdenk 	if(cur_dev==NULL) {
9717205e407Swdenk 		printf("No current device\n");
9727205e407Swdenk 		return 1;
9737205e407Swdenk 	}
974a2663ea4Swdenk #if (CONFIG_COMMANDS & CFG_CMD_IDE) || (CONFIG_COMMANDS & CFG_CMD_SCSI) || \
975a2663ea4Swdenk     (CONFIG_COMMANDS & CFG_CMD_USB) || (CONFIG_MMC)
9767205e407Swdenk 	printf("Interface:  ");
9777205e407Swdenk 	switch(cur_dev->if_type) {
9787205e407Swdenk 		case IF_TYPE_IDE :	printf("IDE"); break;
9797205e407Swdenk 		case IF_TYPE_SCSI :	printf("SCSI"); break;
9807205e407Swdenk 		case IF_TYPE_ATAPI :	printf("ATAPI"); break;
9817205e407Swdenk 		case IF_TYPE_USB :	printf("USB"); break;
9827205e407Swdenk 		case IF_TYPE_DOC :	printf("DOC"); break;
9837205e407Swdenk 		case IF_TYPE_MMC :	printf("MMC"); break;
9847205e407Swdenk 		default :		printf("Unknown");
9857205e407Swdenk 	}
9867205e407Swdenk 	printf("\n  Device %d: ",cur_dev->dev);
9877205e407Swdenk 	dev_print(cur_dev);
9887205e407Swdenk #endif
9897205e407Swdenk 	if(read_bootsectandvi(&bs, &volinfo, &fatsize)) {
9907205e407Swdenk 		printf("\nNo valid FAT fs found\n");
9917205e407Swdenk 		return 1;
9927205e407Swdenk 	}
9937205e407Swdenk 	memcpy (vol_label, volinfo.volume_label, 11);
9947205e407Swdenk 	vol_label[11] = '\0';
9957205e407Swdenk 	volinfo.fs_type[5]='\0';
9967205e407Swdenk 	printf("Partition %d: Filesystem: %s \"%s\"\n",cur_part,volinfo.fs_type,vol_label);
9977205e407Swdenk 	return 0;
99871f95118Swdenk }
99971f95118Swdenk 
100071f95118Swdenk 
100171f95118Swdenk int
100271f95118Swdenk file_fat_ls(const char *dir)
100371f95118Swdenk {
100471f95118Swdenk 	return do_fat_read(dir, NULL, 0, LS_YES);
100571f95118Swdenk }
100671f95118Swdenk 
100771f95118Swdenk 
100871f95118Swdenk long
100971f95118Swdenk file_fat_read(const char *filename, void *buffer, unsigned long maxsize)
101071f95118Swdenk {
10117205e407Swdenk 	printf("reading %s\n",filename);
101271f95118Swdenk 	return do_fat_read(filename, buffer, maxsize, LS_NO);
101371f95118Swdenk }
101471f95118Swdenk 
101571f95118Swdenk #endif /* #if (CONFIG_COMMANDS & CFG_CMD_FAT) */
1016