xref: /rk3399_rockchip-uboot/fs/fat/fat.c (revision 6ad77d88e57f6ab815ec7e85c5ac329054318c73)
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>
349a800ac7SEric Nelson #include <malloc.h>
359a800ac7SEric Nelson #include <linux/compiler.h>
3671f95118Swdenk 
3771f95118Swdenk /*
3871f95118Swdenk  * Convert a string to lowercase.
3971f95118Swdenk  */
407385c28eSWolfgang Denk static void downcase(char *str)
4171f95118Swdenk {
4271f95118Swdenk 	while (*str != '\0') {
4371f95118Swdenk 		TOLOWER(*str);
4471f95118Swdenk 		str++;
4571f95118Swdenk 	}
4671f95118Swdenk }
4771f95118Swdenk 
489813b750SKyle Moffett static block_dev_desc_t *cur_dev;
499813b750SKyle Moffett static disk_partition_t cur_part_info;
507385c28eSWolfgang Denk 
519813b750SKyle Moffett #define DOS_BOOT_MAGIC_OFFSET	0x1fe
527205e407Swdenk #define DOS_FS_TYPE_OFFSET	0x36
5366c2d73cSWolfgang Denk #define DOS_FS32_TYPE_OFFSET	0x52
5471f95118Swdenk 
559813b750SKyle Moffett static int disk_read(__u32 block, __u32 nr_blocks, void *buf)
5671f95118Swdenk {
579813b750SKyle Moffett 	if (!cur_dev || !cur_dev->block_read)
587205e407Swdenk 		return -1;
597385c28eSWolfgang Denk 
609813b750SKyle Moffett 	return cur_dev->block_read(cur_dev->dev,
619813b750SKyle Moffett 			cur_part_info.start + block, nr_blocks, buf);
6271f95118Swdenk }
6371f95118Swdenk 
645e8f9831SStephen Warren int fat_set_blk_dev(block_dev_desc_t *dev_desc, disk_partition_t *info)
6571f95118Swdenk {
669a800ac7SEric Nelson 	ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz);
677205e407Swdenk 
689813b750SKyle Moffett 	cur_dev = dev_desc;
695e8f9831SStephen Warren 	cur_part_info = *info;
709813b750SKyle Moffett 
719813b750SKyle Moffett 	/* Make sure it has a valid FAT header */
729813b750SKyle Moffett 	if (disk_read(0, 1, buffer) != 1) {
739813b750SKyle Moffett 		cur_dev = NULL;
749813b750SKyle Moffett 		return -1;
757205e407Swdenk 	}
769813b750SKyle Moffett 
779813b750SKyle Moffett 	/* Check if it's actually a DOS volume */
789813b750SKyle Moffett 	if (memcmp(buffer + DOS_BOOT_MAGIC_OFFSET, "\x55\xAA", 2)) {
799813b750SKyle Moffett 		cur_dev = NULL;
809813b750SKyle Moffett 		return -1;
819813b750SKyle Moffett 	}
829813b750SKyle Moffett 
839813b750SKyle Moffett 	/* Check for FAT12/FAT16/FAT32 filesystem */
849813b750SKyle Moffett 	if (!memcmp(buffer + DOS_FS_TYPE_OFFSET, "FAT", 3))
8571f95118Swdenk 		return 0;
869813b750SKyle Moffett 	if (!memcmp(buffer + DOS_FS32_TYPE_OFFSET, "FAT32", 5))
879813b750SKyle Moffett 		return 0;
889813b750SKyle Moffett 
899813b750SKyle Moffett 	cur_dev = NULL;
909813b750SKyle Moffett 	return -1;
9171f95118Swdenk }
9271f95118Swdenk 
935e8f9831SStephen Warren int fat_register_device(block_dev_desc_t *dev_desc, int part_no)
945e8f9831SStephen Warren {
955e8f9831SStephen Warren 	disk_partition_t info;
965e8f9831SStephen Warren 
975e8f9831SStephen Warren 	/* First close any currently found FAT filesystem */
985e8f9831SStephen Warren 	cur_dev = NULL;
995e8f9831SStephen Warren 
1005e8f9831SStephen Warren 	/* Read the partition table, if present */
1015e8f9831SStephen Warren 	if (get_partition_info(dev_desc, part_no, &info)) {
1025e8f9831SStephen Warren 		if (part_no != 0) {
1035e8f9831SStephen Warren 			printf("** Partition %d not valid on device %d **\n",
1045e8f9831SStephen Warren 					part_no, dev_desc->dev);
1055e8f9831SStephen Warren 			return -1;
1065e8f9831SStephen Warren 		}
1075e8f9831SStephen Warren 
1085e8f9831SStephen Warren 		info.start = 0;
1095e8f9831SStephen Warren 		info.size = dev_desc->lba;
1105e8f9831SStephen Warren 		info.blksz = dev_desc->blksz;
1115e8f9831SStephen Warren 		info.name[0] = 0;
1125e8f9831SStephen Warren 		info.type[0] = 0;
1135e8f9831SStephen Warren 		info.bootable = 0;
1145e8f9831SStephen Warren #ifdef CONFIG_PARTITION_UUIDS
1155e8f9831SStephen Warren 		info.uuid[0] = 0;
1165e8f9831SStephen Warren #endif
1175e8f9831SStephen Warren 	}
1185e8f9831SStephen Warren 
1195e8f9831SStephen Warren 	return fat_set_blk_dev(dev_desc, &info);
1205e8f9831SStephen Warren }
1219813b750SKyle Moffett 
12271f95118Swdenk /*
12371f95118Swdenk  * Get the first occurence of a directory delimiter ('/' or '\') in a string.
12471f95118Swdenk  * Return index into string if found, -1 otherwise.
12571f95118Swdenk  */
1267385c28eSWolfgang Denk static int dirdelim(char *str)
12771f95118Swdenk {
12871f95118Swdenk 	char *start = str;
12971f95118Swdenk 
13071f95118Swdenk 	while (*str != '\0') {
1317385c28eSWolfgang Denk 		if (ISDIRDELIM(*str))
1327385c28eSWolfgang Denk 			return str - start;
13371f95118Swdenk 		str++;
13471f95118Swdenk 	}
13571f95118Swdenk 	return -1;
13671f95118Swdenk }
13771f95118Swdenk 
13871f95118Swdenk /*
13971f95118Swdenk  * Extract zero terminated short name from a directory entry.
14071f95118Swdenk  */
14171f95118Swdenk static void get_name(dir_entry *dirent, char *s_name)
14271f95118Swdenk {
14371f95118Swdenk 	char *ptr;
14471f95118Swdenk 
14571f95118Swdenk 	memcpy(s_name, dirent->name, 8);
14671f95118Swdenk 	s_name[8] = '\0';
14771f95118Swdenk 	ptr = s_name;
14871f95118Swdenk 	while (*ptr && *ptr != ' ')
14971f95118Swdenk 		ptr++;
15071f95118Swdenk 	if (dirent->ext[0] && dirent->ext[0] != ' ') {
15171f95118Swdenk 		*ptr = '.';
15271f95118Swdenk 		ptr++;
15371f95118Swdenk 		memcpy(ptr, dirent->ext, 3);
15471f95118Swdenk 		ptr[3] = '\0';
15571f95118Swdenk 		while (*ptr && *ptr != ' ')
15671f95118Swdenk 			ptr++;
15771f95118Swdenk 	}
15871f95118Swdenk 	*ptr = '\0';
15971f95118Swdenk 	if (*s_name == DELETED_FLAG)
16071f95118Swdenk 		*s_name = '\0';
16171f95118Swdenk 	else if (*s_name == aRING)
1623c2c2f42SRemy Bohmer 		*s_name = DELETED_FLAG;
16371f95118Swdenk 	downcase(s_name);
16471f95118Swdenk }
16571f95118Swdenk 
16671f95118Swdenk /*
16771f95118Swdenk  * Get the entry at index 'entry' in a FAT (12/16/32) table.
16871f95118Swdenk  * On failure 0x00 is returned.
16971f95118Swdenk  */
1707385c28eSWolfgang Denk static __u32 get_fatent(fsdata *mydata, __u32 entry)
17171f95118Swdenk {
17271f95118Swdenk 	__u32 bufnum;
1737385c28eSWolfgang Denk 	__u32 off16, offset;
17471f95118Swdenk 	__u32 ret = 0x00;
1757385c28eSWolfgang Denk 	__u16 val1, val2;
17671f95118Swdenk 
17771f95118Swdenk 	switch (mydata->fatsize) {
17871f95118Swdenk 	case 32:
17971f95118Swdenk 		bufnum = entry / FAT32BUFSIZE;
18071f95118Swdenk 		offset = entry - bufnum * FAT32BUFSIZE;
18171f95118Swdenk 		break;
18271f95118Swdenk 	case 16:
18371f95118Swdenk 		bufnum = entry / FAT16BUFSIZE;
18471f95118Swdenk 		offset = entry - bufnum * FAT16BUFSIZE;
18571f95118Swdenk 		break;
18671f95118Swdenk 	case 12:
18771f95118Swdenk 		bufnum = entry / FAT12BUFSIZE;
18871f95118Swdenk 		offset = entry - bufnum * FAT12BUFSIZE;
18971f95118Swdenk 		break;
19071f95118Swdenk 
19171f95118Swdenk 	default:
19271f95118Swdenk 		/* Unsupported FAT size */
19371f95118Swdenk 		return ret;
19471f95118Swdenk 	}
19571f95118Swdenk 
1967385c28eSWolfgang Denk 	debug("FAT%d: entry: 0x%04x = %d, offset: 0x%04x = %d\n",
1972aa98c66SWolfgang Denk 	       mydata->fatsize, entry, entry, offset, offset);
1982aa98c66SWolfgang Denk 
19971f95118Swdenk 	/* Read a new block of FAT entries into the cache. */
20071f95118Swdenk 	if (bufnum != mydata->fatbufnum) {
20160b36f0fSSergei Shtylyov 		__u32 getsize = FATBUFBLOCKS;
20271f95118Swdenk 		__u8 *bufptr = mydata->fatbuf;
20371f95118Swdenk 		__u32 fatlength = mydata->fatlength;
20471f95118Swdenk 		__u32 startblock = bufnum * FATBUFBLOCKS;
20571f95118Swdenk 
2068006dd2eSBenoît Thébaudeau 		if (startblock + getsize > fatlength)
2078006dd2eSBenoît Thébaudeau 			getsize = fatlength - startblock;
20860b36f0fSSergei Shtylyov 
20971f95118Swdenk 		startblock += mydata->fat_sect;	/* Offset from start of disk */
21071f95118Swdenk 
21171f95118Swdenk 		if (disk_read(startblock, getsize, bufptr) < 0) {
2127385c28eSWolfgang Denk 			debug("Error reading FAT blocks\n");
21371f95118Swdenk 			return ret;
21471f95118Swdenk 		}
21571f95118Swdenk 		mydata->fatbufnum = bufnum;
21671f95118Swdenk 	}
21771f95118Swdenk 
21871f95118Swdenk 	/* Get the actual entry from the table */
21971f95118Swdenk 	switch (mydata->fatsize) {
22071f95118Swdenk 	case 32:
22171f95118Swdenk 		ret = FAT2CPU32(((__u32 *) mydata->fatbuf)[offset]);
22271f95118Swdenk 		break;
22371f95118Swdenk 	case 16:
22471f95118Swdenk 		ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[offset]);
22571f95118Swdenk 		break;
2267385c28eSWolfgang Denk 	case 12:
2277385c28eSWolfgang Denk 		off16 = (offset * 3) / 4;
22871f95118Swdenk 
22971f95118Swdenk 		switch (offset & 0x3) {
23071f95118Swdenk 		case 0:
23171f95118Swdenk 			ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[off16]);
23271f95118Swdenk 			ret &= 0xfff;
23371f95118Swdenk 			break;
23471f95118Swdenk 		case 1:
23571f95118Swdenk 			val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
23671f95118Swdenk 			val1 &= 0xf000;
23771f95118Swdenk 			val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]);
23871f95118Swdenk 			val2 &= 0x00ff;
23971f95118Swdenk 			ret = (val2 << 4) | (val1 >> 12);
24071f95118Swdenk 			break;
24171f95118Swdenk 		case 2:
24271f95118Swdenk 			val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
24371f95118Swdenk 			val1 &= 0xff00;
24471f95118Swdenk 			val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]);
24571f95118Swdenk 			val2 &= 0x000f;
24671f95118Swdenk 			ret = (val2 << 8) | (val1 >> 8);
24771f95118Swdenk 			break;
24871f95118Swdenk 		case 3:
2497385c28eSWolfgang Denk 			ret = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
25071f95118Swdenk 			ret = (ret & 0xfff0) >> 4;
25171f95118Swdenk 			break;
25271f95118Swdenk 		default:
25371f95118Swdenk 			break;
25471f95118Swdenk 		}
25571f95118Swdenk 		break;
25671f95118Swdenk 	}
2577385c28eSWolfgang Denk 	debug("FAT%d: ret: %08x, offset: %04x\n",
2582aa98c66SWolfgang Denk 	       mydata->fatsize, ret, offset);
25971f95118Swdenk 
26071f95118Swdenk 	return ret;
26171f95118Swdenk }
26271f95118Swdenk 
26371f95118Swdenk /*
26471f95118Swdenk  * Read at most 'size' bytes from the specified cluster into 'buffer'.
26571f95118Swdenk  * Return 0 on success, -1 otherwise.
26671f95118Swdenk  */
26771f95118Swdenk static int
2689795e07bSBenoît Thébaudeau get_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size)
26971f95118Swdenk {
2703f270f42SErik Hansen 	__u32 idx = 0;
27171f95118Swdenk 	__u32 startsect;
27246236b14SKyle Moffett 	int ret;
27371f95118Swdenk 
27471f95118Swdenk 	if (clustnum > 0) {
2757385c28eSWolfgang Denk 		startsect = mydata->data_begin +
2767385c28eSWolfgang Denk 				clustnum * mydata->clust_size;
27771f95118Swdenk 	} else {
27871f95118Swdenk 		startsect = mydata->rootdir_sect;
27971f95118Swdenk 	}
28071f95118Swdenk 
2817385c28eSWolfgang Denk 	debug("gc - clustnum: %d, startsect: %d\n", clustnum, startsect);
2827385c28eSWolfgang Denk 
283cc63b25eSBenoît Thébaudeau 	if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
2849a800ac7SEric Nelson 		ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
2857385c28eSWolfgang Denk 
286cc63b25eSBenoît Thébaudeau 		printf("FAT: Misaligned buffer address (%p)\n", buffer);
287cc63b25eSBenoît Thébaudeau 
288cc63b25eSBenoît Thébaudeau 		while (size >= mydata->sect_size) {
289cc63b25eSBenoît Thébaudeau 			ret = disk_read(startsect++, 1, tmpbuf);
29046236b14SKyle Moffett 			if (ret != 1) {
29146236b14SKyle Moffett 				debug("Error reading data (got %d)\n", ret);
29271f95118Swdenk 				return -1;
29371f95118Swdenk 			}
29471f95118Swdenk 
295cc63b25eSBenoît Thébaudeau 			memcpy(buffer, tmpbuf, mydata->sect_size);
296cc63b25eSBenoît Thébaudeau 			buffer += mydata->sect_size;
297cc63b25eSBenoît Thébaudeau 			size -= mydata->sect_size;
298cc63b25eSBenoît Thébaudeau 		}
299cc63b25eSBenoît Thébaudeau 	} else {
300cc63b25eSBenoît Thébaudeau 		idx = size / mydata->sect_size;
301cc63b25eSBenoît Thébaudeau 		ret = disk_read(startsect, idx, buffer);
302cc63b25eSBenoît Thébaudeau 		if (ret != idx) {
303cc63b25eSBenoît Thébaudeau 			debug("Error reading data (got %d)\n", ret);
304cc63b25eSBenoît Thébaudeau 			return -1;
305cc63b25eSBenoît Thébaudeau 		}
306cc63b25eSBenoît Thébaudeau 		startsect += idx;
307cc63b25eSBenoît Thébaudeau 		idx *= mydata->sect_size;
308cc63b25eSBenoît Thébaudeau 		buffer += idx;
309cc63b25eSBenoît Thébaudeau 		size -= idx;
310cc63b25eSBenoît Thébaudeau 	}
311cc63b25eSBenoît Thébaudeau 	if (size) {
312cc63b25eSBenoît Thébaudeau 		ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
313cc63b25eSBenoît Thébaudeau 
314cc63b25eSBenoît Thébaudeau 		ret = disk_read(startsect, 1, tmpbuf);
315cc63b25eSBenoît Thébaudeau 		if (ret != 1) {
316cc63b25eSBenoît Thébaudeau 			debug("Error reading data (got %d)\n", ret);
317cc63b25eSBenoît Thébaudeau 			return -1;
318cc63b25eSBenoît Thébaudeau 		}
319cc63b25eSBenoît Thébaudeau 
320cc63b25eSBenoît Thébaudeau 		memcpy(buffer, tmpbuf, size);
32171f95118Swdenk 	}
32271f95118Swdenk 
32371f95118Swdenk 	return 0;
32471f95118Swdenk }
32571f95118Swdenk 
32671f95118Swdenk /*
3271170e634SBenoît Thébaudeau  * Read at most 'maxsize' bytes from 'pos' in the file associated with 'dentptr'
32871f95118Swdenk  * into 'buffer'.
32971f95118Swdenk  * Return the number of bytes read or -1 on fatal errors.
33071f95118Swdenk  */
3311170e634SBenoît Thébaudeau __u8 get_contents_vfatname_block[MAX_CLUSTSIZE]
3321170e634SBenoît Thébaudeau 	__aligned(ARCH_DMA_MINALIGN);
3331170e634SBenoît Thébaudeau 
33471f95118Swdenk static long
3351170e634SBenoît Thébaudeau get_contents(fsdata *mydata, dir_entry *dentptr, unsigned long pos,
3361170e634SBenoît Thébaudeau 	     __u8 *buffer, unsigned long maxsize)
33771f95118Swdenk {
33871f95118Swdenk 	unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0;
339ac497771SSergei Shtylyov 	unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
34071f95118Swdenk 	__u32 curclust = START(dentptr);
3417205e407Swdenk 	__u32 endclust, newclust;
3427205e407Swdenk 	unsigned long actsize;
34371f95118Swdenk 
3447385c28eSWolfgang Denk 	debug("Filesize: %ld bytes\n", filesize);
34571f95118Swdenk 
3461170e634SBenoît Thébaudeau 	if (pos >= filesize) {
3471170e634SBenoît Thébaudeau 		debug("Read position past EOF: %lu\n", pos);
3481170e634SBenoît Thébaudeau 		return gotsize;
3491170e634SBenoît Thébaudeau 	}
3501170e634SBenoît Thébaudeau 
3511170e634SBenoît Thébaudeau 	if (maxsize > 0 && filesize > pos + maxsize)
3521170e634SBenoît Thébaudeau 		filesize = pos + maxsize;
35371f95118Swdenk 
3547385c28eSWolfgang Denk 	debug("%ld bytes\n", filesize);
35571f95118Swdenk 
3567205e407Swdenk 	actsize = bytesperclust;
3571170e634SBenoît Thébaudeau 
3581170e634SBenoît Thébaudeau 	/* go to cluster at pos */
3591170e634SBenoît Thébaudeau 	while (actsize <= pos) {
3601170e634SBenoît Thébaudeau 		curclust = get_fatent(mydata, curclust);
3611170e634SBenoît Thébaudeau 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
3621170e634SBenoît Thébaudeau 			debug("curclust: 0x%x\n", curclust);
3631170e634SBenoît Thébaudeau 			debug("Invalid FAT entry\n");
3641170e634SBenoît Thébaudeau 			return gotsize;
3651170e634SBenoît Thébaudeau 		}
3661170e634SBenoît Thébaudeau 		actsize += bytesperclust;
3671170e634SBenoît Thébaudeau 	}
3681170e634SBenoît Thébaudeau 
3691170e634SBenoît Thébaudeau 	/* actsize > pos */
3701170e634SBenoît Thébaudeau 	actsize -= bytesperclust;
3711170e634SBenoît Thébaudeau 	filesize -= actsize;
3721170e634SBenoît Thébaudeau 	pos -= actsize;
3731170e634SBenoît Thébaudeau 
3741170e634SBenoît Thébaudeau 	/* align to beginning of next cluster if any */
3751170e634SBenoît Thébaudeau 	if (pos) {
3761170e634SBenoît Thébaudeau 		actsize = min(filesize, bytesperclust);
3771170e634SBenoît Thébaudeau 		if (get_cluster(mydata, curclust, get_contents_vfatname_block,
3781170e634SBenoît Thébaudeau 				(int)actsize) != 0) {
3791170e634SBenoît Thébaudeau 			printf("Error reading cluster\n");
3801170e634SBenoît Thébaudeau 			return -1;
3811170e634SBenoît Thébaudeau 		}
3821170e634SBenoît Thébaudeau 		filesize -= actsize;
3831170e634SBenoît Thébaudeau 		actsize -= pos;
3841170e634SBenoît Thébaudeau 		memcpy(buffer, get_contents_vfatname_block + pos, actsize);
3851170e634SBenoît Thébaudeau 		gotsize += actsize;
3861170e634SBenoît Thébaudeau 		if (!filesize)
3871170e634SBenoît Thébaudeau 			return gotsize;
3881170e634SBenoît Thébaudeau 		buffer += actsize;
3891170e634SBenoît Thébaudeau 
3901170e634SBenoît Thébaudeau 		curclust = get_fatent(mydata, curclust);
3911170e634SBenoît Thébaudeau 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
3921170e634SBenoît Thébaudeau 			debug("curclust: 0x%x\n", curclust);
3931170e634SBenoît Thébaudeau 			debug("Invalid FAT entry\n");
3941170e634SBenoît Thébaudeau 			return gotsize;
3951170e634SBenoît Thébaudeau 		}
3961170e634SBenoît Thébaudeau 	}
3971170e634SBenoît Thébaudeau 
3981170e634SBenoît Thébaudeau 	actsize = bytesperclust;
3997205e407Swdenk 	endclust = curclust;
4007385c28eSWolfgang Denk 
40171f95118Swdenk 	do {
4027205e407Swdenk 		/* search for consecutive clusters */
4037205e407Swdenk 		while (actsize < filesize) {
4047205e407Swdenk 			newclust = get_fatent(mydata, endclust);
4057205e407Swdenk 			if ((newclust - 1) != endclust)
4067205e407Swdenk 				goto getit;
4078ce4e5c2Smichael 			if (CHECK_CLUST(newclust, mydata->fatsize)) {
4087385c28eSWolfgang Denk 				debug("curclust: 0x%x\n", newclust);
4097385c28eSWolfgang Denk 				debug("Invalid FAT entry\n");
4107205e407Swdenk 				return gotsize;
4117205e407Swdenk 			}
4127205e407Swdenk 			endclust = newclust;
4137205e407Swdenk 			actsize += bytesperclust;
4147205e407Swdenk 		}
4157385c28eSWolfgang Denk 
4167205e407Swdenk 		/* get remaining bytes */
4177205e407Swdenk 		actsize = filesize;
4180880e5bbSBenoît Thébaudeau 		if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
4197385c28eSWolfgang Denk 			printf("Error reading cluster\n");
4207205e407Swdenk 			return -1;
4217205e407Swdenk 		}
4227205e407Swdenk 		gotsize += actsize;
4237205e407Swdenk 		return gotsize;
4247205e407Swdenk getit:
4257205e407Swdenk 		if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
4267385c28eSWolfgang Denk 			printf("Error reading cluster\n");
4277205e407Swdenk 			return -1;
4287205e407Swdenk 		}
4297205e407Swdenk 		gotsize += (int)actsize;
4307205e407Swdenk 		filesize -= actsize;
4317205e407Swdenk 		buffer += actsize;
4327385c28eSWolfgang Denk 
4337205e407Swdenk 		curclust = get_fatent(mydata, endclust);
4348ce4e5c2Smichael 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
4357385c28eSWolfgang Denk 			debug("curclust: 0x%x\n", curclust);
4367385c28eSWolfgang Denk 			printf("Invalid FAT entry\n");
43771f95118Swdenk 			return gotsize;
43871f95118Swdenk 		}
4397205e407Swdenk 		actsize = bytesperclust;
4407205e407Swdenk 		endclust = curclust;
44171f95118Swdenk 	} while (1);
44271f95118Swdenk }
44371f95118Swdenk 
44471f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT
44571f95118Swdenk /*
44671f95118Swdenk  * Extract the file name information from 'slotptr' into 'l_name',
44771f95118Swdenk  * starting at l_name[*idx].
44871f95118Swdenk  * Return 1 if terminator (zero byte) is found, 0 otherwise.
44971f95118Swdenk  */
4507385c28eSWolfgang Denk static int slot2str(dir_slot *slotptr, char *l_name, int *idx)
45171f95118Swdenk {
45271f95118Swdenk 	int j;
45371f95118Swdenk 
45471f95118Swdenk 	for (j = 0; j <= 8; j += 2) {
45571f95118Swdenk 		l_name[*idx] = slotptr->name0_4[j];
4567385c28eSWolfgang Denk 		if (l_name[*idx] == 0x00)
4577385c28eSWolfgang Denk 			return 1;
45871f95118Swdenk 		(*idx)++;
45971f95118Swdenk 	}
46071f95118Swdenk 	for (j = 0; j <= 10; j += 2) {
46171f95118Swdenk 		l_name[*idx] = slotptr->name5_10[j];
4627385c28eSWolfgang Denk 		if (l_name[*idx] == 0x00)
4637385c28eSWolfgang Denk 			return 1;
46471f95118Swdenk 		(*idx)++;
46571f95118Swdenk 	}
46671f95118Swdenk 	for (j = 0; j <= 2; j += 2) {
46771f95118Swdenk 		l_name[*idx] = slotptr->name11_12[j];
4687385c28eSWolfgang Denk 		if (l_name[*idx] == 0x00)
4697385c28eSWolfgang Denk 			return 1;
47071f95118Swdenk 		(*idx)++;
47171f95118Swdenk 	}
47271f95118Swdenk 
47371f95118Swdenk 	return 0;
47471f95118Swdenk }
47571f95118Swdenk 
47671f95118Swdenk /*
47771f95118Swdenk  * Extract the full long filename starting at 'retdent' (which is really
47871f95118Swdenk  * a slot) into 'l_name'. If successful also copy the real directory entry
47971f95118Swdenk  * into 'retdent'
48071f95118Swdenk  * Return 0 on success, -1 otherwise.
48171f95118Swdenk  */
48271f95118Swdenk static int
48371f95118Swdenk get_vfatname(fsdata *mydata, int curclust, __u8 *cluster,
48471f95118Swdenk 	     dir_entry *retdent, char *l_name)
48571f95118Swdenk {
48671f95118Swdenk 	dir_entry *realdent;
48771f95118Swdenk 	dir_slot *slotptr = (dir_slot *)retdent;
488025421eaSSergei Shtylyov 	__u8 *buflimit = cluster + mydata->sect_size * ((curclust == 0) ?
489025421eaSSergei Shtylyov 							PREFETCH_BLOCKS :
490025421eaSSergei Shtylyov 							mydata->clust_size);
4912d1a537dSwdenk 	__u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff;
49271f95118Swdenk 	int idx = 0;
49371f95118Swdenk 
4943831530dSMikhail Zolotaryov 	if (counter > VFAT_MAXSEQ) {
4953831530dSMikhail Zolotaryov 		debug("Error: VFAT name is too long\n");
4963831530dSMikhail Zolotaryov 		return -1;
4973831530dSMikhail Zolotaryov 	}
4983831530dSMikhail Zolotaryov 
4993831530dSMikhail Zolotaryov 	while ((__u8 *)slotptr < buflimit) {
5007385c28eSWolfgang Denk 		if (counter == 0)
5017385c28eSWolfgang Denk 			break;
5022d1a537dSwdenk 		if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter)
5032d1a537dSwdenk 			return -1;
50471f95118Swdenk 		slotptr++;
50571f95118Swdenk 		counter--;
50671f95118Swdenk 	}
50771f95118Swdenk 
5083831530dSMikhail Zolotaryov 	if ((__u8 *)slotptr >= buflimit) {
50971f95118Swdenk 		dir_slot *slotptr2;
51071f95118Swdenk 
5113831530dSMikhail Zolotaryov 		if (curclust == 0)
5123831530dSMikhail Zolotaryov 			return -1;
51371f95118Swdenk 		curclust = get_fatent(mydata, curclust);
5148ce4e5c2Smichael 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
5157385c28eSWolfgang Denk 			debug("curclust: 0x%x\n", curclust);
5167385c28eSWolfgang Denk 			printf("Invalid FAT entry\n");
51771f95118Swdenk 			return -1;
51871f95118Swdenk 		}
5197385c28eSWolfgang Denk 
5201170e634SBenoît Thébaudeau 		if (get_cluster(mydata, curclust, get_contents_vfatname_block,
521ac497771SSergei Shtylyov 				mydata->clust_size * mydata->sect_size) != 0) {
5227385c28eSWolfgang Denk 			debug("Error: reading directory block\n");
52371f95118Swdenk 			return -1;
52471f95118Swdenk 		}
5257385c28eSWolfgang Denk 
5261170e634SBenoît Thébaudeau 		slotptr2 = (dir_slot *)get_contents_vfatname_block;
5273831530dSMikhail Zolotaryov 		while (counter > 0) {
5283831530dSMikhail Zolotaryov 			if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK)
5293831530dSMikhail Zolotaryov 			    & 0xff) != counter)
5303831530dSMikhail Zolotaryov 				return -1;
53171f95118Swdenk 			slotptr2++;
5323831530dSMikhail Zolotaryov 			counter--;
5333831530dSMikhail Zolotaryov 		}
5347385c28eSWolfgang Denk 
53571f95118Swdenk 		/* Save the real directory entry */
5363831530dSMikhail Zolotaryov 		realdent = (dir_entry *)slotptr2;
5371170e634SBenoît Thébaudeau 		while ((__u8 *)slotptr2 > get_contents_vfatname_block) {
53871f95118Swdenk 			slotptr2--;
5393831530dSMikhail Zolotaryov 			slot2str(slotptr2, l_name, &idx);
54071f95118Swdenk 		}
54171f95118Swdenk 	} else {
54271f95118Swdenk 		/* Save the real directory entry */
54371f95118Swdenk 		realdent = (dir_entry *)slotptr;
54471f95118Swdenk 	}
54571f95118Swdenk 
54671f95118Swdenk 	do {
54771f95118Swdenk 		slotptr--;
5487385c28eSWolfgang Denk 		if (slot2str(slotptr, l_name, &idx))
5497385c28eSWolfgang Denk 			break;
5502d1a537dSwdenk 	} while (!(slotptr->id & LAST_LONG_ENTRY_MASK));
55171f95118Swdenk 
55271f95118Swdenk 	l_name[idx] = '\0';
5537385c28eSWolfgang Denk 	if (*l_name == DELETED_FLAG)
5547385c28eSWolfgang Denk 		*l_name = '\0';
5557385c28eSWolfgang Denk 	else if (*l_name == aRING)
5567385c28eSWolfgang Denk 		*l_name = DELETED_FLAG;
55771f95118Swdenk 	downcase(l_name);
55871f95118Swdenk 
55971f95118Swdenk 	/* Return the real directory entry */
56071f95118Swdenk 	memcpy(retdent, realdent, sizeof(dir_entry));
56171f95118Swdenk 
56271f95118Swdenk 	return 0;
56371f95118Swdenk }
56471f95118Swdenk 
56571f95118Swdenk /* Calculate short name checksum */
566ff04f6d1SMarek Vasut static __u8 mkcksum(const char name[8], const char ext[3])
56771f95118Swdenk {
56871f95118Swdenk 	int i;
5697385c28eSWolfgang Denk 
57071f95118Swdenk 	__u8 ret = 0;
57171f95118Swdenk 
572*6ad77d88SMarek Vasut 	for (i = 0; i < 8; i++)
573ff04f6d1SMarek Vasut 		ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + name[i];
574*6ad77d88SMarek Vasut 	for (i = 0; i < 3; i++)
575ff04f6d1SMarek Vasut 		ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + ext[i];
57671f95118Swdenk 
57771f95118Swdenk 	return ret;
57871f95118Swdenk }
5797385c28eSWolfgang Denk #endif	/* CONFIG_SUPPORT_VFAT */
58071f95118Swdenk 
58171f95118Swdenk /*
58271f95118Swdenk  * Get the directory entry associated with 'filename' from the directory
58371f95118Swdenk  * starting at 'startsect'
58471f95118Swdenk  */
5859a800ac7SEric Nelson __u8 get_dentfromdir_block[MAX_CLUSTSIZE]
5869a800ac7SEric Nelson 	__aligned(ARCH_DMA_MINALIGN);
5877385c28eSWolfgang Denk 
58871f95118Swdenk static dir_entry *get_dentfromdir(fsdata *mydata, int startsect,
58971f95118Swdenk 				  char *filename, dir_entry *retdent,
59071f95118Swdenk 				  int dols)
59171f95118Swdenk {
59271f95118Swdenk 	__u16 prevcksum = 0xffff;
59371f95118Swdenk 	__u32 curclust = START(retdent);
59471f95118Swdenk 	int files = 0, dirs = 0;
59571f95118Swdenk 
5967385c28eSWolfgang Denk 	debug("get_dentfromdir: %s\n", filename);
5977385c28eSWolfgang Denk 
59871f95118Swdenk 	while (1) {
59971f95118Swdenk 		dir_entry *dentptr;
6007385c28eSWolfgang Denk 
60171f95118Swdenk 		int i;
60271f95118Swdenk 
6035fa66df6Swdenk 		if (get_cluster(mydata, curclust, get_dentfromdir_block,
604ac497771SSergei Shtylyov 				mydata->clust_size * mydata->sect_size) != 0) {
6057385c28eSWolfgang Denk 			debug("Error: reading directory block\n");
60671f95118Swdenk 			return NULL;
60771f95118Swdenk 		}
6087385c28eSWolfgang Denk 
6095fa66df6Swdenk 		dentptr = (dir_entry *)get_dentfromdir_block;
6107385c28eSWolfgang Denk 
61171f95118Swdenk 		for (i = 0; i < DIRENTSPERCLUST; i++) {
6123831530dSMikhail Zolotaryov 			char s_name[14], l_name[VFAT_MAXLEN_BYTES];
61371f95118Swdenk 
61471f95118Swdenk 			l_name[0] = '\0';
615855a496fSwdenk 			if (dentptr->name[0] == DELETED_FLAG) {
616855a496fSwdenk 				dentptr++;
617855a496fSwdenk 				continue;
618855a496fSwdenk 			}
61971f95118Swdenk 			if ((dentptr->attr & ATTR_VOLUME)) {
62071f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT
621206d68fdSJ. Vijayanand 				if ((dentptr->attr & ATTR_VFAT) == ATTR_VFAT &&
6222d1a537dSwdenk 				    (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
6237385c28eSWolfgang Denk 					prevcksum = ((dir_slot *)dentptr)->alias_checksum;
6247385c28eSWolfgang Denk 					get_vfatname(mydata, curclust,
6257385c28eSWolfgang Denk 						     get_dentfromdir_block,
62671f95118Swdenk 						     dentptr, l_name);
62771f95118Swdenk 					if (dols) {
6287385c28eSWolfgang Denk 						int isdir;
62971f95118Swdenk 						char dirc;
63071f95118Swdenk 						int doit = 0;
63171f95118Swdenk 
6327385c28eSWolfgang Denk 						isdir = (dentptr->attr & ATTR_DIR);
6337385c28eSWolfgang Denk 
63471f95118Swdenk 						if (isdir) {
63571f95118Swdenk 							dirs++;
63671f95118Swdenk 							dirc = '/';
63771f95118Swdenk 							doit = 1;
63871f95118Swdenk 						} else {
63971f95118Swdenk 							dirc = ' ';
64071f95118Swdenk 							if (l_name[0] != 0) {
64171f95118Swdenk 								files++;
64271f95118Swdenk 								doit = 1;
64371f95118Swdenk 							}
64471f95118Swdenk 						}
64571f95118Swdenk 						if (doit) {
64671f95118Swdenk 							if (dirc == ' ') {
64771f95118Swdenk 								printf(" %8ld   %s%c\n",
64871f95118Swdenk 									(long)FAT2CPU32(dentptr->size),
6497385c28eSWolfgang Denk 									l_name,
6507385c28eSWolfgang Denk 									dirc);
65171f95118Swdenk 							} else {
6527385c28eSWolfgang Denk 								printf("            %s%c\n",
6537385c28eSWolfgang Denk 									l_name,
6547385c28eSWolfgang Denk 									dirc);
65571f95118Swdenk 							}
65671f95118Swdenk 						}
65771f95118Swdenk 						dentptr++;
65871f95118Swdenk 						continue;
65971f95118Swdenk 					}
6607385c28eSWolfgang Denk 					debug("vfatname: |%s|\n", l_name);
66171f95118Swdenk 				} else
66271f95118Swdenk #endif
66371f95118Swdenk 				{
66471f95118Swdenk 					/* Volume label or VFAT entry */
66571f95118Swdenk 					dentptr++;
66671f95118Swdenk 					continue;
66771f95118Swdenk 				}
66871f95118Swdenk 			}
66971f95118Swdenk 			if (dentptr->name[0] == 0) {
67071f95118Swdenk 				if (dols) {
6717385c28eSWolfgang Denk 					printf("\n%d file(s), %d dir(s)\n\n",
6727385c28eSWolfgang Denk 						files, dirs);
67371f95118Swdenk 				}
6747385c28eSWolfgang Denk 				debug("Dentname == NULL - %d\n", i);
67571f95118Swdenk 				return NULL;
67671f95118Swdenk 			}
67771f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT
678ff04f6d1SMarek Vasut 			__u8 csum = mkcksum(dentptr->name, dentptr->ext);
679ff04f6d1SMarek Vasut 			if (dols && csum == prevcksum) {
680bf34e7d9SSergei Shtylyov 				prevcksum = 0xffff;
68171f95118Swdenk 				dentptr++;
68271f95118Swdenk 				continue;
68371f95118Swdenk 			}
68471f95118Swdenk #endif
68571f95118Swdenk 			get_name(dentptr, s_name);
68671f95118Swdenk 			if (dols) {
68771f95118Swdenk 				int isdir = (dentptr->attr & ATTR_DIR);
68871f95118Swdenk 				char dirc;
68971f95118Swdenk 				int doit = 0;
69071f95118Swdenk 
69171f95118Swdenk 				if (isdir) {
69271f95118Swdenk 					dirs++;
69371f95118Swdenk 					dirc = '/';
69471f95118Swdenk 					doit = 1;
69571f95118Swdenk 				} else {
69671f95118Swdenk 					dirc = ' ';
69771f95118Swdenk 					if (s_name[0] != 0) {
69871f95118Swdenk 						files++;
69971f95118Swdenk 						doit = 1;
70071f95118Swdenk 					}
70171f95118Swdenk 				}
7027385c28eSWolfgang Denk 
70371f95118Swdenk 				if (doit) {
70471f95118Swdenk 					if (dirc == ' ') {
70571f95118Swdenk 						printf(" %8ld   %s%c\n",
7067385c28eSWolfgang Denk 							(long)FAT2CPU32(dentptr->size),
7077385c28eSWolfgang Denk 							s_name, dirc);
70871f95118Swdenk 					} else {
7097385c28eSWolfgang Denk 						printf("            %s%c\n",
7107385c28eSWolfgang Denk 							s_name, dirc);
71171f95118Swdenk 					}
71271f95118Swdenk 				}
7137385c28eSWolfgang Denk 
71471f95118Swdenk 				dentptr++;
71571f95118Swdenk 				continue;
71671f95118Swdenk 			}
7177385c28eSWolfgang Denk 
7187385c28eSWolfgang Denk 			if (strcmp(filename, s_name)
7197385c28eSWolfgang Denk 			    && strcmp(filename, l_name)) {
7207385c28eSWolfgang Denk 				debug("Mismatch: |%s|%s|\n", s_name, l_name);
72171f95118Swdenk 				dentptr++;
72271f95118Swdenk 				continue;
72371f95118Swdenk 			}
7247385c28eSWolfgang Denk 
72571f95118Swdenk 			memcpy(retdent, dentptr, sizeof(dir_entry));
72671f95118Swdenk 
7277385c28eSWolfgang Denk 			debug("DentName: %s", s_name);
7287385c28eSWolfgang Denk 			debug(", start: 0x%x", START(dentptr));
7297385c28eSWolfgang Denk 			debug(", size:  0x%x %s\n",
73071f95118Swdenk 			      FAT2CPU32(dentptr->size),
73171f95118Swdenk 			      (dentptr->attr & ATTR_DIR) ? "(DIR)" : "");
73271f95118Swdenk 
73371f95118Swdenk 			return retdent;
73471f95118Swdenk 		}
7357385c28eSWolfgang Denk 
73671f95118Swdenk 		curclust = get_fatent(mydata, curclust);
7378ce4e5c2Smichael 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
7387385c28eSWolfgang Denk 			debug("curclust: 0x%x\n", curclust);
7397385c28eSWolfgang Denk 			printf("Invalid FAT entry\n");
74071f95118Swdenk 			return NULL;
74171f95118Swdenk 		}
74271f95118Swdenk 	}
74371f95118Swdenk 
74471f95118Swdenk 	return NULL;
74571f95118Swdenk }
74671f95118Swdenk 
74771f95118Swdenk /*
74871f95118Swdenk  * Read boot sector and volume info from a FAT filesystem
74971f95118Swdenk  */
75071f95118Swdenk static int
75171f95118Swdenk read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize)
75271f95118Swdenk {
753ac497771SSergei Shtylyov 	__u8 *block;
75471f95118Swdenk 	volume_info *vistart;
755ac497771SSergei Shtylyov 	int ret = 0;
756ac497771SSergei Shtylyov 
757ac497771SSergei Shtylyov 	if (cur_dev == NULL) {
758ac497771SSergei Shtylyov 		debug("Error: no device selected\n");
759ac497771SSergei Shtylyov 		return -1;
760ac497771SSergei Shtylyov 	}
761ac497771SSergei Shtylyov 
7629a800ac7SEric Nelson 	block = memalign(ARCH_DMA_MINALIGN, cur_dev->blksz);
763ac497771SSergei Shtylyov 	if (block == NULL) {
764ac497771SSergei Shtylyov 		debug("Error: allocating block\n");
765ac497771SSergei Shtylyov 		return -1;
766ac497771SSergei Shtylyov 	}
76771f95118Swdenk 
76871f95118Swdenk 	if (disk_read(0, 1, block) < 0) {
7697385c28eSWolfgang Denk 		debug("Error: reading block\n");
770ac497771SSergei Shtylyov 		goto fail;
77171f95118Swdenk 	}
77271f95118Swdenk 
77371f95118Swdenk 	memcpy(bs, block, sizeof(boot_sector));
77471f95118Swdenk 	bs->reserved = FAT2CPU16(bs->reserved);
77571f95118Swdenk 	bs->fat_length = FAT2CPU16(bs->fat_length);
77671f95118Swdenk 	bs->secs_track = FAT2CPU16(bs->secs_track);
77771f95118Swdenk 	bs->heads = FAT2CPU16(bs->heads);
77871f95118Swdenk 	bs->total_sect = FAT2CPU32(bs->total_sect);
77971f95118Swdenk 
78071f95118Swdenk 	/* FAT32 entries */
78171f95118Swdenk 	if (bs->fat_length == 0) {
78271f95118Swdenk 		/* Assume FAT32 */
78371f95118Swdenk 		bs->fat32_length = FAT2CPU32(bs->fat32_length);
78471f95118Swdenk 		bs->flags = FAT2CPU16(bs->flags);
78571f95118Swdenk 		bs->root_cluster = FAT2CPU32(bs->root_cluster);
78671f95118Swdenk 		bs->info_sector = FAT2CPU16(bs->info_sector);
78771f95118Swdenk 		bs->backup_boot = FAT2CPU16(bs->backup_boot);
78871f95118Swdenk 		vistart = (volume_info *)(block + sizeof(boot_sector));
78971f95118Swdenk 		*fatsize = 32;
79071f95118Swdenk 	} else {
79171f95118Swdenk 		vistart = (volume_info *)&(bs->fat32_length);
79271f95118Swdenk 		*fatsize = 0;
79371f95118Swdenk 	}
79471f95118Swdenk 	memcpy(volinfo, vistart, sizeof(volume_info));
79571f95118Swdenk 
79671f95118Swdenk 	if (*fatsize == 32) {
7977385c28eSWolfgang Denk 		if (strncmp(FAT32_SIGN, vistart->fs_type, SIGNLEN) == 0)
798ac497771SSergei Shtylyov 			goto exit;
79971f95118Swdenk 	} else {
800651351feSTom Rix 		if (strncmp(FAT12_SIGN, vistart->fs_type, SIGNLEN) == 0) {
80171f95118Swdenk 			*fatsize = 12;
802ac497771SSergei Shtylyov 			goto exit;
80371f95118Swdenk 		}
804651351feSTom Rix 		if (strncmp(FAT16_SIGN, vistart->fs_type, SIGNLEN) == 0) {
80571f95118Swdenk 			*fatsize = 16;
806ac497771SSergei Shtylyov 			goto exit;
80771f95118Swdenk 		}
80871f95118Swdenk 	}
80971f95118Swdenk 
8107385c28eSWolfgang Denk 	debug("Error: broken fs_type sign\n");
811ac497771SSergei Shtylyov fail:
812ac497771SSergei Shtylyov 	ret = -1;
813ac497771SSergei Shtylyov exit:
814ac497771SSergei Shtylyov 	free(block);
815ac497771SSergei Shtylyov 	return ret;
81671f95118Swdenk }
81771f95118Swdenk 
8181170e634SBenoît Thébaudeau __u8 do_fat_read_at_block[MAX_CLUSTSIZE]
8199a800ac7SEric Nelson 	__aligned(ARCH_DMA_MINALIGN);
8207385c28eSWolfgang Denk 
82120cc00ddSstroese long
8221170e634SBenoît Thébaudeau do_fat_read_at(const char *filename, unsigned long pos, void *buffer,
8231170e634SBenoît Thébaudeau 	       unsigned long maxsize, int dols)
82471f95118Swdenk {
82571f95118Swdenk 	char fnamecopy[2048];
82671f95118Swdenk 	boot_sector bs;
82771f95118Swdenk 	volume_info volinfo;
82871f95118Swdenk 	fsdata datablock;
82971f95118Swdenk 	fsdata *mydata = &datablock;
830cd1b042cSBenoît Thébaudeau 	dir_entry *dentptr = NULL;
83171f95118Swdenk 	__u16 prevcksum = 0xffff;
83271f95118Swdenk 	char *subname = "";
8333f270f42SErik Hansen 	__u32 cursect;
83471f95118Swdenk 	int idx, isdir = 0;
83571f95118Swdenk 	int files = 0, dirs = 0;
836ac497771SSergei Shtylyov 	long ret = -1;
83771f95118Swdenk 	int firsttime;
83840e21916SSergei Shtylyov 	__u32 root_cluster = 0;
8393f270f42SErik Hansen 	int rootdir_size = 0;
8402aa98c66SWolfgang Denk 	int j;
84171f95118Swdenk 
84271f95118Swdenk 	if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) {
8437385c28eSWolfgang Denk 		debug("Error: reading boot sector\n");
84471f95118Swdenk 		return -1;
84571f95118Swdenk 	}
8467385c28eSWolfgang Denk 
84740e21916SSergei Shtylyov 	if (mydata->fatsize == 32) {
8482aa98c66SWolfgang Denk 		root_cluster = bs.root_cluster;
84971f95118Swdenk 		mydata->fatlength = bs.fat32_length;
85040e21916SSergei Shtylyov 	} else {
85171f95118Swdenk 		mydata->fatlength = bs.fat_length;
85240e21916SSergei Shtylyov 	}
8537385c28eSWolfgang Denk 
85471f95118Swdenk 	mydata->fat_sect = bs.reserved;
8557385c28eSWolfgang Denk 
85671f95118Swdenk 	cursect = mydata->rootdir_sect
85771f95118Swdenk 		= mydata->fat_sect + mydata->fatlength * bs.fats;
8587385c28eSWolfgang Denk 
859ac497771SSergei Shtylyov 	mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0];
86071f95118Swdenk 	mydata->clust_size = bs.cluster_size;
86146236b14SKyle Moffett 	if (mydata->sect_size != cur_part_info.blksz) {
86246236b14SKyle Moffett 		printf("Error: FAT sector size mismatch (fs=%hu, dev=%lu)\n",
86346236b14SKyle Moffett 				mydata->sect_size, cur_part_info.blksz);
86446236b14SKyle Moffett 		return -1;
86546236b14SKyle Moffett 	}
8667385c28eSWolfgang Denk 
86771f95118Swdenk 	if (mydata->fatsize == 32) {
8687385c28eSWolfgang Denk 		mydata->data_begin = mydata->rootdir_sect -
8697385c28eSWolfgang Denk 					(mydata->clust_size * 2);
87071f95118Swdenk 	} else {
8717385c28eSWolfgang Denk 		rootdir_size = ((bs.dir_entries[1]  * (int)256 +
8727385c28eSWolfgang Denk 				 bs.dir_entries[0]) *
8737385c28eSWolfgang Denk 				 sizeof(dir_entry)) /
874ac497771SSergei Shtylyov 				 mydata->sect_size;
8757385c28eSWolfgang Denk 		mydata->data_begin = mydata->rootdir_sect +
8767385c28eSWolfgang Denk 					rootdir_size -
8777385c28eSWolfgang Denk 					(mydata->clust_size * 2);
87871f95118Swdenk 	}
8797385c28eSWolfgang Denk 
88071f95118Swdenk 	mydata->fatbufnum = -1;
8819a800ac7SEric Nelson 	mydata->fatbuf = memalign(ARCH_DMA_MINALIGN, FATBUFSIZE);
882ac497771SSergei Shtylyov 	if (mydata->fatbuf == NULL) {
883ac497771SSergei Shtylyov 		debug("Error: allocating memory\n");
884ac497771SSergei Shtylyov 		return -1;
885ac497771SSergei Shtylyov 	}
88671f95118Swdenk 
8872aa98c66SWolfgang Denk #ifdef CONFIG_SUPPORT_VFAT
8887385c28eSWolfgang Denk 	debug("VFAT Support enabled\n");
8892aa98c66SWolfgang Denk #endif
8907385c28eSWolfgang Denk 	debug("FAT%d, fat_sect: %d, fatlength: %d\n",
8917385c28eSWolfgang Denk 	       mydata->fatsize, mydata->fat_sect, mydata->fatlength);
8927385c28eSWolfgang Denk 	debug("Rootdir begins at cluster: %d, sector: %d, offset: %x\n"
89371f95118Swdenk 	       "Data begins at: %d\n",
8942aa98c66SWolfgang Denk 	       root_cluster,
8952aa98c66SWolfgang Denk 	       mydata->rootdir_sect,
896ac497771SSergei Shtylyov 	       mydata->rootdir_sect * mydata->sect_size, mydata->data_begin);
897ac497771SSergei Shtylyov 	debug("Sector size: %d, cluster size: %d\n", mydata->sect_size,
898ac497771SSergei Shtylyov 	      mydata->clust_size);
89971f95118Swdenk 
90071f95118Swdenk 	/* "cwd" is always the root... */
90171f95118Swdenk 	while (ISDIRDELIM(*filename))
90271f95118Swdenk 		filename++;
9037385c28eSWolfgang Denk 
90471f95118Swdenk 	/* Make a copy of the filename and convert it to lowercase */
90571f95118Swdenk 	strcpy(fnamecopy, filename);
90671f95118Swdenk 	downcase(fnamecopy);
9077385c28eSWolfgang Denk 
90871f95118Swdenk 	if (*fnamecopy == '\0') {
90971f95118Swdenk 		if (!dols)
910ac497771SSergei Shtylyov 			goto exit;
9117385c28eSWolfgang Denk 
91271f95118Swdenk 		dols = LS_ROOT;
91371f95118Swdenk 	} else if ((idx = dirdelim(fnamecopy)) >= 0) {
91471f95118Swdenk 		isdir = 1;
91571f95118Swdenk 		fnamecopy[idx] = '\0';
91671f95118Swdenk 		subname = fnamecopy + idx + 1;
9177385c28eSWolfgang Denk 
91871f95118Swdenk 		/* Handle multiple delimiters */
91971f95118Swdenk 		while (ISDIRDELIM(*subname))
92071f95118Swdenk 			subname++;
92171f95118Swdenk 	} else if (dols) {
92271f95118Swdenk 		isdir = 1;
92371f95118Swdenk 	}
92471f95118Swdenk 
9252aa98c66SWolfgang Denk 	j = 0;
92671f95118Swdenk 	while (1) {
92771f95118Swdenk 		int i;
92871f95118Swdenk 
929cd1b042cSBenoît Thébaudeau 		if (j == 0) {
9302d1b83b3SAndreas Bießmann 			debug("FAT read sect=%d, clust_size=%d, DIRENTSPERBLOCK=%zd\n",
9312aa98c66SWolfgang Denk 				cursect, mydata->clust_size, DIRENTSPERBLOCK);
9327385c28eSWolfgang Denk 
9333831530dSMikhail Zolotaryov 			if (disk_read(cursect,
9343831530dSMikhail Zolotaryov 					(mydata->fatsize == 32) ?
9353831530dSMikhail Zolotaryov 					(mydata->clust_size) :
936025421eaSSergei Shtylyov 					PREFETCH_BLOCKS,
9371170e634SBenoît Thébaudeau 					do_fat_read_at_block) < 0) {
9387385c28eSWolfgang Denk 				debug("Error: reading rootdir block\n");
939ac497771SSergei Shtylyov 				goto exit;
94071f95118Swdenk 			}
9417385c28eSWolfgang Denk 
9421170e634SBenoît Thébaudeau 			dentptr = (dir_entry *) do_fat_read_at_block;
943cd1b042cSBenoît Thébaudeau 		}
9447385c28eSWolfgang Denk 
94571f95118Swdenk 		for (i = 0; i < DIRENTSPERBLOCK; i++) {
9463831530dSMikhail Zolotaryov 			char s_name[14], l_name[VFAT_MAXLEN_BYTES];
947ff04f6d1SMarek Vasut 			__u8 csum;
94871f95118Swdenk 
94971f95118Swdenk 			l_name[0] = '\0';
9503831530dSMikhail Zolotaryov 			if (dentptr->name[0] == DELETED_FLAG) {
9513831530dSMikhail Zolotaryov 				dentptr++;
9523831530dSMikhail Zolotaryov 				continue;
9533831530dSMikhail Zolotaryov 			}
954ff04f6d1SMarek Vasut 
955ff04f6d1SMarek Vasut 			csum = mkcksum(dentptr->name, dentptr->ext);
956ff04f6d1SMarek Vasut 			if (dentptr->attr & ATTR_VOLUME) {
95771f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT
958206d68fdSJ. Vijayanand 				if ((dentptr->attr & ATTR_VFAT) == ATTR_VFAT &&
9592d1a537dSwdenk 				    (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
9607385c28eSWolfgang Denk 					prevcksum =
9617385c28eSWolfgang Denk 						((dir_slot *)dentptr)->alias_checksum;
9627385c28eSWolfgang Denk 
9633831530dSMikhail Zolotaryov 					get_vfatname(mydata,
96440e21916SSergei Shtylyov 						     root_cluster,
9651170e634SBenoît Thébaudeau 						     do_fat_read_at_block,
9667385c28eSWolfgang Denk 						     dentptr, l_name);
9677385c28eSWolfgang Denk 
96871f95118Swdenk 					if (dols == LS_ROOT) {
96971f95118Swdenk 						char dirc;
97071f95118Swdenk 						int doit = 0;
9717385c28eSWolfgang Denk 						int isdir =
9727385c28eSWolfgang Denk 							(dentptr->attr & ATTR_DIR);
97371f95118Swdenk 
97471f95118Swdenk 						if (isdir) {
97571f95118Swdenk 							dirs++;
97671f95118Swdenk 							dirc = '/';
97771f95118Swdenk 							doit = 1;
97871f95118Swdenk 						} else {
97971f95118Swdenk 							dirc = ' ';
98071f95118Swdenk 							if (l_name[0] != 0) {
98171f95118Swdenk 								files++;
98271f95118Swdenk 								doit = 1;
98371f95118Swdenk 							}
98471f95118Swdenk 						}
98571f95118Swdenk 						if (doit) {
98671f95118Swdenk 							if (dirc == ' ') {
98771f95118Swdenk 								printf(" %8ld   %s%c\n",
98871f95118Swdenk 									(long)FAT2CPU32(dentptr->size),
9897385c28eSWolfgang Denk 									l_name,
9907385c28eSWolfgang Denk 									dirc);
99171f95118Swdenk 							} else {
9927385c28eSWolfgang Denk 								printf("            %s%c\n",
9937385c28eSWolfgang Denk 									l_name,
9947385c28eSWolfgang Denk 									dirc);
99571f95118Swdenk 							}
99671f95118Swdenk 						}
99771f95118Swdenk 						dentptr++;
99871f95118Swdenk 						continue;
99971f95118Swdenk 					}
10007385c28eSWolfgang Denk 					debug("Rootvfatname: |%s|\n",
10017385c28eSWolfgang Denk 					       l_name);
100271f95118Swdenk 				} else
100371f95118Swdenk #endif
100471f95118Swdenk 				{
100571f95118Swdenk 					/* Volume label or VFAT entry */
100671f95118Swdenk 					dentptr++;
100771f95118Swdenk 					continue;
100871f95118Swdenk 				}
100971f95118Swdenk 			} else if (dentptr->name[0] == 0) {
10107385c28eSWolfgang Denk 				debug("RootDentname == NULL - %d\n", i);
101171f95118Swdenk 				if (dols == LS_ROOT) {
10127385c28eSWolfgang Denk 					printf("\n%d file(s), %d dir(s)\n\n",
10137385c28eSWolfgang Denk 						files, dirs);
1014ac497771SSergei Shtylyov 					ret = 0;
101571f95118Swdenk 				}
1016ac497771SSergei Shtylyov 				goto exit;
101771f95118Swdenk 			}
101871f95118Swdenk #ifdef CONFIG_SUPPORT_VFAT
1019ff04f6d1SMarek Vasut 			else if (dols == LS_ROOT && csum == prevcksum) {
1020bf34e7d9SSergei Shtylyov 				prevcksum = 0xffff;
102171f95118Swdenk 				dentptr++;
102271f95118Swdenk 				continue;
102371f95118Swdenk 			}
102471f95118Swdenk #endif
102571f95118Swdenk 			get_name(dentptr, s_name);
10267385c28eSWolfgang Denk 
102771f95118Swdenk 			if (dols == LS_ROOT) {
102871f95118Swdenk 				int isdir = (dentptr->attr & ATTR_DIR);
102971f95118Swdenk 				char dirc;
103071f95118Swdenk 				int doit = 0;
103171f95118Swdenk 
103271f95118Swdenk 				if (isdir) {
103371f95118Swdenk 					dirc = '/';
1034a43278a4Swdenk 					if (s_name[0] != 0) {
1035a43278a4Swdenk 						dirs++;
103671f95118Swdenk 						doit = 1;
1037a43278a4Swdenk 					}
103871f95118Swdenk 				} else {
103971f95118Swdenk 					dirc = ' ';
104071f95118Swdenk 					if (s_name[0] != 0) {
104171f95118Swdenk 						files++;
104271f95118Swdenk 						doit = 1;
104371f95118Swdenk 					}
104471f95118Swdenk 				}
104571f95118Swdenk 				if (doit) {
104671f95118Swdenk 					if (dirc == ' ') {
104771f95118Swdenk 						printf(" %8ld   %s%c\n",
10487385c28eSWolfgang Denk 							(long)FAT2CPU32(dentptr->size),
10497385c28eSWolfgang Denk 							s_name, dirc);
105071f95118Swdenk 					} else {
10517385c28eSWolfgang Denk 						printf("            %s%c\n",
10527385c28eSWolfgang Denk 							s_name, dirc);
105371f95118Swdenk 					}
105471f95118Swdenk 				}
105571f95118Swdenk 				dentptr++;
105671f95118Swdenk 				continue;
105771f95118Swdenk 			}
10587385c28eSWolfgang Denk 
10597385c28eSWolfgang Denk 			if (strcmp(fnamecopy, s_name)
10607385c28eSWolfgang Denk 			    && strcmp(fnamecopy, l_name)) {
10617385c28eSWolfgang Denk 				debug("RootMismatch: |%s|%s|\n", s_name,
10627385c28eSWolfgang Denk 				       l_name);
106371f95118Swdenk 				dentptr++;
106471f95118Swdenk 				continue;
106571f95118Swdenk 			}
10667385c28eSWolfgang Denk 
106771f95118Swdenk 			if (isdir && !(dentptr->attr & ATTR_DIR))
1068ac497771SSergei Shtylyov 				goto exit;
106971f95118Swdenk 
10707385c28eSWolfgang Denk 			debug("RootName: %s", s_name);
10717385c28eSWolfgang Denk 			debug(", start: 0x%x", START(dentptr));
10727385c28eSWolfgang Denk 			debug(", size:  0x%x %s\n",
10737385c28eSWolfgang Denk 			       FAT2CPU32(dentptr->size),
10747385c28eSWolfgang Denk 			       isdir ? "(DIR)" : "");
107571f95118Swdenk 
107671f95118Swdenk 			goto rootdir_done;	/* We got a match */
107771f95118Swdenk 		}
10787385c28eSWolfgang Denk 		debug("END LOOP: j=%d   clust_size=%d\n", j,
10797385c28eSWolfgang Denk 		       mydata->clust_size);
10802aa98c66SWolfgang Denk 
10812aa98c66SWolfgang Denk 		/*
10822aa98c66SWolfgang Denk 		 * On FAT32 we must fetch the FAT entries for the next
10832aa98c66SWolfgang Denk 		 * root directory clusters when a cluster has been
10842aa98c66SWolfgang Denk 		 * completely processed.
10852aa98c66SWolfgang Denk 		 */
10863f270f42SErik Hansen 		++j;
1087cd1b042cSBenoît Thébaudeau 		int rootdir_end = 0;
1088cd1b042cSBenoît Thébaudeau 		if (mydata->fatsize == 32) {
1089cd1b042cSBenoît Thébaudeau 			if (j == mydata->clust_size) {
10903f270f42SErik Hansen 				int nxtsect = 0;
10913f270f42SErik Hansen 				int nxt_clust = 0;
10922aa98c66SWolfgang Denk 
10932aa98c66SWolfgang Denk 				nxt_clust = get_fatent(mydata, root_cluster);
1094cd1b042cSBenoît Thébaudeau 				rootdir_end = CHECK_CLUST(nxt_clust, 32);
10957385c28eSWolfgang Denk 
10967385c28eSWolfgang Denk 				nxtsect = mydata->data_begin +
10977385c28eSWolfgang Denk 					(nxt_clust * mydata->clust_size);
10987385c28eSWolfgang Denk 
10992aa98c66SWolfgang Denk 				root_cluster = nxt_clust;
11002aa98c66SWolfgang Denk 
11012aa98c66SWolfgang Denk 				cursect = nxtsect;
11022aa98c66SWolfgang Denk 				j = 0;
1103cd1b042cSBenoît Thébaudeau 			}
11042aa98c66SWolfgang Denk 		} else {
1105cd1b042cSBenoît Thébaudeau 			if (j == PREFETCH_BLOCKS)
1106cd1b042cSBenoît Thébaudeau 				j = 0;
1107cd1b042cSBenoît Thébaudeau 
1108cd1b042cSBenoît Thébaudeau 			rootdir_end = (++cursect - mydata->rootdir_sect >=
1109cd1b042cSBenoît Thébaudeau 				       rootdir_size);
111071f95118Swdenk 		}
11113f270f42SErik Hansen 
11123f270f42SErik Hansen 		/* If end of rootdir reached */
1113cd1b042cSBenoît Thébaudeau 		if (rootdir_end) {
11143f270f42SErik Hansen 			if (dols == LS_ROOT) {
11153f270f42SErik Hansen 				printf("\n%d file(s), %d dir(s)\n\n",
11163f270f42SErik Hansen 				       files, dirs);
1117ac497771SSergei Shtylyov 				ret = 0;
11183f270f42SErik Hansen 			}
1119ac497771SSergei Shtylyov 			goto exit;
11203f270f42SErik Hansen 		}
11212aa98c66SWolfgang Denk 	}
112271f95118Swdenk rootdir_done:
112371f95118Swdenk 
112471f95118Swdenk 	firsttime = 1;
11257385c28eSWolfgang Denk 
112671f95118Swdenk 	while (isdir) {
112771f95118Swdenk 		int startsect = mydata->data_begin
112871f95118Swdenk 			+ START(dentptr) * mydata->clust_size;
112971f95118Swdenk 		dir_entry dent;
113071f95118Swdenk 		char *nextname = NULL;
113171f95118Swdenk 
113271f95118Swdenk 		dent = *dentptr;
113371f95118Swdenk 		dentptr = &dent;
113471f95118Swdenk 
113571f95118Swdenk 		idx = dirdelim(subname);
11367385c28eSWolfgang Denk 
113771f95118Swdenk 		if (idx >= 0) {
113871f95118Swdenk 			subname[idx] = '\0';
113971f95118Swdenk 			nextname = subname + idx + 1;
114071f95118Swdenk 			/* Handle multiple delimiters */
114171f95118Swdenk 			while (ISDIRDELIM(*nextname))
114271f95118Swdenk 				nextname++;
114371f95118Swdenk 			if (dols && *nextname == '\0')
114471f95118Swdenk 				firsttime = 0;
114571f95118Swdenk 		} else {
114671f95118Swdenk 			if (dols && firsttime) {
114771f95118Swdenk 				firsttime = 0;
114871f95118Swdenk 			} else {
114971f95118Swdenk 				isdir = 0;
115071f95118Swdenk 			}
115171f95118Swdenk 		}
115271f95118Swdenk 
115371f95118Swdenk 		if (get_dentfromdir(mydata, startsect, subname, dentptr,
115471f95118Swdenk 				     isdir ? 0 : dols) == NULL) {
115571f95118Swdenk 			if (dols && !isdir)
1156ac497771SSergei Shtylyov 				ret = 0;
1157ac497771SSergei Shtylyov 			goto exit;
115871f95118Swdenk 		}
115971f95118Swdenk 
11607ee46cebSBenoît Thébaudeau 		if (isdir && !(dentptr->attr & ATTR_DIR))
1161ac497771SSergei Shtylyov 			goto exit;
11627ee46cebSBenoît Thébaudeau 
11637ee46cebSBenoît Thébaudeau 		if (idx >= 0)
116471f95118Swdenk 			subname = nextname;
116571f95118Swdenk 	}
11667385c28eSWolfgang Denk 
11671170e634SBenoît Thébaudeau 	ret = get_contents(mydata, dentptr, pos, buffer, maxsize);
11687385c28eSWolfgang Denk 	debug("Size: %d, got: %ld\n", FAT2CPU32(dentptr->size), ret);
116971f95118Swdenk 
1170ac497771SSergei Shtylyov exit:
1171ac497771SSergei Shtylyov 	free(mydata->fatbuf);
117271f95118Swdenk 	return ret;
117371f95118Swdenk }
117471f95118Swdenk 
11751170e634SBenoît Thébaudeau long
11761170e634SBenoît Thébaudeau do_fat_read(const char *filename, void *buffer, unsigned long maxsize, int dols)
11771170e634SBenoît Thébaudeau {
11781170e634SBenoît Thébaudeau 	return do_fat_read_at(filename, 0, buffer, maxsize, dols);
11791170e634SBenoît Thébaudeau }
11801170e634SBenoît Thébaudeau 
11817385c28eSWolfgang Denk int file_fat_detectfs(void)
118271f95118Swdenk {
118371f95118Swdenk 	boot_sector bs;
118471f95118Swdenk 	volume_info volinfo;
118571f95118Swdenk 	int fatsize;
11867205e407Swdenk 	char vol_label[12];
118771f95118Swdenk 
11887205e407Swdenk 	if (cur_dev == NULL) {
11897205e407Swdenk 		printf("No current device\n");
11907205e407Swdenk 		return 1;
11917205e407Swdenk 	}
11927385c28eSWolfgang Denk 
1193dd60d122SJon Loeliger #if defined(CONFIG_CMD_IDE) || \
11948c5170a7SSonic Zhang     defined(CONFIG_CMD_SATA) || \
1195dd60d122SJon Loeliger     defined(CONFIG_CMD_SCSI) || \
1196dd60d122SJon Loeliger     defined(CONFIG_CMD_USB) || \
119721f6f963SAndy Fleming     defined(CONFIG_MMC)
11987205e407Swdenk 	printf("Interface:  ");
11997205e407Swdenk 	switch (cur_dev->if_type) {
12007385c28eSWolfgang Denk 	case IF_TYPE_IDE:
12017385c28eSWolfgang Denk 		printf("IDE");
12027385c28eSWolfgang Denk 		break;
12037385c28eSWolfgang Denk 	case IF_TYPE_SATA:
12047385c28eSWolfgang Denk 		printf("SATA");
12057385c28eSWolfgang Denk 		break;
12067385c28eSWolfgang Denk 	case IF_TYPE_SCSI:
12077385c28eSWolfgang Denk 		printf("SCSI");
12087385c28eSWolfgang Denk 		break;
12097385c28eSWolfgang Denk 	case IF_TYPE_ATAPI:
12107385c28eSWolfgang Denk 		printf("ATAPI");
12117385c28eSWolfgang Denk 		break;
12127385c28eSWolfgang Denk 	case IF_TYPE_USB:
12137385c28eSWolfgang Denk 		printf("USB");
12147385c28eSWolfgang Denk 		break;
12157385c28eSWolfgang Denk 	case IF_TYPE_DOC:
12167385c28eSWolfgang Denk 		printf("DOC");
12177385c28eSWolfgang Denk 		break;
12187385c28eSWolfgang Denk 	case IF_TYPE_MMC:
12197385c28eSWolfgang Denk 		printf("MMC");
12207385c28eSWolfgang Denk 		break;
12217385c28eSWolfgang Denk 	default:
12227385c28eSWolfgang Denk 		printf("Unknown");
12237205e407Swdenk 	}
12247385c28eSWolfgang Denk 
12257205e407Swdenk 	printf("\n  Device %d: ", cur_dev->dev);
12267205e407Swdenk 	dev_print(cur_dev);
12277205e407Swdenk #endif
12287385c28eSWolfgang Denk 
12297205e407Swdenk 	if (read_bootsectandvi(&bs, &volinfo, &fatsize)) {
12307205e407Swdenk 		printf("\nNo valid FAT fs found\n");
12317205e407Swdenk 		return 1;
12327205e407Swdenk 	}
12337385c28eSWolfgang Denk 
12347205e407Swdenk 	memcpy(vol_label, volinfo.volume_label, 11);
12357205e407Swdenk 	vol_label[11] = '\0';
12367205e407Swdenk 	volinfo.fs_type[5] = '\0';
12377385c28eSWolfgang Denk 
1238461f86e6SStephen Warren 	printf("Filesystem: %s \"%s\"\n", volinfo.fs_type, vol_label);
12397385c28eSWolfgang Denk 
12407205e407Swdenk 	return 0;
124171f95118Swdenk }
124271f95118Swdenk 
12437385c28eSWolfgang Denk int file_fat_ls(const char *dir)
124471f95118Swdenk {
124571f95118Swdenk 	return do_fat_read(dir, NULL, 0, LS_YES);
124671f95118Swdenk }
124771f95118Swdenk 
12481170e634SBenoît Thébaudeau long file_fat_read_at(const char *filename, unsigned long pos, void *buffer,
12491170e634SBenoît Thébaudeau 		      unsigned long maxsize)
125071f95118Swdenk {
12517205e407Swdenk 	printf("reading %s\n", filename);
12521170e634SBenoît Thébaudeau 	return do_fat_read_at(filename, pos, buffer, maxsize, LS_NO);
12531170e634SBenoît Thébaudeau }
12541170e634SBenoît Thébaudeau 
12551170e634SBenoît Thébaudeau long file_fat_read(const char *filename, void *buffer, unsigned long maxsize)
12561170e634SBenoît Thébaudeau {
12571170e634SBenoît Thébaudeau 	return file_fat_read_at(filename, 0, buffer, maxsize);
125871f95118Swdenk }
1259