xref: /rk3399_rockchip-uboot/fs/fat/fat.c (revision b8948d2aef80717d3d2c4f37ec086ce3ea5ad24f)
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  *
91a459660SWolfgang Denk  * SPDX-License-Identifier:	GPL-2.0+
1071f95118Swdenk  */
1171f95118Swdenk 
1271f95118Swdenk #include <common.h>
132a981dc2SSimon Glass #include <blk.h>
1471f95118Swdenk #include <config.h>
15ac497771SSergei Shtylyov #include <exports.h>
1671f95118Swdenk #include <fat.h>
1771f95118Swdenk #include <asm/byteorder.h>
187205e407Swdenk #include <part.h>
199a800ac7SEric Nelson #include <malloc.h>
20cf92e05cSSimon Glass #include <memalign.h>
219a800ac7SEric Nelson #include <linux/compiler.h>
22fb7e16ccSRichard Genoud #include <linux/ctype.h>
2371f95118Swdenk 
24cb940c7eSRichard Genoud #ifdef CONFIG_SUPPORT_VFAT
25cb940c7eSRichard Genoud static const int vfat_enabled = 1;
26cb940c7eSRichard Genoud #else
27cb940c7eSRichard Genoud static const int vfat_enabled = 0;
28cb940c7eSRichard Genoud #endif
29cb940c7eSRichard Genoud 
3071f95118Swdenk /*
3171f95118Swdenk  * Convert a string to lowercase.
3271f95118Swdenk  */
337385c28eSWolfgang Denk static void downcase(char *str)
3471f95118Swdenk {
3571f95118Swdenk 	while (*str != '\0') {
36fb7e16ccSRichard Genoud 		*str = tolower(*str);
3771f95118Swdenk 		str++;
3871f95118Swdenk 	}
3971f95118Swdenk }
4071f95118Swdenk 
414101f687SSimon Glass static struct blk_desc *cur_dev;
429813b750SKyle Moffett static disk_partition_t cur_part_info;
437385c28eSWolfgang Denk 
449813b750SKyle Moffett #define DOS_BOOT_MAGIC_OFFSET	0x1fe
457205e407Swdenk #define DOS_FS_TYPE_OFFSET	0x36
4666c2d73cSWolfgang Denk #define DOS_FS32_TYPE_OFFSET	0x52
4771f95118Swdenk 
489813b750SKyle Moffett static int disk_read(__u32 block, __u32 nr_blocks, void *buf)
4971f95118Swdenk {
500a04ed86SŁukasz Majewski 	ulong ret;
510a04ed86SŁukasz Majewski 
522a981dc2SSimon Glass 	if (!cur_dev)
537205e407Swdenk 		return -1;
547385c28eSWolfgang Denk 
552a981dc2SSimon Glass 	ret = blk_dread(cur_dev, cur_part_info.start + block, nr_blocks, buf);
560a04ed86SŁukasz Majewski 
570a04ed86SŁukasz Majewski 	if (nr_blocks && ret == 0)
580a04ed86SŁukasz Majewski 		return -1;
590a04ed86SŁukasz Majewski 
600a04ed86SŁukasz Majewski 	return ret;
6171f95118Swdenk }
6271f95118Swdenk 
634101f687SSimon Glass int fat_set_blk_dev(struct blk_desc *dev_desc, disk_partition_t *info)
6471f95118Swdenk {
659a800ac7SEric Nelson 	ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz);
667205e407Swdenk 
679813b750SKyle Moffett 	cur_dev = dev_desc;
685e8f9831SStephen Warren 	cur_part_info = *info;
699813b750SKyle Moffett 
709813b750SKyle Moffett 	/* Make sure it has a valid FAT header */
719813b750SKyle Moffett 	if (disk_read(0, 1, buffer) != 1) {
729813b750SKyle Moffett 		cur_dev = NULL;
739813b750SKyle Moffett 		return -1;
747205e407Swdenk 	}
759813b750SKyle Moffett 
769813b750SKyle Moffett 	/* Check if it's actually a DOS volume */
779813b750SKyle Moffett 	if (memcmp(buffer + DOS_BOOT_MAGIC_OFFSET, "\x55\xAA", 2)) {
789813b750SKyle Moffett 		cur_dev = NULL;
799813b750SKyle Moffett 		return -1;
809813b750SKyle Moffett 	}
819813b750SKyle Moffett 
829813b750SKyle Moffett 	/* Check for FAT12/FAT16/FAT32 filesystem */
839813b750SKyle Moffett 	if (!memcmp(buffer + DOS_FS_TYPE_OFFSET, "FAT", 3))
8471f95118Swdenk 		return 0;
859813b750SKyle Moffett 	if (!memcmp(buffer + DOS_FS32_TYPE_OFFSET, "FAT32", 5))
869813b750SKyle Moffett 		return 0;
879813b750SKyle Moffett 
889813b750SKyle Moffett 	cur_dev = NULL;
899813b750SKyle Moffett 	return -1;
9071f95118Swdenk }
9171f95118Swdenk 
924101f687SSimon Glass int fat_register_device(struct blk_desc *dev_desc, int part_no)
935e8f9831SStephen Warren {
945e8f9831SStephen Warren 	disk_partition_t info;
955e8f9831SStephen Warren 
965e8f9831SStephen Warren 	/* First close any currently found FAT filesystem */
975e8f9831SStephen Warren 	cur_dev = NULL;
985e8f9831SStephen Warren 
995e8f9831SStephen Warren 	/* Read the partition table, if present */
1003e8bd469SSimon Glass 	if (part_get_info(dev_desc, part_no, &info)) {
1015e8f9831SStephen Warren 		if (part_no != 0) {
1025e8f9831SStephen Warren 			printf("** Partition %d not valid on device %d **\n",
103bcce53d0SSimon Glass 					part_no, dev_desc->devnum);
1045e8f9831SStephen Warren 			return -1;
1055e8f9831SStephen Warren 		}
1065e8f9831SStephen Warren 
1075e8f9831SStephen Warren 		info.start = 0;
1085e8f9831SStephen Warren 		info.size = dev_desc->lba;
1095e8f9831SStephen Warren 		info.blksz = dev_desc->blksz;
1105e8f9831SStephen Warren 		info.name[0] = 0;
1115e8f9831SStephen Warren 		info.type[0] = 0;
1125e8f9831SStephen Warren 		info.bootable = 0;
1135e8f9831SStephen Warren #ifdef CONFIG_PARTITION_UUIDS
1145e8f9831SStephen Warren 		info.uuid[0] = 0;
1155e8f9831SStephen Warren #endif
1165e8f9831SStephen Warren 	}
1175e8f9831SStephen Warren 
1185e8f9831SStephen Warren 	return fat_set_blk_dev(dev_desc, &info);
1195e8f9831SStephen Warren }
1209813b750SKyle Moffett 
12171f95118Swdenk /*
12271f95118Swdenk  * Get the first occurence of a directory delimiter ('/' or '\') in a string.
12371f95118Swdenk  * Return index into string if found, -1 otherwise.
12471f95118Swdenk  */
1257385c28eSWolfgang Denk static int dirdelim(char *str)
12671f95118Swdenk {
12771f95118Swdenk 	char *start = str;
12871f95118Swdenk 
12971f95118Swdenk 	while (*str != '\0') {
1307385c28eSWolfgang Denk 		if (ISDIRDELIM(*str))
1317385c28eSWolfgang Denk 			return str - start;
13271f95118Swdenk 		str++;
13371f95118Swdenk 	}
13471f95118Swdenk 	return -1;
13571f95118Swdenk }
13671f95118Swdenk 
13771f95118Swdenk /*
13871f95118Swdenk  * Extract zero terminated short name from a directory entry.
13971f95118Swdenk  */
14071f95118Swdenk static void get_name(dir_entry *dirent, char *s_name)
14171f95118Swdenk {
14271f95118Swdenk 	char *ptr;
14371f95118Swdenk 
14471f95118Swdenk 	memcpy(s_name, dirent->name, 8);
14571f95118Swdenk 	s_name[8] = '\0';
14671f95118Swdenk 	ptr = s_name;
14771f95118Swdenk 	while (*ptr && *ptr != ' ')
14871f95118Swdenk 		ptr++;
14971f95118Swdenk 	if (dirent->ext[0] && dirent->ext[0] != ' ') {
15071f95118Swdenk 		*ptr = '.';
15171f95118Swdenk 		ptr++;
15271f95118Swdenk 		memcpy(ptr, dirent->ext, 3);
15371f95118Swdenk 		ptr[3] = '\0';
15471f95118Swdenk 		while (*ptr && *ptr != ' ')
15571f95118Swdenk 			ptr++;
15671f95118Swdenk 	}
15771f95118Swdenk 	*ptr = '\0';
15871f95118Swdenk 	if (*s_name == DELETED_FLAG)
15971f95118Swdenk 		*s_name = '\0';
16071f95118Swdenk 	else if (*s_name == aRING)
1613c2c2f42SRemy Bohmer 		*s_name = DELETED_FLAG;
16271f95118Swdenk 	downcase(s_name);
16371f95118Swdenk }
16471f95118Swdenk 
165*b8948d2aSStefan Brüns static int flush_dirty_fat_buffer(fsdata *mydata);
166*b8948d2aSStefan Brüns #if !defined(CONFIG_FAT_WRITE)
167*b8948d2aSStefan Brüns /* Stub for read only operation */
168*b8948d2aSStefan Brüns int flush_dirty_fat_buffer(fsdata *mydata)
169*b8948d2aSStefan Brüns {
170*b8948d2aSStefan Brüns 	(void)(mydata);
171*b8948d2aSStefan Brüns 	return 0;
172*b8948d2aSStefan Brüns }
173*b8948d2aSStefan Brüns #endif
174*b8948d2aSStefan Brüns 
17571f95118Swdenk /*
17671f95118Swdenk  * Get the entry at index 'entry' in a FAT (12/16/32) table.
17771f95118Swdenk  * On failure 0x00 is returned.
17871f95118Swdenk  */
1797385c28eSWolfgang Denk static __u32 get_fatent(fsdata *mydata, __u32 entry)
18071f95118Swdenk {
18171f95118Swdenk 	__u32 bufnum;
1827385c28eSWolfgang Denk 	__u32 off16, offset;
18371f95118Swdenk 	__u32 ret = 0x00;
1847385c28eSWolfgang Denk 	__u16 val1, val2;
18571f95118Swdenk 
186*b8948d2aSStefan Brüns 	if (CHECK_CLUST(entry, mydata->fatsize)) {
187*b8948d2aSStefan Brüns 		printf("Error: Invalid FAT entry: 0x%08x\n", entry);
188*b8948d2aSStefan Brüns 		return ret;
189*b8948d2aSStefan Brüns 	}
190*b8948d2aSStefan Brüns 
19171f95118Swdenk 	switch (mydata->fatsize) {
19271f95118Swdenk 	case 32:
19371f95118Swdenk 		bufnum = entry / FAT32BUFSIZE;
19471f95118Swdenk 		offset = entry - bufnum * FAT32BUFSIZE;
19571f95118Swdenk 		break;
19671f95118Swdenk 	case 16:
19771f95118Swdenk 		bufnum = entry / FAT16BUFSIZE;
19871f95118Swdenk 		offset = entry - bufnum * FAT16BUFSIZE;
19971f95118Swdenk 		break;
20071f95118Swdenk 	case 12:
20171f95118Swdenk 		bufnum = entry / FAT12BUFSIZE;
20271f95118Swdenk 		offset = entry - bufnum * FAT12BUFSIZE;
20371f95118Swdenk 		break;
20471f95118Swdenk 
20571f95118Swdenk 	default:
20671f95118Swdenk 		/* Unsupported FAT size */
20771f95118Swdenk 		return ret;
20871f95118Swdenk 	}
20971f95118Swdenk 
210*b8948d2aSStefan Brüns 	debug("FAT%d: entry: 0x%08x = %d, offset: 0x%04x = %d\n",
2112aa98c66SWolfgang Denk 	       mydata->fatsize, entry, entry, offset, offset);
2122aa98c66SWolfgang Denk 
21371f95118Swdenk 	/* Read a new block of FAT entries into the cache. */
21471f95118Swdenk 	if (bufnum != mydata->fatbufnum) {
21560b36f0fSSergei Shtylyov 		__u32 getsize = FATBUFBLOCKS;
21671f95118Swdenk 		__u8 *bufptr = mydata->fatbuf;
21771f95118Swdenk 		__u32 fatlength = mydata->fatlength;
21871f95118Swdenk 		__u32 startblock = bufnum * FATBUFBLOCKS;
21971f95118Swdenk 
2206c1a8080SStefan Brüns 		/* Cap length if fatlength is not a multiple of FATBUFBLOCKS */
2218006dd2eSBenoît Thébaudeau 		if (startblock + getsize > fatlength)
2228006dd2eSBenoît Thébaudeau 			getsize = fatlength - startblock;
22360b36f0fSSergei Shtylyov 
22471f95118Swdenk 		startblock += mydata->fat_sect;	/* Offset from start of disk */
22571f95118Swdenk 
226*b8948d2aSStefan Brüns 		/* Write back the fatbuf to the disk */
227*b8948d2aSStefan Brüns 		if (flush_dirty_fat_buffer(mydata) < 0)
228*b8948d2aSStefan Brüns 			return -1;
229*b8948d2aSStefan Brüns 
23071f95118Swdenk 		if (disk_read(startblock, getsize, bufptr) < 0) {
2317385c28eSWolfgang Denk 			debug("Error reading FAT blocks\n");
23271f95118Swdenk 			return ret;
23371f95118Swdenk 		}
23471f95118Swdenk 		mydata->fatbufnum = bufnum;
23571f95118Swdenk 	}
23671f95118Swdenk 
23771f95118Swdenk 	/* Get the actual entry from the table */
23871f95118Swdenk 	switch (mydata->fatsize) {
23971f95118Swdenk 	case 32:
24071f95118Swdenk 		ret = FAT2CPU32(((__u32 *) mydata->fatbuf)[offset]);
24171f95118Swdenk 		break;
24271f95118Swdenk 	case 16:
24371f95118Swdenk 		ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[offset]);
24471f95118Swdenk 		break;
2457385c28eSWolfgang Denk 	case 12:
2467385c28eSWolfgang Denk 		off16 = (offset * 3) / 4;
24771f95118Swdenk 
24871f95118Swdenk 		switch (offset & 0x3) {
24971f95118Swdenk 		case 0:
25071f95118Swdenk 			ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[off16]);
25171f95118Swdenk 			ret &= 0xfff;
25271f95118Swdenk 			break;
25371f95118Swdenk 		case 1:
25471f95118Swdenk 			val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
25571f95118Swdenk 			val1 &= 0xf000;
25671f95118Swdenk 			val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]);
25771f95118Swdenk 			val2 &= 0x00ff;
25871f95118Swdenk 			ret = (val2 << 4) | (val1 >> 12);
25971f95118Swdenk 			break;
26071f95118Swdenk 		case 2:
26171f95118Swdenk 			val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
26271f95118Swdenk 			val1 &= 0xff00;
26371f95118Swdenk 			val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]);
26471f95118Swdenk 			val2 &= 0x000f;
26571f95118Swdenk 			ret = (val2 << 8) | (val1 >> 8);
26671f95118Swdenk 			break;
26771f95118Swdenk 		case 3:
2687385c28eSWolfgang Denk 			ret = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
26971f95118Swdenk 			ret = (ret & 0xfff0) >> 4;
27071f95118Swdenk 			break;
27171f95118Swdenk 		default:
27271f95118Swdenk 			break;
27371f95118Swdenk 		}
27471f95118Swdenk 		break;
27571f95118Swdenk 	}
276*b8948d2aSStefan Brüns 	debug("FAT%d: ret: 0x%08x, entry: 0x%08x, offset: 0x%04x\n",
277*b8948d2aSStefan Brüns 	       mydata->fatsize, ret, entry, offset);
27871f95118Swdenk 
27971f95118Swdenk 	return ret;
28071f95118Swdenk }
28171f95118Swdenk 
28271f95118Swdenk /*
28371f95118Swdenk  * Read at most 'size' bytes from the specified cluster into 'buffer'.
28471f95118Swdenk  * Return 0 on success, -1 otherwise.
28571f95118Swdenk  */
28671f95118Swdenk static int
2879795e07bSBenoît Thébaudeau get_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size)
28871f95118Swdenk {
2893f270f42SErik Hansen 	__u32 idx = 0;
29071f95118Swdenk 	__u32 startsect;
29146236b14SKyle Moffett 	int ret;
29271f95118Swdenk 
29371f95118Swdenk 	if (clustnum > 0) {
2947385c28eSWolfgang Denk 		startsect = mydata->data_begin +
2957385c28eSWolfgang Denk 				clustnum * mydata->clust_size;
29671f95118Swdenk 	} else {
29771f95118Swdenk 		startsect = mydata->rootdir_sect;
29871f95118Swdenk 	}
29971f95118Swdenk 
3007385c28eSWolfgang Denk 	debug("gc - clustnum: %d, startsect: %d\n", clustnum, startsect);
3017385c28eSWolfgang Denk 
302cc63b25eSBenoît Thébaudeau 	if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
3039a800ac7SEric Nelson 		ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
3047385c28eSWolfgang Denk 
305cc63b25eSBenoît Thébaudeau 		printf("FAT: Misaligned buffer address (%p)\n", buffer);
306cc63b25eSBenoît Thébaudeau 
307cc63b25eSBenoît Thébaudeau 		while (size >= mydata->sect_size) {
308cc63b25eSBenoît Thébaudeau 			ret = disk_read(startsect++, 1, tmpbuf);
30946236b14SKyle Moffett 			if (ret != 1) {
31046236b14SKyle Moffett 				debug("Error reading data (got %d)\n", ret);
31171f95118Swdenk 				return -1;
31271f95118Swdenk 			}
31371f95118Swdenk 
314cc63b25eSBenoît Thébaudeau 			memcpy(buffer, tmpbuf, mydata->sect_size);
315cc63b25eSBenoît Thébaudeau 			buffer += mydata->sect_size;
316cc63b25eSBenoît Thébaudeau 			size -= mydata->sect_size;
317cc63b25eSBenoît Thébaudeau 		}
318cc63b25eSBenoît Thébaudeau 	} else {
319cc63b25eSBenoît Thébaudeau 		idx = size / mydata->sect_size;
320cc63b25eSBenoît Thébaudeau 		ret = disk_read(startsect, idx, buffer);
321cc63b25eSBenoît Thébaudeau 		if (ret != idx) {
322cc63b25eSBenoît Thébaudeau 			debug("Error reading data (got %d)\n", ret);
323cc63b25eSBenoît Thébaudeau 			return -1;
324cc63b25eSBenoît Thébaudeau 		}
325cc63b25eSBenoît Thébaudeau 		startsect += idx;
326cc63b25eSBenoît Thébaudeau 		idx *= mydata->sect_size;
327cc63b25eSBenoît Thébaudeau 		buffer += idx;
328cc63b25eSBenoît Thébaudeau 		size -= idx;
329cc63b25eSBenoît Thébaudeau 	}
330cc63b25eSBenoît Thébaudeau 	if (size) {
331cc63b25eSBenoît Thébaudeau 		ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
332cc63b25eSBenoît Thébaudeau 
333cc63b25eSBenoît Thébaudeau 		ret = disk_read(startsect, 1, tmpbuf);
334cc63b25eSBenoît Thébaudeau 		if (ret != 1) {
335cc63b25eSBenoît Thébaudeau 			debug("Error reading data (got %d)\n", ret);
336cc63b25eSBenoît Thébaudeau 			return -1;
337cc63b25eSBenoît Thébaudeau 		}
338cc63b25eSBenoît Thébaudeau 
339cc63b25eSBenoît Thébaudeau 		memcpy(buffer, tmpbuf, size);
34071f95118Swdenk 	}
34171f95118Swdenk 
34271f95118Swdenk 	return 0;
34371f95118Swdenk }
34471f95118Swdenk 
34571f95118Swdenk /*
3461170e634SBenoît Thébaudeau  * Read at most 'maxsize' bytes from 'pos' in the file associated with 'dentptr'
34771f95118Swdenk  * into 'buffer'.
3481ad0b98aSSuriyan Ramasami  * Update the number of bytes read in *gotsize or return -1 on fatal errors.
34971f95118Swdenk  */
3501170e634SBenoît Thébaudeau __u8 get_contents_vfatname_block[MAX_CLUSTSIZE]
3511170e634SBenoît Thébaudeau 	__aligned(ARCH_DMA_MINALIGN);
3521170e634SBenoît Thébaudeau 
3531ad0b98aSSuriyan Ramasami static int get_contents(fsdata *mydata, dir_entry *dentptr, loff_t pos,
3541ad0b98aSSuriyan Ramasami 			__u8 *buffer, loff_t maxsize, loff_t *gotsize)
35571f95118Swdenk {
3561ad0b98aSSuriyan Ramasami 	loff_t filesize = FAT2CPU32(dentptr->size);
357ac497771SSergei Shtylyov 	unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
35871f95118Swdenk 	__u32 curclust = START(dentptr);
3597205e407Swdenk 	__u32 endclust, newclust;
3601ad0b98aSSuriyan Ramasami 	loff_t actsize;
36171f95118Swdenk 
3621ad0b98aSSuriyan Ramasami 	*gotsize = 0;
3631ad0b98aSSuriyan Ramasami 	debug("Filesize: %llu bytes\n", filesize);
36471f95118Swdenk 
3651170e634SBenoît Thébaudeau 	if (pos >= filesize) {
3661ad0b98aSSuriyan Ramasami 		debug("Read position past EOF: %llu\n", pos);
3671ad0b98aSSuriyan Ramasami 		return 0;
3681170e634SBenoît Thébaudeau 	}
3691170e634SBenoît Thébaudeau 
3701170e634SBenoît Thébaudeau 	if (maxsize > 0 && filesize > pos + maxsize)
3711170e634SBenoît Thébaudeau 		filesize = pos + maxsize;
37271f95118Swdenk 
3731ad0b98aSSuriyan Ramasami 	debug("%llu bytes\n", filesize);
37471f95118Swdenk 
3757205e407Swdenk 	actsize = bytesperclust;
3761170e634SBenoît Thébaudeau 
3771170e634SBenoît Thébaudeau 	/* go to cluster at pos */
3781170e634SBenoît Thébaudeau 	while (actsize <= pos) {
3791170e634SBenoît Thébaudeau 		curclust = get_fatent(mydata, curclust);
3801170e634SBenoît Thébaudeau 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
3811170e634SBenoît Thébaudeau 			debug("curclust: 0x%x\n", curclust);
3821170e634SBenoît Thébaudeau 			debug("Invalid FAT entry\n");
3831ad0b98aSSuriyan Ramasami 			return 0;
3841170e634SBenoît Thébaudeau 		}
3851170e634SBenoît Thébaudeau 		actsize += bytesperclust;
3861170e634SBenoît Thébaudeau 	}
3871170e634SBenoît Thébaudeau 
3881170e634SBenoît Thébaudeau 	/* actsize > pos */
3891170e634SBenoît Thébaudeau 	actsize -= bytesperclust;
3901170e634SBenoît Thébaudeau 	filesize -= actsize;
3911170e634SBenoît Thébaudeau 	pos -= actsize;
3921170e634SBenoît Thébaudeau 
3931170e634SBenoît Thébaudeau 	/* align to beginning of next cluster if any */
3941170e634SBenoît Thébaudeau 	if (pos) {
3951ad0b98aSSuriyan Ramasami 		actsize = min(filesize, (loff_t)bytesperclust);
3961170e634SBenoît Thébaudeau 		if (get_cluster(mydata, curclust, get_contents_vfatname_block,
3971170e634SBenoît Thébaudeau 				(int)actsize) != 0) {
3981170e634SBenoît Thébaudeau 			printf("Error reading cluster\n");
3991170e634SBenoît Thébaudeau 			return -1;
4001170e634SBenoît Thébaudeau 		}
4011170e634SBenoît Thébaudeau 		filesize -= actsize;
4021170e634SBenoît Thébaudeau 		actsize -= pos;
4031170e634SBenoît Thébaudeau 		memcpy(buffer, get_contents_vfatname_block + pos, actsize);
4041ad0b98aSSuriyan Ramasami 		*gotsize += actsize;
4051170e634SBenoît Thébaudeau 		if (!filesize)
4061ad0b98aSSuriyan Ramasami 			return 0;
4071170e634SBenoît Thébaudeau 		buffer += actsize;
4081170e634SBenoît Thébaudeau 
4091170e634SBenoît Thébaudeau 		curclust = get_fatent(mydata, curclust);
4101170e634SBenoît Thébaudeau 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
4111170e634SBenoît Thébaudeau 			debug("curclust: 0x%x\n", curclust);
4121170e634SBenoît Thébaudeau 			debug("Invalid FAT entry\n");
4131ad0b98aSSuriyan Ramasami 			return 0;
4141170e634SBenoît Thébaudeau 		}
4151170e634SBenoît Thébaudeau 	}
4161170e634SBenoît Thébaudeau 
4171170e634SBenoît Thébaudeau 	actsize = bytesperclust;
4187205e407Swdenk 	endclust = curclust;
4197385c28eSWolfgang Denk 
42071f95118Swdenk 	do {
4217205e407Swdenk 		/* search for consecutive clusters */
4227205e407Swdenk 		while (actsize < filesize) {
4237205e407Swdenk 			newclust = get_fatent(mydata, endclust);
4247205e407Swdenk 			if ((newclust - 1) != endclust)
4257205e407Swdenk 				goto getit;
4268ce4e5c2Smichael 			if (CHECK_CLUST(newclust, mydata->fatsize)) {
4277385c28eSWolfgang Denk 				debug("curclust: 0x%x\n", newclust);
4287385c28eSWolfgang Denk 				debug("Invalid FAT entry\n");
4291ad0b98aSSuriyan Ramasami 				return 0;
4307205e407Swdenk 			}
4317205e407Swdenk 			endclust = newclust;
4327205e407Swdenk 			actsize += bytesperclust;
4337205e407Swdenk 		}
4347385c28eSWolfgang Denk 
4357205e407Swdenk 		/* get remaining bytes */
4367205e407Swdenk 		actsize = filesize;
4370880e5bbSBenoît Thébaudeau 		if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
4387385c28eSWolfgang Denk 			printf("Error reading cluster\n");
4397205e407Swdenk 			return -1;
4407205e407Swdenk 		}
4411ad0b98aSSuriyan Ramasami 		*gotsize += actsize;
4421ad0b98aSSuriyan Ramasami 		return 0;
4437205e407Swdenk getit:
4447205e407Swdenk 		if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
4457385c28eSWolfgang Denk 			printf("Error reading cluster\n");
4467205e407Swdenk 			return -1;
4477205e407Swdenk 		}
4481ad0b98aSSuriyan Ramasami 		*gotsize += (int)actsize;
4497205e407Swdenk 		filesize -= actsize;
4507205e407Swdenk 		buffer += actsize;
4517385c28eSWolfgang Denk 
4527205e407Swdenk 		curclust = get_fatent(mydata, endclust);
4538ce4e5c2Smichael 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
4547385c28eSWolfgang Denk 			debug("curclust: 0x%x\n", curclust);
4557385c28eSWolfgang Denk 			printf("Invalid FAT entry\n");
4561ad0b98aSSuriyan Ramasami 			return 0;
45771f95118Swdenk 		}
4587205e407Swdenk 		actsize = bytesperclust;
4597205e407Swdenk 		endclust = curclust;
46071f95118Swdenk 	} while (1);
46171f95118Swdenk }
46271f95118Swdenk 
46371f95118Swdenk /*
46471f95118Swdenk  * Extract the file name information from 'slotptr' into 'l_name',
46571f95118Swdenk  * starting at l_name[*idx].
46671f95118Swdenk  * Return 1 if terminator (zero byte) is found, 0 otherwise.
46771f95118Swdenk  */
4687385c28eSWolfgang Denk static int slot2str(dir_slot *slotptr, char *l_name, int *idx)
46971f95118Swdenk {
47071f95118Swdenk 	int j;
47171f95118Swdenk 
47271f95118Swdenk 	for (j = 0; j <= 8; j += 2) {
47371f95118Swdenk 		l_name[*idx] = slotptr->name0_4[j];
4747385c28eSWolfgang Denk 		if (l_name[*idx] == 0x00)
4757385c28eSWolfgang Denk 			return 1;
47671f95118Swdenk 		(*idx)++;
47771f95118Swdenk 	}
47871f95118Swdenk 	for (j = 0; j <= 10; j += 2) {
47971f95118Swdenk 		l_name[*idx] = slotptr->name5_10[j];
4807385c28eSWolfgang Denk 		if (l_name[*idx] == 0x00)
4817385c28eSWolfgang Denk 			return 1;
48271f95118Swdenk 		(*idx)++;
48371f95118Swdenk 	}
48471f95118Swdenk 	for (j = 0; j <= 2; j += 2) {
48571f95118Swdenk 		l_name[*idx] = slotptr->name11_12[j];
4867385c28eSWolfgang Denk 		if (l_name[*idx] == 0x00)
4877385c28eSWolfgang Denk 			return 1;
48871f95118Swdenk 		(*idx)++;
48971f95118Swdenk 	}
49071f95118Swdenk 
49171f95118Swdenk 	return 0;
49271f95118Swdenk }
49371f95118Swdenk 
49471f95118Swdenk /*
49571f95118Swdenk  * Extract the full long filename starting at 'retdent' (which is really
49671f95118Swdenk  * a slot) into 'l_name'. If successful also copy the real directory entry
49771f95118Swdenk  * into 'retdent'
49871f95118Swdenk  * Return 0 on success, -1 otherwise.
49971f95118Swdenk  */
50071f95118Swdenk static int
50171f95118Swdenk get_vfatname(fsdata *mydata, int curclust, __u8 *cluster,
50271f95118Swdenk 	     dir_entry *retdent, char *l_name)
50371f95118Swdenk {
50471f95118Swdenk 	dir_entry *realdent;
50571f95118Swdenk 	dir_slot *slotptr = (dir_slot *)retdent;
506025421eaSSergei Shtylyov 	__u8 *buflimit = cluster + mydata->sect_size * ((curclust == 0) ?
507025421eaSSergei Shtylyov 							PREFETCH_BLOCKS :
508025421eaSSergei Shtylyov 							mydata->clust_size);
5092d1a537dSwdenk 	__u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff;
51071f95118Swdenk 	int idx = 0;
51171f95118Swdenk 
5123831530dSMikhail Zolotaryov 	if (counter > VFAT_MAXSEQ) {
5133831530dSMikhail Zolotaryov 		debug("Error: VFAT name is too long\n");
5143831530dSMikhail Zolotaryov 		return -1;
5153831530dSMikhail Zolotaryov 	}
5163831530dSMikhail Zolotaryov 
5173831530dSMikhail Zolotaryov 	while ((__u8 *)slotptr < buflimit) {
5187385c28eSWolfgang Denk 		if (counter == 0)
5197385c28eSWolfgang Denk 			break;
5202d1a537dSwdenk 		if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter)
5212d1a537dSwdenk 			return -1;
52271f95118Swdenk 		slotptr++;
52371f95118Swdenk 		counter--;
52471f95118Swdenk 	}
52571f95118Swdenk 
5263831530dSMikhail Zolotaryov 	if ((__u8 *)slotptr >= buflimit) {
52771f95118Swdenk 		dir_slot *slotptr2;
52871f95118Swdenk 
5293831530dSMikhail Zolotaryov 		if (curclust == 0)
5303831530dSMikhail Zolotaryov 			return -1;
53171f95118Swdenk 		curclust = get_fatent(mydata, curclust);
5328ce4e5c2Smichael 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
5337385c28eSWolfgang Denk 			debug("curclust: 0x%x\n", curclust);
5347385c28eSWolfgang Denk 			printf("Invalid FAT entry\n");
53571f95118Swdenk 			return -1;
53671f95118Swdenk 		}
5377385c28eSWolfgang Denk 
5381170e634SBenoît Thébaudeau 		if (get_cluster(mydata, curclust, get_contents_vfatname_block,
539ac497771SSergei Shtylyov 				mydata->clust_size * mydata->sect_size) != 0) {
5407385c28eSWolfgang Denk 			debug("Error: reading directory block\n");
54171f95118Swdenk 			return -1;
54271f95118Swdenk 		}
5437385c28eSWolfgang Denk 
5441170e634SBenoît Thébaudeau 		slotptr2 = (dir_slot *)get_contents_vfatname_block;
5453831530dSMikhail Zolotaryov 		while (counter > 0) {
5463831530dSMikhail Zolotaryov 			if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK)
5473831530dSMikhail Zolotaryov 			    & 0xff) != counter)
5483831530dSMikhail Zolotaryov 				return -1;
54971f95118Swdenk 			slotptr2++;
5503831530dSMikhail Zolotaryov 			counter--;
5513831530dSMikhail Zolotaryov 		}
5527385c28eSWolfgang Denk 
55371f95118Swdenk 		/* Save the real directory entry */
5543831530dSMikhail Zolotaryov 		realdent = (dir_entry *)slotptr2;
5551170e634SBenoît Thébaudeau 		while ((__u8 *)slotptr2 > get_contents_vfatname_block) {
55671f95118Swdenk 			slotptr2--;
5573831530dSMikhail Zolotaryov 			slot2str(slotptr2, l_name, &idx);
55871f95118Swdenk 		}
55971f95118Swdenk 	} else {
56071f95118Swdenk 		/* Save the real directory entry */
56171f95118Swdenk 		realdent = (dir_entry *)slotptr;
56271f95118Swdenk 	}
56371f95118Swdenk 
56471f95118Swdenk 	do {
56571f95118Swdenk 		slotptr--;
5667385c28eSWolfgang Denk 		if (slot2str(slotptr, l_name, &idx))
5677385c28eSWolfgang Denk 			break;
5682d1a537dSwdenk 	} while (!(slotptr->id & LAST_LONG_ENTRY_MASK));
56971f95118Swdenk 
57071f95118Swdenk 	l_name[idx] = '\0';
5717385c28eSWolfgang Denk 	if (*l_name == DELETED_FLAG)
5727385c28eSWolfgang Denk 		*l_name = '\0';
5737385c28eSWolfgang Denk 	else if (*l_name == aRING)
5747385c28eSWolfgang Denk 		*l_name = DELETED_FLAG;
57571f95118Swdenk 	downcase(l_name);
57671f95118Swdenk 
57771f95118Swdenk 	/* Return the real directory entry */
57871f95118Swdenk 	memcpy(retdent, realdent, sizeof(dir_entry));
57971f95118Swdenk 
58071f95118Swdenk 	return 0;
58171f95118Swdenk }
58271f95118Swdenk 
58371f95118Swdenk /* Calculate short name checksum */
584ff04f6d1SMarek Vasut static __u8 mkcksum(const char name[8], const char ext[3])
58571f95118Swdenk {
58671f95118Swdenk 	int i;
5877385c28eSWolfgang Denk 
58871f95118Swdenk 	__u8 ret = 0;
58971f95118Swdenk 
5906ad77d88SMarek Vasut 	for (i = 0; i < 8; i++)
591ff04f6d1SMarek Vasut 		ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + name[i];
5926ad77d88SMarek Vasut 	for (i = 0; i < 3; i++)
593ff04f6d1SMarek Vasut 		ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + ext[i];
59471f95118Swdenk 
59571f95118Swdenk 	return ret;
59671f95118Swdenk }
59771f95118Swdenk 
59871f95118Swdenk /*
59971f95118Swdenk  * Get the directory entry associated with 'filename' from the directory
60071f95118Swdenk  * starting at 'startsect'
60171f95118Swdenk  */
6029a800ac7SEric Nelson __u8 get_dentfromdir_block[MAX_CLUSTSIZE]
6039a800ac7SEric Nelson 	__aligned(ARCH_DMA_MINALIGN);
6047385c28eSWolfgang Denk 
60571f95118Swdenk static dir_entry *get_dentfromdir(fsdata *mydata, int startsect,
60671f95118Swdenk 				  char *filename, dir_entry *retdent,
60771f95118Swdenk 				  int dols)
60871f95118Swdenk {
60971f95118Swdenk 	__u16 prevcksum = 0xffff;
61071f95118Swdenk 	__u32 curclust = START(retdent);
61171f95118Swdenk 	int files = 0, dirs = 0;
61271f95118Swdenk 
6137385c28eSWolfgang Denk 	debug("get_dentfromdir: %s\n", filename);
6147385c28eSWolfgang Denk 
61571f95118Swdenk 	while (1) {
61671f95118Swdenk 		dir_entry *dentptr;
6177385c28eSWolfgang Denk 
61871f95118Swdenk 		int i;
61971f95118Swdenk 
6205fa66df6Swdenk 		if (get_cluster(mydata, curclust, get_dentfromdir_block,
621ac497771SSergei Shtylyov 				mydata->clust_size * mydata->sect_size) != 0) {
6227385c28eSWolfgang Denk 			debug("Error: reading directory block\n");
62371f95118Swdenk 			return NULL;
62471f95118Swdenk 		}
6257385c28eSWolfgang Denk 
6265fa66df6Swdenk 		dentptr = (dir_entry *)get_dentfromdir_block;
6277385c28eSWolfgang Denk 
62871f95118Swdenk 		for (i = 0; i < DIRENTSPERCLUST; i++) {
6293831530dSMikhail Zolotaryov 			char s_name[14], l_name[VFAT_MAXLEN_BYTES];
63071f95118Swdenk 
63171f95118Swdenk 			l_name[0] = '\0';
632855a496fSwdenk 			if (dentptr->name[0] == DELETED_FLAG) {
633855a496fSwdenk 				dentptr++;
634855a496fSwdenk 				continue;
635855a496fSwdenk 			}
63671f95118Swdenk 			if ((dentptr->attr & ATTR_VOLUME)) {
637cb940c7eSRichard Genoud 				if (vfat_enabled &&
638cb940c7eSRichard Genoud 				    (dentptr->attr & ATTR_VFAT) == ATTR_VFAT &&
6392d1a537dSwdenk 				    (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
6407385c28eSWolfgang Denk 					prevcksum = ((dir_slot *)dentptr)->alias_checksum;
6417385c28eSWolfgang Denk 					get_vfatname(mydata, curclust,
6427385c28eSWolfgang Denk 						     get_dentfromdir_block,
64371f95118Swdenk 						     dentptr, l_name);
64471f95118Swdenk 					if (dols) {
6457385c28eSWolfgang Denk 						int isdir;
64671f95118Swdenk 						char dirc;
64771f95118Swdenk 						int doit = 0;
64871f95118Swdenk 
6497385c28eSWolfgang Denk 						isdir = (dentptr->attr & ATTR_DIR);
6507385c28eSWolfgang Denk 
65171f95118Swdenk 						if (isdir) {
65271f95118Swdenk 							dirs++;
65371f95118Swdenk 							dirc = '/';
65471f95118Swdenk 							doit = 1;
65571f95118Swdenk 						} else {
65671f95118Swdenk 							dirc = ' ';
65771f95118Swdenk 							if (l_name[0] != 0) {
65871f95118Swdenk 								files++;
65971f95118Swdenk 								doit = 1;
66071f95118Swdenk 							}
66171f95118Swdenk 						}
66271f95118Swdenk 						if (doit) {
66371f95118Swdenk 							if (dirc == ' ') {
6641ad0b98aSSuriyan Ramasami 								printf(" %8u   %s%c\n",
6651ad0b98aSSuriyan Ramasami 								       FAT2CPU32(dentptr->size),
6667385c28eSWolfgang Denk 									l_name,
6677385c28eSWolfgang Denk 									dirc);
66871f95118Swdenk 							} else {
6697385c28eSWolfgang Denk 								printf("            %s%c\n",
6707385c28eSWolfgang Denk 									l_name,
6717385c28eSWolfgang Denk 									dirc);
67271f95118Swdenk 							}
67371f95118Swdenk 						}
67471f95118Swdenk 						dentptr++;
67571f95118Swdenk 						continue;
67671f95118Swdenk 					}
6777385c28eSWolfgang Denk 					debug("vfatname: |%s|\n", l_name);
678cb940c7eSRichard Genoud 				} else {
67971f95118Swdenk 					/* Volume label or VFAT entry */
68071f95118Swdenk 					dentptr++;
68171f95118Swdenk 					continue;
68271f95118Swdenk 				}
68371f95118Swdenk 			}
68471f95118Swdenk 			if (dentptr->name[0] == 0) {
68571f95118Swdenk 				if (dols) {
6867385c28eSWolfgang Denk 					printf("\n%d file(s), %d dir(s)\n\n",
6877385c28eSWolfgang Denk 						files, dirs);
68871f95118Swdenk 				}
6897385c28eSWolfgang Denk 				debug("Dentname == NULL - %d\n", i);
69071f95118Swdenk 				return NULL;
69171f95118Swdenk 			}
692cb940c7eSRichard Genoud 			if (vfat_enabled) {
693ff04f6d1SMarek Vasut 				__u8 csum = mkcksum(dentptr->name, dentptr->ext);
694ff04f6d1SMarek Vasut 				if (dols && csum == prevcksum) {
695bf34e7d9SSergei Shtylyov 					prevcksum = 0xffff;
69671f95118Swdenk 					dentptr++;
69771f95118Swdenk 					continue;
69871f95118Swdenk 				}
699cb940c7eSRichard Genoud 			}
700cb940c7eSRichard Genoud 
70171f95118Swdenk 			get_name(dentptr, s_name);
70271f95118Swdenk 			if (dols) {
70371f95118Swdenk 				int isdir = (dentptr->attr & ATTR_DIR);
70471f95118Swdenk 				char dirc;
70571f95118Swdenk 				int doit = 0;
70671f95118Swdenk 
70771f95118Swdenk 				if (isdir) {
70871f95118Swdenk 					dirs++;
70971f95118Swdenk 					dirc = '/';
71071f95118Swdenk 					doit = 1;
71171f95118Swdenk 				} else {
71271f95118Swdenk 					dirc = ' ';
71371f95118Swdenk 					if (s_name[0] != 0) {
71471f95118Swdenk 						files++;
71571f95118Swdenk 						doit = 1;
71671f95118Swdenk 					}
71771f95118Swdenk 				}
7187385c28eSWolfgang Denk 
71971f95118Swdenk 				if (doit) {
72071f95118Swdenk 					if (dirc == ' ') {
7211ad0b98aSSuriyan Ramasami 						printf(" %8u   %s%c\n",
7221ad0b98aSSuriyan Ramasami 						       FAT2CPU32(dentptr->size),
7237385c28eSWolfgang Denk 							s_name, dirc);
72471f95118Swdenk 					} else {
7257385c28eSWolfgang Denk 						printf("            %s%c\n",
7267385c28eSWolfgang Denk 							s_name, dirc);
72771f95118Swdenk 					}
72871f95118Swdenk 				}
7297385c28eSWolfgang Denk 
73071f95118Swdenk 				dentptr++;
73171f95118Swdenk 				continue;
73271f95118Swdenk 			}
7337385c28eSWolfgang Denk 
7347385c28eSWolfgang Denk 			if (strcmp(filename, s_name)
7357385c28eSWolfgang Denk 			    && strcmp(filename, l_name)) {
7367385c28eSWolfgang Denk 				debug("Mismatch: |%s|%s|\n", s_name, l_name);
73771f95118Swdenk 				dentptr++;
73871f95118Swdenk 				continue;
73971f95118Swdenk 			}
7407385c28eSWolfgang Denk 
74171f95118Swdenk 			memcpy(retdent, dentptr, sizeof(dir_entry));
74271f95118Swdenk 
7437385c28eSWolfgang Denk 			debug("DentName: %s", s_name);
7447385c28eSWolfgang Denk 			debug(", start: 0x%x", START(dentptr));
7457385c28eSWolfgang Denk 			debug(", size:  0x%x %s\n",
74671f95118Swdenk 			      FAT2CPU32(dentptr->size),
74771f95118Swdenk 			      (dentptr->attr & ATTR_DIR) ? "(DIR)" : "");
74871f95118Swdenk 
74971f95118Swdenk 			return retdent;
75071f95118Swdenk 		}
7517385c28eSWolfgang Denk 
75271f95118Swdenk 		curclust = get_fatent(mydata, curclust);
7538ce4e5c2Smichael 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
7547385c28eSWolfgang Denk 			debug("curclust: 0x%x\n", curclust);
7557385c28eSWolfgang Denk 			printf("Invalid FAT entry\n");
75671f95118Swdenk 			return NULL;
75771f95118Swdenk 		}
75871f95118Swdenk 	}
75971f95118Swdenk 
76071f95118Swdenk 	return NULL;
76171f95118Swdenk }
76271f95118Swdenk 
76371f95118Swdenk /*
76471f95118Swdenk  * Read boot sector and volume info from a FAT filesystem
76571f95118Swdenk  */
76671f95118Swdenk static int
76771f95118Swdenk read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize)
76871f95118Swdenk {
769ac497771SSergei Shtylyov 	__u8 *block;
77071f95118Swdenk 	volume_info *vistart;
771ac497771SSergei Shtylyov 	int ret = 0;
772ac497771SSergei Shtylyov 
773ac497771SSergei Shtylyov 	if (cur_dev == NULL) {
774ac497771SSergei Shtylyov 		debug("Error: no device selected\n");
775ac497771SSergei Shtylyov 		return -1;
776ac497771SSergei Shtylyov 	}
777ac497771SSergei Shtylyov 
7789a800ac7SEric Nelson 	block = memalign(ARCH_DMA_MINALIGN, cur_dev->blksz);
779ac497771SSergei Shtylyov 	if (block == NULL) {
780ac497771SSergei Shtylyov 		debug("Error: allocating block\n");
781ac497771SSergei Shtylyov 		return -1;
782ac497771SSergei Shtylyov 	}
78371f95118Swdenk 
78471f95118Swdenk 	if (disk_read(0, 1, block) < 0) {
7857385c28eSWolfgang Denk 		debug("Error: reading block\n");
786ac497771SSergei Shtylyov 		goto fail;
78771f95118Swdenk 	}
78871f95118Swdenk 
78971f95118Swdenk 	memcpy(bs, block, sizeof(boot_sector));
79071f95118Swdenk 	bs->reserved = FAT2CPU16(bs->reserved);
79171f95118Swdenk 	bs->fat_length = FAT2CPU16(bs->fat_length);
79271f95118Swdenk 	bs->secs_track = FAT2CPU16(bs->secs_track);
79371f95118Swdenk 	bs->heads = FAT2CPU16(bs->heads);
79471f95118Swdenk 	bs->total_sect = FAT2CPU32(bs->total_sect);
79571f95118Swdenk 
79671f95118Swdenk 	/* FAT32 entries */
79771f95118Swdenk 	if (bs->fat_length == 0) {
79871f95118Swdenk 		/* Assume FAT32 */
79971f95118Swdenk 		bs->fat32_length = FAT2CPU32(bs->fat32_length);
80071f95118Swdenk 		bs->flags = FAT2CPU16(bs->flags);
80171f95118Swdenk 		bs->root_cluster = FAT2CPU32(bs->root_cluster);
80271f95118Swdenk 		bs->info_sector = FAT2CPU16(bs->info_sector);
80371f95118Swdenk 		bs->backup_boot = FAT2CPU16(bs->backup_boot);
80471f95118Swdenk 		vistart = (volume_info *)(block + sizeof(boot_sector));
80571f95118Swdenk 		*fatsize = 32;
80671f95118Swdenk 	} else {
80771f95118Swdenk 		vistart = (volume_info *)&(bs->fat32_length);
80871f95118Swdenk 		*fatsize = 0;
80971f95118Swdenk 	}
81071f95118Swdenk 	memcpy(volinfo, vistart, sizeof(volume_info));
81171f95118Swdenk 
81271f95118Swdenk 	if (*fatsize == 32) {
8137385c28eSWolfgang Denk 		if (strncmp(FAT32_SIGN, vistart->fs_type, SIGNLEN) == 0)
814ac497771SSergei Shtylyov 			goto exit;
81571f95118Swdenk 	} else {
816651351feSTom Rix 		if (strncmp(FAT12_SIGN, vistart->fs_type, SIGNLEN) == 0) {
81771f95118Swdenk 			*fatsize = 12;
818ac497771SSergei Shtylyov 			goto exit;
81971f95118Swdenk 		}
820651351feSTom Rix 		if (strncmp(FAT16_SIGN, vistart->fs_type, SIGNLEN) == 0) {
82171f95118Swdenk 			*fatsize = 16;
822ac497771SSergei Shtylyov 			goto exit;
82371f95118Swdenk 		}
82471f95118Swdenk 	}
82571f95118Swdenk 
8267385c28eSWolfgang Denk 	debug("Error: broken fs_type sign\n");
827ac497771SSergei Shtylyov fail:
828ac497771SSergei Shtylyov 	ret = -1;
829ac497771SSergei Shtylyov exit:
830ac497771SSergei Shtylyov 	free(block);
831ac497771SSergei Shtylyov 	return ret;
83271f95118Swdenk }
83371f95118Swdenk 
8341170e634SBenoît Thébaudeau __u8 do_fat_read_at_block[MAX_CLUSTSIZE]
8359a800ac7SEric Nelson 	__aligned(ARCH_DMA_MINALIGN);
8367385c28eSWolfgang Denk 
8371ad0b98aSSuriyan Ramasami int do_fat_read_at(const char *filename, loff_t pos, void *buffer,
8381ad0b98aSSuriyan Ramasami 		   loff_t maxsize, int dols, int dogetsize, loff_t *size)
83971f95118Swdenk {
84071f95118Swdenk 	char fnamecopy[2048];
84171f95118Swdenk 	boot_sector bs;
84271f95118Swdenk 	volume_info volinfo;
84371f95118Swdenk 	fsdata datablock;
84471f95118Swdenk 	fsdata *mydata = &datablock;
845cd1b042cSBenoît Thébaudeau 	dir_entry *dentptr = NULL;
84671f95118Swdenk 	__u16 prevcksum = 0xffff;
84771f95118Swdenk 	char *subname = "";
8483f270f42SErik Hansen 	__u32 cursect;
84971f95118Swdenk 	int idx, isdir = 0;
85071f95118Swdenk 	int files = 0, dirs = 0;
8511ad0b98aSSuriyan Ramasami 	int ret = -1;
85271f95118Swdenk 	int firsttime;
85340e21916SSergei Shtylyov 	__u32 root_cluster = 0;
85464f65e1eSPrzemyslaw Marczak 	__u32 read_blk;
8553f270f42SErik Hansen 	int rootdir_size = 0;
85664f65e1eSPrzemyslaw Marczak 	int buffer_blk_cnt;
85764f65e1eSPrzemyslaw Marczak 	int do_read;
85864f65e1eSPrzemyslaw Marczak 	__u8 *dir_ptr;
85971f95118Swdenk 
86071f95118Swdenk 	if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) {
8617385c28eSWolfgang Denk 		debug("Error: reading boot sector\n");
86271f95118Swdenk 		return -1;
86371f95118Swdenk 	}
8647385c28eSWolfgang Denk 
86540e21916SSergei Shtylyov 	if (mydata->fatsize == 32) {
8662aa98c66SWolfgang Denk 		root_cluster = bs.root_cluster;
86771f95118Swdenk 		mydata->fatlength = bs.fat32_length;
86840e21916SSergei Shtylyov 	} else {
86971f95118Swdenk 		mydata->fatlength = bs.fat_length;
87040e21916SSergei Shtylyov 	}
8717385c28eSWolfgang Denk 
87271f95118Swdenk 	mydata->fat_sect = bs.reserved;
8737385c28eSWolfgang Denk 
87471f95118Swdenk 	cursect = mydata->rootdir_sect
87571f95118Swdenk 		= mydata->fat_sect + mydata->fatlength * bs.fats;
8767385c28eSWolfgang Denk 
877ac497771SSergei Shtylyov 	mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0];
87871f95118Swdenk 	mydata->clust_size = bs.cluster_size;
87946236b14SKyle Moffett 	if (mydata->sect_size != cur_part_info.blksz) {
88046236b14SKyle Moffett 		printf("Error: FAT sector size mismatch (fs=%hu, dev=%lu)\n",
88146236b14SKyle Moffett 				mydata->sect_size, cur_part_info.blksz);
88246236b14SKyle Moffett 		return -1;
88346236b14SKyle Moffett 	}
8847385c28eSWolfgang Denk 
88571f95118Swdenk 	if (mydata->fatsize == 32) {
8867385c28eSWolfgang Denk 		mydata->data_begin = mydata->rootdir_sect -
8877385c28eSWolfgang Denk 					(mydata->clust_size * 2);
88871f95118Swdenk 	} else {
8897385c28eSWolfgang Denk 		rootdir_size = ((bs.dir_entries[1]  * (int)256 +
8907385c28eSWolfgang Denk 				 bs.dir_entries[0]) *
8917385c28eSWolfgang Denk 				 sizeof(dir_entry)) /
892ac497771SSergei Shtylyov 				 mydata->sect_size;
8937385c28eSWolfgang Denk 		mydata->data_begin = mydata->rootdir_sect +
8947385c28eSWolfgang Denk 					rootdir_size -
8957385c28eSWolfgang Denk 					(mydata->clust_size * 2);
89671f95118Swdenk 	}
8977385c28eSWolfgang Denk 
89871f95118Swdenk 	mydata->fatbufnum = -1;
8993c0ed9c3SStefan Brüns 	mydata->fat_dirty = 0;
9009a800ac7SEric Nelson 	mydata->fatbuf = memalign(ARCH_DMA_MINALIGN, FATBUFSIZE);
901ac497771SSergei Shtylyov 	if (mydata->fatbuf == NULL) {
902ac497771SSergei Shtylyov 		debug("Error: allocating memory\n");
903ac497771SSergei Shtylyov 		return -1;
904ac497771SSergei Shtylyov 	}
90571f95118Swdenk 
906cb940c7eSRichard Genoud 	if (vfat_enabled)
9077385c28eSWolfgang Denk 		debug("VFAT Support enabled\n");
908cb940c7eSRichard Genoud 
9097385c28eSWolfgang Denk 	debug("FAT%d, fat_sect: %d, fatlength: %d\n",
9107385c28eSWolfgang Denk 	       mydata->fatsize, mydata->fat_sect, mydata->fatlength);
9117385c28eSWolfgang Denk 	debug("Rootdir begins at cluster: %d, sector: %d, offset: %x\n"
91271f95118Swdenk 	       "Data begins at: %d\n",
9132aa98c66SWolfgang Denk 	       root_cluster,
9142aa98c66SWolfgang Denk 	       mydata->rootdir_sect,
915ac497771SSergei Shtylyov 	       mydata->rootdir_sect * mydata->sect_size, mydata->data_begin);
916ac497771SSergei Shtylyov 	debug("Sector size: %d, cluster size: %d\n", mydata->sect_size,
917ac497771SSergei Shtylyov 	      mydata->clust_size);
91871f95118Swdenk 
91971f95118Swdenk 	/* "cwd" is always the root... */
92071f95118Swdenk 	while (ISDIRDELIM(*filename))
92171f95118Swdenk 		filename++;
9227385c28eSWolfgang Denk 
92371f95118Swdenk 	/* Make a copy of the filename and convert it to lowercase */
92471f95118Swdenk 	strcpy(fnamecopy, filename);
92571f95118Swdenk 	downcase(fnamecopy);
9267385c28eSWolfgang Denk 
92718a10d46SStephen Warren root_reparse:
92871f95118Swdenk 	if (*fnamecopy == '\0') {
92971f95118Swdenk 		if (!dols)
930ac497771SSergei Shtylyov 			goto exit;
9317385c28eSWolfgang Denk 
93271f95118Swdenk 		dols = LS_ROOT;
93371f95118Swdenk 	} else if ((idx = dirdelim(fnamecopy)) >= 0) {
93471f95118Swdenk 		isdir = 1;
93571f95118Swdenk 		fnamecopy[idx] = '\0';
93671f95118Swdenk 		subname = fnamecopy + idx + 1;
9377385c28eSWolfgang Denk 
93871f95118Swdenk 		/* Handle multiple delimiters */
93971f95118Swdenk 		while (ISDIRDELIM(*subname))
94071f95118Swdenk 			subname++;
94171f95118Swdenk 	} else if (dols) {
94271f95118Swdenk 		isdir = 1;
94371f95118Swdenk 	}
94471f95118Swdenk 
94564f65e1eSPrzemyslaw Marczak 	buffer_blk_cnt = 0;
94664f65e1eSPrzemyslaw Marczak 	firsttime = 1;
94771f95118Swdenk 	while (1) {
94871f95118Swdenk 		int i;
94971f95118Swdenk 
95064f65e1eSPrzemyslaw Marczak 		if (mydata->fatsize == 32 || firsttime) {
95164f65e1eSPrzemyslaw Marczak 			dir_ptr = do_fat_read_at_block;
95264f65e1eSPrzemyslaw Marczak 			firsttime = 0;
95364f65e1eSPrzemyslaw Marczak 		} else {
95464f65e1eSPrzemyslaw Marczak 			/**
95564f65e1eSPrzemyslaw Marczak 			 * FAT16 sector buffer modification:
95664f65e1eSPrzemyslaw Marczak 			 * Each loop, the second buffered block is moved to
95764f65e1eSPrzemyslaw Marczak 			 * the buffer begin, and two next sectors are read
95864f65e1eSPrzemyslaw Marczak 			 * next to the previously moved one. So the sector
95964f65e1eSPrzemyslaw Marczak 			 * buffer keeps always 3 sectors for fat16.
96064f65e1eSPrzemyslaw Marczak 			 * And the current sector is the buffer second sector
96164f65e1eSPrzemyslaw Marczak 			 * beside the "firsttime" read, when it is the first one.
96264f65e1eSPrzemyslaw Marczak 			 *
96364f65e1eSPrzemyslaw Marczak 			 * PREFETCH_BLOCKS is 2 for FAT16 == loop[0:1]
96464f65e1eSPrzemyslaw Marczak 			 * n = computed root dir sector
96564f65e1eSPrzemyslaw Marczak 			 * loop |  cursect-1  | cursect    | cursect+1  |
96664f65e1eSPrzemyslaw Marczak 			 *   0  |  sector n+0 | sector n+1 | none       |
96764f65e1eSPrzemyslaw Marczak 			 *   1  |  none       | sector n+0 | sector n+1 |
96864f65e1eSPrzemyslaw Marczak 			 *   0  |  sector n+1 | sector n+2 | sector n+3 |
96964f65e1eSPrzemyslaw Marczak 			 *   1  |  sector n+3 | ...
97064f65e1eSPrzemyslaw Marczak 			*/
97164f65e1eSPrzemyslaw Marczak 			dir_ptr = (do_fat_read_at_block + mydata->sect_size);
97264f65e1eSPrzemyslaw Marczak 			memcpy(do_fat_read_at_block, dir_ptr, mydata->sect_size);
97364f65e1eSPrzemyslaw Marczak 		}
9747385c28eSWolfgang Denk 
97564f65e1eSPrzemyslaw Marczak 		do_read = 1;
97664f65e1eSPrzemyslaw Marczak 
97764f65e1eSPrzemyslaw Marczak 		if (mydata->fatsize == 32 && buffer_blk_cnt)
97864f65e1eSPrzemyslaw Marczak 			do_read = 0;
97964f65e1eSPrzemyslaw Marczak 
98064f65e1eSPrzemyslaw Marczak 		if (do_read) {
98164f65e1eSPrzemyslaw Marczak 			read_blk = (mydata->fatsize == 32) ?
98264f65e1eSPrzemyslaw Marczak 				    mydata->clust_size : PREFETCH_BLOCKS;
98364f65e1eSPrzemyslaw Marczak 
98464f65e1eSPrzemyslaw Marczak 			debug("FAT read(sect=%d, cnt:%d), clust_size=%d, DIRENTSPERBLOCK=%zd\n",
98564f65e1eSPrzemyslaw Marczak 				cursect, read_blk, mydata->clust_size, DIRENTSPERBLOCK);
98664f65e1eSPrzemyslaw Marczak 
98764f65e1eSPrzemyslaw Marczak 			if (disk_read(cursect, read_blk, dir_ptr) < 0) {
9887385c28eSWolfgang Denk 				debug("Error: reading rootdir block\n");
989ac497771SSergei Shtylyov 				goto exit;
99071f95118Swdenk 			}
9917385c28eSWolfgang Denk 
99264f65e1eSPrzemyslaw Marczak 			dentptr = (dir_entry *)dir_ptr;
993cd1b042cSBenoît Thébaudeau 		}
9947385c28eSWolfgang Denk 
99571f95118Swdenk 		for (i = 0; i < DIRENTSPERBLOCK; i++) {
9963831530dSMikhail Zolotaryov 			char s_name[14], l_name[VFAT_MAXLEN_BYTES];
997ff04f6d1SMarek Vasut 			__u8 csum;
99871f95118Swdenk 
99971f95118Swdenk 			l_name[0] = '\0';
10003831530dSMikhail Zolotaryov 			if (dentptr->name[0] == DELETED_FLAG) {
10013831530dSMikhail Zolotaryov 				dentptr++;
10023831530dSMikhail Zolotaryov 				continue;
10033831530dSMikhail Zolotaryov 			}
1004ff04f6d1SMarek Vasut 
1005cb940c7eSRichard Genoud 			if (vfat_enabled)
1006ff04f6d1SMarek Vasut 				csum = mkcksum(dentptr->name, dentptr->ext);
1007cb940c7eSRichard Genoud 
1008ff04f6d1SMarek Vasut 			if (dentptr->attr & ATTR_VOLUME) {
1009cb940c7eSRichard Genoud 				if (vfat_enabled &&
1010cb940c7eSRichard Genoud 				    (dentptr->attr & ATTR_VFAT) == ATTR_VFAT &&
10112d1a537dSwdenk 				    (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
10127385c28eSWolfgang Denk 					prevcksum =
10137385c28eSWolfgang Denk 						((dir_slot *)dentptr)->alias_checksum;
10147385c28eSWolfgang Denk 
10153831530dSMikhail Zolotaryov 					get_vfatname(mydata,
101640e21916SSergei Shtylyov 						     root_cluster,
101764f65e1eSPrzemyslaw Marczak 						     dir_ptr,
10187385c28eSWolfgang Denk 						     dentptr, l_name);
10197385c28eSWolfgang Denk 
102071f95118Swdenk 					if (dols == LS_ROOT) {
102171f95118Swdenk 						char dirc;
102271f95118Swdenk 						int doit = 0;
10237385c28eSWolfgang Denk 						int isdir =
10247385c28eSWolfgang Denk 							(dentptr->attr & ATTR_DIR);
102571f95118Swdenk 
102671f95118Swdenk 						if (isdir) {
102771f95118Swdenk 							dirs++;
102871f95118Swdenk 							dirc = '/';
102971f95118Swdenk 							doit = 1;
103071f95118Swdenk 						} else {
103171f95118Swdenk 							dirc = ' ';
103271f95118Swdenk 							if (l_name[0] != 0) {
103371f95118Swdenk 								files++;
103471f95118Swdenk 								doit = 1;
103571f95118Swdenk 							}
103671f95118Swdenk 						}
103771f95118Swdenk 						if (doit) {
103871f95118Swdenk 							if (dirc == ' ') {
10391ad0b98aSSuriyan Ramasami 								printf(" %8u   %s%c\n",
10401ad0b98aSSuriyan Ramasami 								       FAT2CPU32(dentptr->size),
10417385c28eSWolfgang Denk 									l_name,
10427385c28eSWolfgang Denk 									dirc);
104371f95118Swdenk 							} else {
10447385c28eSWolfgang Denk 								printf("            %s%c\n",
10457385c28eSWolfgang Denk 									l_name,
10467385c28eSWolfgang Denk 									dirc);
104771f95118Swdenk 							}
104871f95118Swdenk 						}
104971f95118Swdenk 						dentptr++;
105071f95118Swdenk 						continue;
105171f95118Swdenk 					}
10527385c28eSWolfgang Denk 					debug("Rootvfatname: |%s|\n",
10537385c28eSWolfgang Denk 					       l_name);
1054cb940c7eSRichard Genoud 				} else {
105571f95118Swdenk 					/* Volume label or VFAT entry */
105671f95118Swdenk 					dentptr++;
105771f95118Swdenk 					continue;
105871f95118Swdenk 				}
105971f95118Swdenk 			} else if (dentptr->name[0] == 0) {
10607385c28eSWolfgang Denk 				debug("RootDentname == NULL - %d\n", i);
106171f95118Swdenk 				if (dols == LS_ROOT) {
10627385c28eSWolfgang Denk 					printf("\n%d file(s), %d dir(s)\n\n",
10637385c28eSWolfgang Denk 						files, dirs);
1064ac497771SSergei Shtylyov 					ret = 0;
106571f95118Swdenk 				}
1066ac497771SSergei Shtylyov 				goto exit;
106771f95118Swdenk 			}
1068cb940c7eSRichard Genoud 			else if (vfat_enabled &&
1069cb940c7eSRichard Genoud 				 dols == LS_ROOT && csum == prevcksum) {
1070bf34e7d9SSergei Shtylyov 				prevcksum = 0xffff;
107171f95118Swdenk 				dentptr++;
107271f95118Swdenk 				continue;
107371f95118Swdenk 			}
1074cb940c7eSRichard Genoud 
107571f95118Swdenk 			get_name(dentptr, s_name);
10767385c28eSWolfgang Denk 
107771f95118Swdenk 			if (dols == LS_ROOT) {
107871f95118Swdenk 				int isdir = (dentptr->attr & ATTR_DIR);
107971f95118Swdenk 				char dirc;
108071f95118Swdenk 				int doit = 0;
108171f95118Swdenk 
108271f95118Swdenk 				if (isdir) {
108371f95118Swdenk 					dirc = '/';
1084a43278a4Swdenk 					if (s_name[0] != 0) {
1085a43278a4Swdenk 						dirs++;
108671f95118Swdenk 						doit = 1;
1087a43278a4Swdenk 					}
108871f95118Swdenk 				} else {
108971f95118Swdenk 					dirc = ' ';
109071f95118Swdenk 					if (s_name[0] != 0) {
109171f95118Swdenk 						files++;
109271f95118Swdenk 						doit = 1;
109371f95118Swdenk 					}
109471f95118Swdenk 				}
109571f95118Swdenk 				if (doit) {
109671f95118Swdenk 					if (dirc == ' ') {
10971ad0b98aSSuriyan Ramasami 						printf(" %8u   %s%c\n",
10981ad0b98aSSuriyan Ramasami 						       FAT2CPU32(dentptr->size),
10997385c28eSWolfgang Denk 							s_name, dirc);
110071f95118Swdenk 					} else {
11017385c28eSWolfgang Denk 						printf("            %s%c\n",
11027385c28eSWolfgang Denk 							s_name, dirc);
110371f95118Swdenk 					}
110471f95118Swdenk 				}
110571f95118Swdenk 				dentptr++;
110671f95118Swdenk 				continue;
110771f95118Swdenk 			}
11087385c28eSWolfgang Denk 
11097385c28eSWolfgang Denk 			if (strcmp(fnamecopy, s_name)
11107385c28eSWolfgang Denk 			    && strcmp(fnamecopy, l_name)) {
11117385c28eSWolfgang Denk 				debug("RootMismatch: |%s|%s|\n", s_name,
11127385c28eSWolfgang Denk 				       l_name);
111371f95118Swdenk 				dentptr++;
111471f95118Swdenk 				continue;
111571f95118Swdenk 			}
11167385c28eSWolfgang Denk 
111771f95118Swdenk 			if (isdir && !(dentptr->attr & ATTR_DIR))
1118ac497771SSergei Shtylyov 				goto exit;
111971f95118Swdenk 
11207385c28eSWolfgang Denk 			debug("RootName: %s", s_name);
11217385c28eSWolfgang Denk 			debug(", start: 0x%x", START(dentptr));
11227385c28eSWolfgang Denk 			debug(", size:  0x%x %s\n",
11237385c28eSWolfgang Denk 			       FAT2CPU32(dentptr->size),
11247385c28eSWolfgang Denk 			       isdir ? "(DIR)" : "");
112571f95118Swdenk 
112671f95118Swdenk 			goto rootdir_done;	/* We got a match */
112771f95118Swdenk 		}
112864f65e1eSPrzemyslaw Marczak 		debug("END LOOP: buffer_blk_cnt=%d   clust_size=%d\n", buffer_blk_cnt,
11297385c28eSWolfgang Denk 		       mydata->clust_size);
11302aa98c66SWolfgang Denk 
11312aa98c66SWolfgang Denk 		/*
11322aa98c66SWolfgang Denk 		 * On FAT32 we must fetch the FAT entries for the next
11332aa98c66SWolfgang Denk 		 * root directory clusters when a cluster has been
11342aa98c66SWolfgang Denk 		 * completely processed.
11352aa98c66SWolfgang Denk 		 */
113664f65e1eSPrzemyslaw Marczak 		++buffer_blk_cnt;
1137cd1b042cSBenoît Thébaudeau 		int rootdir_end = 0;
1138cd1b042cSBenoît Thébaudeau 		if (mydata->fatsize == 32) {
113964f65e1eSPrzemyslaw Marczak 			if (buffer_blk_cnt == mydata->clust_size) {
11403f270f42SErik Hansen 				int nxtsect = 0;
11413f270f42SErik Hansen 				int nxt_clust = 0;
11422aa98c66SWolfgang Denk 
11432aa98c66SWolfgang Denk 				nxt_clust = get_fatent(mydata, root_cluster);
1144cd1b042cSBenoît Thébaudeau 				rootdir_end = CHECK_CLUST(nxt_clust, 32);
11457385c28eSWolfgang Denk 
11467385c28eSWolfgang Denk 				nxtsect = mydata->data_begin +
11477385c28eSWolfgang Denk 					(nxt_clust * mydata->clust_size);
11487385c28eSWolfgang Denk 
11492aa98c66SWolfgang Denk 				root_cluster = nxt_clust;
11502aa98c66SWolfgang Denk 
11512aa98c66SWolfgang Denk 				cursect = nxtsect;
115264f65e1eSPrzemyslaw Marczak 				buffer_blk_cnt = 0;
1153cd1b042cSBenoît Thébaudeau 			}
11542aa98c66SWolfgang Denk 		} else {
115564f65e1eSPrzemyslaw Marczak 			if (buffer_blk_cnt == PREFETCH_BLOCKS)
115664f65e1eSPrzemyslaw Marczak 				buffer_blk_cnt = 0;
1157cd1b042cSBenoît Thébaudeau 
1158cd1b042cSBenoît Thébaudeau 			rootdir_end = (++cursect - mydata->rootdir_sect >=
1159cd1b042cSBenoît Thébaudeau 				       rootdir_size);
116071f95118Swdenk 		}
11613f270f42SErik Hansen 
11623f270f42SErik Hansen 		/* If end of rootdir reached */
1163cd1b042cSBenoît Thébaudeau 		if (rootdir_end) {
11643f270f42SErik Hansen 			if (dols == LS_ROOT) {
11653f270f42SErik Hansen 				printf("\n%d file(s), %d dir(s)\n\n",
11663f270f42SErik Hansen 				       files, dirs);
11671ad0b98aSSuriyan Ramasami 				*size = 0;
11683f270f42SErik Hansen 			}
1169ac497771SSergei Shtylyov 			goto exit;
11703f270f42SErik Hansen 		}
11712aa98c66SWolfgang Denk 	}
117271f95118Swdenk rootdir_done:
117371f95118Swdenk 
117471f95118Swdenk 	firsttime = 1;
11757385c28eSWolfgang Denk 
117671f95118Swdenk 	while (isdir) {
117771f95118Swdenk 		int startsect = mydata->data_begin
117871f95118Swdenk 			+ START(dentptr) * mydata->clust_size;
117971f95118Swdenk 		dir_entry dent;
118071f95118Swdenk 		char *nextname = NULL;
118171f95118Swdenk 
118271f95118Swdenk 		dent = *dentptr;
118371f95118Swdenk 		dentptr = &dent;
118471f95118Swdenk 
118571f95118Swdenk 		idx = dirdelim(subname);
11867385c28eSWolfgang Denk 
118771f95118Swdenk 		if (idx >= 0) {
118871f95118Swdenk 			subname[idx] = '\0';
118971f95118Swdenk 			nextname = subname + idx + 1;
119071f95118Swdenk 			/* Handle multiple delimiters */
119171f95118Swdenk 			while (ISDIRDELIM(*nextname))
119271f95118Swdenk 				nextname++;
119371f95118Swdenk 			if (dols && *nextname == '\0')
119471f95118Swdenk 				firsttime = 0;
119571f95118Swdenk 		} else {
119671f95118Swdenk 			if (dols && firsttime) {
119771f95118Swdenk 				firsttime = 0;
119871f95118Swdenk 			} else {
119971f95118Swdenk 				isdir = 0;
120071f95118Swdenk 			}
120171f95118Swdenk 		}
120271f95118Swdenk 
120371f95118Swdenk 		if (get_dentfromdir(mydata, startsect, subname, dentptr,
120471f95118Swdenk 				     isdir ? 0 : dols) == NULL) {
120571f95118Swdenk 			if (dols && !isdir)
12061ad0b98aSSuriyan Ramasami 				*size = 0;
1207ac497771SSergei Shtylyov 			goto exit;
120871f95118Swdenk 		}
120971f95118Swdenk 
12107ee46cebSBenoît Thébaudeau 		if (isdir && !(dentptr->attr & ATTR_DIR))
1211ac497771SSergei Shtylyov 			goto exit;
12127ee46cebSBenoît Thébaudeau 
121318a10d46SStephen Warren 		/*
121418a10d46SStephen Warren 		 * If we are looking for a directory, and found a directory
121518a10d46SStephen Warren 		 * type entry, and the entry is for the root directory (as
121618a10d46SStephen Warren 		 * denoted by a cluster number of 0), jump back to the start
121718a10d46SStephen Warren 		 * of the function, since at least on FAT12/16, the root dir
121818a10d46SStephen Warren 		 * lives in a hard-coded location and needs special handling
121918a10d46SStephen Warren 		 * to parse, rather than simply following the cluster linked
122018a10d46SStephen Warren 		 * list in the FAT, like other directories.
122118a10d46SStephen Warren 		 */
122218a10d46SStephen Warren 		if (isdir && (dentptr->attr & ATTR_DIR) && !START(dentptr)) {
122318a10d46SStephen Warren 			/*
122418a10d46SStephen Warren 			 * Modify the filename to remove the prefix that gets
122518a10d46SStephen Warren 			 * back to the root directory, so the initial root dir
122618a10d46SStephen Warren 			 * parsing code can continue from where we are without
122718a10d46SStephen Warren 			 * confusion.
122818a10d46SStephen Warren 			 */
122918a10d46SStephen Warren 			strcpy(fnamecopy, nextname ?: "");
123018a10d46SStephen Warren 			/*
123118a10d46SStephen Warren 			 * Set up state the same way as the function does when
123218a10d46SStephen Warren 			 * first started. This is required for the root dir
123318a10d46SStephen Warren 			 * parsing code operates in its expected environment.
123418a10d46SStephen Warren 			 */
123518a10d46SStephen Warren 			subname = "";
123618a10d46SStephen Warren 			cursect = mydata->rootdir_sect;
123718a10d46SStephen Warren 			isdir = 0;
123818a10d46SStephen Warren 			goto root_reparse;
123918a10d46SStephen Warren 		}
124018a10d46SStephen Warren 
12417ee46cebSBenoît Thébaudeau 		if (idx >= 0)
124271f95118Swdenk 			subname = nextname;
124371f95118Swdenk 	}
12447385c28eSWolfgang Denk 
12451ad0b98aSSuriyan Ramasami 	if (dogetsize) {
12461ad0b98aSSuriyan Ramasami 		*size = FAT2CPU32(dentptr->size);
12471ad0b98aSSuriyan Ramasami 		ret = 0;
12481ad0b98aSSuriyan Ramasami 	} else {
12491ad0b98aSSuriyan Ramasami 		ret = get_contents(mydata, dentptr, pos, buffer, maxsize, size);
12501ad0b98aSSuriyan Ramasami 	}
12511ad0b98aSSuriyan Ramasami 	debug("Size: %u, got: %llu\n", FAT2CPU32(dentptr->size), *size);
125271f95118Swdenk 
1253ac497771SSergei Shtylyov exit:
1254ac497771SSergei Shtylyov 	free(mydata->fatbuf);
125571f95118Swdenk 	return ret;
125671f95118Swdenk }
125771f95118Swdenk 
12581ad0b98aSSuriyan Ramasami int do_fat_read(const char *filename, void *buffer, loff_t maxsize, int dols,
12591ad0b98aSSuriyan Ramasami 		loff_t *actread)
12601170e634SBenoît Thébaudeau {
12611ad0b98aSSuriyan Ramasami 	return do_fat_read_at(filename, 0, buffer, maxsize, dols, 0, actread);
12621170e634SBenoît Thébaudeau }
12631170e634SBenoît Thébaudeau 
12647385c28eSWolfgang Denk int file_fat_detectfs(void)
126571f95118Swdenk {
126671f95118Swdenk 	boot_sector bs;
126771f95118Swdenk 	volume_info volinfo;
126871f95118Swdenk 	int fatsize;
12697205e407Swdenk 	char vol_label[12];
127071f95118Swdenk 
12717205e407Swdenk 	if (cur_dev == NULL) {
12727205e407Swdenk 		printf("No current device\n");
12737205e407Swdenk 		return 1;
12747205e407Swdenk 	}
12757385c28eSWolfgang Denk 
1276dd60d122SJon Loeliger #if defined(CONFIG_CMD_IDE) || \
12778c5170a7SSonic Zhang     defined(CONFIG_CMD_SATA) || \
1278c649e3c9SSimon Glass     defined(CONFIG_SCSI) || \
1279dd60d122SJon Loeliger     defined(CONFIG_CMD_USB) || \
128021f6f963SAndy Fleming     defined(CONFIG_MMC)
12817205e407Swdenk 	printf("Interface:  ");
12827205e407Swdenk 	switch (cur_dev->if_type) {
12837385c28eSWolfgang Denk 	case IF_TYPE_IDE:
12847385c28eSWolfgang Denk 		printf("IDE");
12857385c28eSWolfgang Denk 		break;
12867385c28eSWolfgang Denk 	case IF_TYPE_SATA:
12877385c28eSWolfgang Denk 		printf("SATA");
12887385c28eSWolfgang Denk 		break;
12897385c28eSWolfgang Denk 	case IF_TYPE_SCSI:
12907385c28eSWolfgang Denk 		printf("SCSI");
12917385c28eSWolfgang Denk 		break;
12927385c28eSWolfgang Denk 	case IF_TYPE_ATAPI:
12937385c28eSWolfgang Denk 		printf("ATAPI");
12947385c28eSWolfgang Denk 		break;
12957385c28eSWolfgang Denk 	case IF_TYPE_USB:
12967385c28eSWolfgang Denk 		printf("USB");
12977385c28eSWolfgang Denk 		break;
12987385c28eSWolfgang Denk 	case IF_TYPE_DOC:
12997385c28eSWolfgang Denk 		printf("DOC");
13007385c28eSWolfgang Denk 		break;
13017385c28eSWolfgang Denk 	case IF_TYPE_MMC:
13027385c28eSWolfgang Denk 		printf("MMC");
13037385c28eSWolfgang Denk 		break;
13047385c28eSWolfgang Denk 	default:
13057385c28eSWolfgang Denk 		printf("Unknown");
13067205e407Swdenk 	}
13077385c28eSWolfgang Denk 
1308bcce53d0SSimon Glass 	printf("\n  Device %d: ", cur_dev->devnum);
13097205e407Swdenk 	dev_print(cur_dev);
13107205e407Swdenk #endif
13117385c28eSWolfgang Denk 
13127205e407Swdenk 	if (read_bootsectandvi(&bs, &volinfo, &fatsize)) {
13137205e407Swdenk 		printf("\nNo valid FAT fs found\n");
13147205e407Swdenk 		return 1;
13157205e407Swdenk 	}
13167385c28eSWolfgang Denk 
13177205e407Swdenk 	memcpy(vol_label, volinfo.volume_label, 11);
13187205e407Swdenk 	vol_label[11] = '\0';
13197205e407Swdenk 	volinfo.fs_type[5] = '\0';
13207385c28eSWolfgang Denk 
1321461f86e6SStephen Warren 	printf("Filesystem: %s \"%s\"\n", volinfo.fs_type, vol_label);
13227385c28eSWolfgang Denk 
13237205e407Swdenk 	return 0;
132471f95118Swdenk }
132571f95118Swdenk 
13267385c28eSWolfgang Denk int file_fat_ls(const char *dir)
132771f95118Swdenk {
13281ad0b98aSSuriyan Ramasami 	loff_t size;
13291ad0b98aSSuriyan Ramasami 
13301ad0b98aSSuriyan Ramasami 	return do_fat_read(dir, NULL, 0, LS_YES, &size);
133171f95118Swdenk }
133271f95118Swdenk 
1333b7b5f319SStephen Warren int fat_exists(const char *filename)
1334b7b5f319SStephen Warren {
13351ad0b98aSSuriyan Ramasami 	int ret;
13361ad0b98aSSuriyan Ramasami 	loff_t size;
13371ad0b98aSSuriyan Ramasami 
13381ad0b98aSSuriyan Ramasami 	ret = do_fat_read_at(filename, 0, NULL, 0, LS_NO, 1, &size);
13391ad0b98aSSuriyan Ramasami 	return ret == 0;
1340b7b5f319SStephen Warren }
1341b7b5f319SStephen Warren 
1342d455d878SSuriyan Ramasami int fat_size(const char *filename, loff_t *size)
1343cf659819SStephen Warren {
1344d455d878SSuriyan Ramasami 	return do_fat_read_at(filename, 0, NULL, 0, LS_NO, 1, size);
1345cf659819SStephen Warren }
1346cf659819SStephen Warren 
13471ad0b98aSSuriyan Ramasami int file_fat_read_at(const char *filename, loff_t pos, void *buffer,
13481ad0b98aSSuriyan Ramasami 		     loff_t maxsize, loff_t *actread)
134971f95118Swdenk {
13507205e407Swdenk 	printf("reading %s\n", filename);
13511ad0b98aSSuriyan Ramasami 	return do_fat_read_at(filename, pos, buffer, maxsize, LS_NO, 0,
13521ad0b98aSSuriyan Ramasami 			      actread);
13531170e634SBenoît Thébaudeau }
13541170e634SBenoît Thébaudeau 
13551ad0b98aSSuriyan Ramasami int file_fat_read(const char *filename, void *buffer, int maxsize)
13561170e634SBenoît Thébaudeau {
13571ad0b98aSSuriyan Ramasami 	loff_t actread;
13581ad0b98aSSuriyan Ramasami 	int ret;
13591ad0b98aSSuriyan Ramasami 
13601ad0b98aSSuriyan Ramasami 	ret =  file_fat_read_at(filename, 0, buffer, maxsize, &actread);
13611ad0b98aSSuriyan Ramasami 	if (ret)
13621ad0b98aSSuriyan Ramasami 		return ret;
13631ad0b98aSSuriyan Ramasami 	else
13641ad0b98aSSuriyan Ramasami 		return actread;
136571f95118Swdenk }
1366e6d52415SSimon Glass 
1367d455d878SSuriyan Ramasami int fat_read_file(const char *filename, void *buf, loff_t offset, loff_t len,
1368d455d878SSuriyan Ramasami 		  loff_t *actread)
1369e6d52415SSimon Glass {
13701ad0b98aSSuriyan Ramasami 	int ret;
1371e6d52415SSimon Glass 
1372d455d878SSuriyan Ramasami 	ret = file_fat_read_at(filename, offset, buf, len, actread);
1373d455d878SSuriyan Ramasami 	if (ret)
1374e6d52415SSimon Glass 		printf("** Unable to read file %s **\n", filename);
1375e6d52415SSimon Glass 
1376d455d878SSuriyan Ramasami 	return ret;
1377e6d52415SSimon Glass }
1378e6d52415SSimon Glass 
1379e6d52415SSimon Glass void fat_close(void)
1380e6d52415SSimon Glass {
1381e6d52415SSimon Glass }
1382