1047 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1047 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0+
 | |
| /*
 | |
|  * exfat.c
 | |
|  *
 | |
|  * exFAT filesystem implementation
 | |
|  */
 | |
| 
 | |
| #include <common.h>
 | |
| #include <blk.h> //new
 | |
| #include <config.h>
 | |
| #include <exports.h>
 | |
| #include <exfat.h>
 | |
| #include <fs.h>
 | |
| #include <asm/byteorder.h>
 | |
| #include <part.h>
 | |
| #include <malloc.h>
 | |
| #include <memalign.h> //new
 | |
| #include <linux/compiler.h>
 | |
| #include <linux/ctype.h>
 | |
| 
 | |
| /*
 | |
|  * Convert a string to lowercase.  Converts at most 'len' characters,
 | |
|  * 'len' may be larger than the length of 'str' if 'str' is NULL
 | |
|  * terminated.
 | |
|  */
 | |
| static void downcase(char *str, size_t len)
 | |
| {
 | |
| 	while (*str != '\0' && len--) {
 | |
| 		*str = tolower(*str);
 | |
| 		str++;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static struct blk_desc *cur_dev;
 | |
| static disk_partition_t cur_part_info;
 | |
| 
 | |
| #define DOS_BOOT_MAGIC_OFFSET	0x1fe
 | |
| #define DOS_FS_TYPE_OFFSET	0x36
 | |
| #define DOS_FS32_TYPE_OFFSET	0x52
 | |
| #define DOS_EXFAT_TYPE_OFFSET	0x3
 | |
| 
 | |
| static int disk_read(__u32 block, __u32 nr_blocks, void *buf)
 | |
| {
 | |
| 	ulong ret;
 | |
| 
 | |
| 	if (!cur_dev)
 | |
| 		return -1;
 | |
| 
 | |
| 	ret = blk_dread(cur_dev, cur_part_info.start + block, nr_blocks, buf);
 | |
| 
 | |
| 	if (ret != nr_blocks)
 | |
| 		return -1;
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| int exfat_set_blk_dev(struct blk_desc *dev_desc, disk_partition_t *info)
 | |
| {
 | |
| 	ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz);
 | |
| 
 | |
| 	cur_dev = dev_desc;
 | |
| 	cur_part_info = *info;
 | |
| 
 | |
| 	/* Make sure it has a valid FAT header */
 | |
| 	if (disk_read(0, 1, buffer) != 1) {
 | |
| 		cur_dev = NULL;
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	/* Check if it's actually a DOS volume */
 | |
| 	if (memcmp(buffer + DOS_BOOT_MAGIC_OFFSET, "\x55\xAA", 2)) {
 | |
| 		cur_dev = NULL;
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	/* Check for exFAT filesystem */
 | |
| 	if (!memcmp(buffer + DOS_EXFAT_TYPE_OFFSET, EXFAT_SIGN, 8))
 | |
| 		return 0;
 | |
| 
 | |
| 	cur_dev = NULL;
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| int exfat_register_device(struct blk_desc *dev_desc, int part_no)
 | |
| {
 | |
| 	disk_partition_t info;
 | |
| 
 | |
| 	/* First close any currently found FAT filesystem */
 | |
| 	cur_dev = NULL;
 | |
| 
 | |
| 	/* Read the partition table, if present */
 | |
| 	if (part_get_info(dev_desc, part_no, &info)) {
 | |
| 		if (part_no != 0) {
 | |
| 			printf("** Partition %d not valid on device %d **\n",
 | |
| 					part_no, dev_desc->devnum);
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		info.start = 0;
 | |
| 		info.size = dev_desc->lba;
 | |
| 		info.blksz = dev_desc->blksz;
 | |
| 		info.name[0] = 0;
 | |
| 		info.type[0] = 0;
 | |
| 		info.bootable = 0;
 | |
| #if CONFIG_IS_ENABLED(PARTITION_UUIDS)
 | |
| 		info.uuid[0] = 0;
 | |
| #endif
 | |
| 	}
 | |
| 
 | |
| 	return exfat_set_blk_dev(dev_desc, &info);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Get the first occurence of a directory delimiter ('/' or '\') in a string.
 | |
|  * Return index into string if found, -1 otherwise.
 | |
|  */
 | |
| static int dirdelim(char *str)
 | |
| {
 | |
| 	char *start = str;
 | |
| 
 | |
| 	while (*str != '\0') {
 | |
| 		if (ISDIRDELIM(*str))
 | |
| 			return str - start;
 | |
| 		str++;
 | |
| 	}
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| static int flush_dirty_exfat_buffer(exfat_fsdata *mydata);
 | |
| 
 | |
| #if !CONFIG_IS_ENABLED(EXFAT_WRITE)
 | |
| /* Stub for read only operation */
 | |
| int flush_dirty_exfat_buffer(exfat_fsdata *mydata)
 | |
| {
 | |
| 	(void)(mydata);
 | |
| 	return 0;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /*
 | |
|  * Get the entry at index 'entry' in a exFAT table.
 | |
|  * On failure 0x00 is returned.
 | |
|  */
 | |
| static __u32 exfat_get_fatent(exfat_fsdata *mydata, __u32 entry, exfat_stream_dentry *strmptr)
 | |
| {
 | |
| 	__u32 bufnum;
 | |
| 	__u32 offset;
 | |
| 	__u32 ret = 0x00;
 | |
| 
 | |
| 	// new
 | |
| 	if (CHECK_CLUST(entry, mydata->fatsize)) {
 | |
| 		printf("Error: Invalid FAT entry: 0x%08x\n", entry);
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	switch (mydata->fatsize) {
 | |
| 	case 32:
 | |
| 		bufnum = entry / EXFATBUFSIZE;
 | |
| 		offset = entry - bufnum * EXFATBUFSIZE;
 | |
| 		break;
 | |
| 	default:
 | |
| 		/* Unsupported FAT size */
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	debug("FAT%d: entry: 0x%08x = %d, offset: 0x%04x = %d\n",
 | |
| 	       mydata->fatsize, entry, entry, offset, offset);
 | |
| 
 | |
| 	if (strmptr && EXFAT_IS_CONTIGUOUS(strmptr)) {
 | |
| 		__u32 first_clus;
 | |
| 		__u32 clus_count;
 | |
| 		unsigned long filesize = (unsigned long)EXFAT_SIZE(strmptr); //can not use __u64 division, or compile will failed
 | |
| 																 //(limitation: file size should not exceed 4GB)
 | |
| 
 | |
| 		first_clus = EXFAT_SIZE(strmptr);
 | |
| 		clus_count = (filesize + mydata->sect_size * mydata->clust_size - 1) / (mydata->sect_size * mydata->clust_size);
 | |
| 
 | |
| 		if (entry < (first_clus +  clus_count - 1)) {
 | |
| 			// startClus can get next cluster , but should in Cluscount
 | |
| 			ret = entry + 1 ;
 | |
| 		} else {
 | |
| 			//DBG_IND(" Cluster out of range is FirstClus = 0X%X ,StartClus = 0x%X , ClusCount = 0x%X \r\n" ,FirstClus ,StartClus, ClusCount);
 | |
| 			ret = 0xFFFFFFFF;
 | |
| 		}
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	/* Read a new block of FAT entries into the cache. */
 | |
| 	if (bufnum != mydata->fatbufnum) {
 | |
| 		__u32 getsize = FATBUFBLOCKS;
 | |
| 		__u8 *bufptr = mydata->fatbuf;
 | |
| 		__u32 fatlength = mydata->fatlength;
 | |
| 		__u32 startblock = bufnum * FATBUFBLOCKS;
 | |
| 
 | |
| 		if (startblock + getsize > fatlength)
 | |
| 			getsize = fatlength - startblock;
 | |
| 
 | |
| 		startblock += mydata->fat_sect;	/* Offset from start of disk */
 | |
| 
 | |
| 		/* Write back the fatbuf to the disk */
 | |
| 		if (flush_dirty_exfat_buffer(mydata) < 0)
 | |
| 			return -1;
 | |
| 
 | |
| 		if (disk_read(startblock, getsize, bufptr) < 0) {
 | |
| 			debug("Error reading FAT blocks\n");
 | |
| 			return ret;
 | |
| 		}
 | |
| 		mydata->fatbufnum = bufnum;
 | |
| 	}
 | |
| 
 | |
| 	/* Get the actual entry from the table */
 | |
| 	switch (mydata->fatsize) {
 | |
| 	case 32:
 | |
| 		ret = FAT2CPU32(((__u32 *) mydata->fatbuf)[offset]);
 | |
| 		break;
 | |
| 	}
 | |
| 	debug("FAT%d: ret: %08x, entry: 0x%08x, offset: %04x\n",
 | |
| 	       mydata->fatsize, ret, entry, offset);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Read at most 'size' bytes from the specified cluster into 'buffer'.
 | |
|  * Return 0 on success, -1 otherwise.
 | |
|  */
 | |
| static int
 | |
| exfat_get_cluster(exfat_fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size)
 | |
| {
 | |
| 	__u32 idx = 0;
 | |
| 	__u32 startsect;
 | |
| 	int ret;
 | |
| 
 | |
| 	if (clustnum > 0) {
 | |
| 		/* startsect = mydata->data_begin +
 | |
| 				clustnum * mydata->clust_size; */
 | |
| 		startsect = exfat_clust_to_sect(mydata, clustnum);
 | |
| 	} else {
 | |
| 		startsect = mydata->rootdir_sect;
 | |
| 	}
 | |
| 
 | |
| 	debug("gc - clustnum: %d, startsect: %d\n", clustnum, startsect);
 | |
| 
 | |
| 	if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
 | |
| 		ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
 | |
| 
 | |
| 		debug("FAT: Misaligned buffer address (%p)\n", buffer);
 | |
| 
 | |
| 		while (size >= mydata->sect_size) {
 | |
| 			ret = disk_read(startsect++, 1, tmpbuf);
 | |
| 			if (ret != 1) {
 | |
| 				debug("Error reading data (got %d)\n", ret);
 | |
| 				return -1;
 | |
| 			}
 | |
| 
 | |
| 			memcpy(buffer, tmpbuf, mydata->sect_size);
 | |
| 			buffer += mydata->sect_size;
 | |
| 			size -= mydata->sect_size;
 | |
| 		}
 | |
| 	} else {
 | |
| 		idx = size / mydata->sect_size;
 | |
| 		ret = disk_read(startsect, idx, buffer);
 | |
| 		if (ret != idx) {
 | |
| 			debug("Error reading data (got %d)\n", ret);
 | |
| 			return -1;
 | |
| 		}
 | |
| 		startsect += idx;
 | |
| 		idx *= mydata->sect_size;
 | |
| 		buffer += idx;
 | |
| 		size -= idx;
 | |
| 	}
 | |
| 	if (size) {
 | |
| 		ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
 | |
| 
 | |
| 		ret = disk_read(startsect, 1, tmpbuf);
 | |
| 		if (ret != 1) {
 | |
| 			debug("Error reading data (got %d)\n", ret);
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		memcpy(buffer, tmpbuf, size);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Read at most 'maxsize' bytes from 'pos' in the file associated with 'dentptr'
 | |
|  * into 'buffer'.
 | |
|  * Update the number of bytes read in *gotsize or return -1 on fatal errors.
 | |
|  */
 | |
|  /*
 | |
| __u8 get_contents_vexfatname_block[EXFAT_MAX_CLUSTSIZE]
 | |
| 	__aligned(ARCH_DMA_MINALIGN);
 | |
|  */
 | |
| 
 | |
| static long
 | |
| exfat_get_contents(exfat_fsdata *mydata, exfat_stream_dentry *strmptr, loff_t pos,
 | |
| 	     __u8 *buffer, loff_t maxsize)
 | |
| {
 | |
| 	loff_t filesize = EXFAT_SIZE(strmptr), gotsize = 0;
 | |
| 	unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
 | |
| 	__u32 curclust = EXFAT_START(strmptr);
 | |
| 	__u32 endclust, newclust;
 | |
| 	loff_t actsize;
 | |
| 
 | |
| 	debug("Filesize: %llu bytes\n", filesize);
 | |
| 
 | |
| 	if (EXFAT_SIZE(strmptr) >= 0xFFFFFFFFULL) {
 | |
| 		debug("size too large\r\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	if (pos >= filesize) {
 | |
| 		debug("Read position past EOF: %llu\n", pos);
 | |
| 		return gotsize;
 | |
| 	}
 | |
| 
 | |
| 	if (maxsize > 0 && filesize > pos + maxsize)
 | |
| 		filesize = pos + maxsize;
 | |
| 
 | |
| 	debug("%llu bytes\n", filesize);
 | |
| 
 | |
| 	actsize = bytesperclust;
 | |
| 
 | |
| 	/* go to cluster at pos */
 | |
| 	while (actsize <= pos) {
 | |
| 		curclust = exfat_get_fatent(mydata, curclust, strmptr);
 | |
| 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
 | |
| 			debug("curclust: 0x%x\n", curclust);
 | |
| 			debug("Invalid FAT entry\n");
 | |
| 			return gotsize;
 | |
| 		}
 | |
| 		actsize += bytesperclust;
 | |
| 	}
 | |
| 
 | |
| 	/* actsize > pos */
 | |
| 	actsize -= bytesperclust;
 | |
| 	filesize -= actsize;
 | |
| 	pos -= actsize;
 | |
| 
 | |
| 	/* align to beginning of next cluster if any */
 | |
| 	if (pos) {
 | |
| 		__u8 *tmp_buffer;
 | |
| 
 | |
| 		actsize = min(filesize, (loff_t)bytesperclust);
 | |
| 		tmp_buffer = malloc_cache_aligned(actsize);
 | |
| 		if (!tmp_buffer) {
 | |
| 			debug("Error: allocating buffer\n");
 | |
| 			return -ENOMEM;
 | |
| 		}
 | |
| 
 | |
| 		if (exfat_get_cluster(mydata, curclust, tmp_buffer, actsize) != 0) {
 | |
| 			printf("Error reading cluster\n");
 | |
| 			free(tmp_buffer);
 | |
| 			return -1;
 | |
| 		}
 | |
| 		filesize -= actsize;
 | |
| 		actsize -= pos;
 | |
| 		memcpy(buffer, tmp_buffer + pos, actsize);
 | |
| 		free(tmp_buffer);
 | |
| 		gotsize += actsize;
 | |
| 		if (!filesize)
 | |
| 			return gotsize;
 | |
| 		buffer += actsize;
 | |
| 
 | |
| 		curclust = exfat_get_fatent(mydata, curclust, strmptr);
 | |
| 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
 | |
| 			debug("curclust: 0x%x\n", curclust);
 | |
| 			debug("Invalid FAT entry\n");
 | |
| 			return gotsize;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	actsize = bytesperclust;
 | |
| 	endclust = curclust;
 | |
| 
 | |
| 	do {
 | |
| 		/* search for consecutive clusters */
 | |
| 		while (actsize < filesize) {
 | |
| 			newclust = exfat_get_fatent(mydata, endclust, strmptr);
 | |
| 			if ((newclust - 1) != endclust)
 | |
| 				goto getit;
 | |
| 			if (CHECK_CLUST(newclust, mydata->fatsize)) {
 | |
| 				debug("curclust: 0x%x\n", newclust);
 | |
| 				debug("Invalid FAT entry\n");
 | |
| 				return gotsize;
 | |
| 			}
 | |
| 			endclust = newclust;
 | |
| 			actsize += bytesperclust;
 | |
| 		}
 | |
| 
 | |
| 		/* get remaining bytes */
 | |
| 		actsize = filesize;
 | |
| 		if (exfat_get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
 | |
| 			printf("Error reading cluster\n");
 | |
| 			return -1;
 | |
| 		}
 | |
| 		gotsize += actsize;
 | |
| 		return gotsize;
 | |
| getit:
 | |
| 		if (exfat_get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
 | |
| 			printf("Error reading cluster\n");
 | |
| 			return -1;
 | |
| 		}
 | |
| 		gotsize += (int)actsize;
 | |
| 		filesize -= actsize;
 | |
| 		buffer += actsize;
 | |
| 
 | |
| 		curclust = exfat_get_fatent(mydata, endclust, strmptr);
 | |
| 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
 | |
| 			debug("curclust: 0x%x\n", curclust);
 | |
| 			printf("Invalid FAT entry\n");
 | |
| 			return gotsize;
 | |
| 		}
 | |
| 		actsize = bytesperclust;
 | |
| 		endclust = curclust;
 | |
| 	} while (1);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Extract the file name information from 'slotptr' into 'l_name',
 | |
|  * starting at l_name[*idx].
 | |
|  * Return 1 if terminator (zero byte) is found, 0 otherwise.
 | |
|  */
 | |
| static int exfat_slot2str(exfat_name_dentry *slotptr, char *l_name, int *idx)
 | |
| {
 | |
| 	int i;
 | |
| 
 | |
| 	for (i = 0; i < EXFAT_NAME_ENTRY_MAXCHAR; i++) {
 | |
| 		if (slotptr->name[i] > 0x80)
 | |
| 			printf("NON-ASCII code 0x%X\r\n", slotptr->name[i]);
 | |
| 
 | |
| 		if (slotptr->entry_type != EXFAT_ENTRY_TYPE_NAME)
 | |
| 			return 0;
 | |
| 
 | |
| 		l_name[*idx] = (char)slotptr->name[i];
 | |
| 		if (l_name[*idx] == 0x00)
 | |
| 			return 1;
 | |
| 		(*idx)++;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| //Note: Only supports ASCII filename
 | |
| static int
 | |
| get_exfatname(exfat_fsdata *mydata, int curclust, __u8 *buf_head,
 | |
| 	     exfat_entry_set *curdent, char *l_name, exfat_stream_dentry *ret_strmptr)
 | |
| {
 | |
| 	exfat_name_dentry *name_deptr = &curdent->name_dentry[0];
 | |
| 	exfat_stream_dentry *strm_deptr = &curdent->stream_dentry;
 | |
| 	__u8 *buflimit = buf_head + mydata->sect_size * ((curclust == 0) ?
 | |
| 							EXFAT_PREFETCH_BLOCKS :
 | |
| 							mydata->clust_size);
 | |
| 	//__u8 counter = (curdent->stream_dentry.name_len + EXFAT_NAME_ENTRY_MAXCHAR - 1) / EXFAT_NAME_ENTRY_MAXCHAR;
 | |
| 	__u8 counter = (curdent->file_dentry.second_count - 1); //second_count excludes the stream entry
 | |
| 	int idx = 0;
 | |
| 
 | |
| 	if (counter > EXFAT_NAME_ENTRY_MAXNUM) {
 | |
| 		debug("Error: EXFAT name is too long\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	while (counter) {
 | |
| 		if ((__u8 *)name_deptr >= buflimit) {
 | |
| 			exfat_name_dentry *name_deptr2;
 | |
| 			__u8 *tmp_buffer;
 | |
| 
 | |
| 			if (curclust == 0)
 | |
| 				return -1;
 | |
| 			curclust = exfat_get_fatent(mydata, curclust, NULL);
 | |
| 			if (CHECK_CLUST(curclust, mydata->fatsize)) {
 | |
| 				debug("curclust: 0x%x\n", curclust);
 | |
| 				printf("Invalid FAT entry\n");
 | |
| 				return -1;
 | |
| 			}
 | |
| 
 | |
| 			tmp_buffer = malloc_cache_aligned(mydata->clust_size * mydata->sect_size);
 | |
| 			if (!tmp_buffer) {
 | |
| 				debug("Error: allocating buffer\n");
 | |
| 				return -ENOMEM;
 | |
| 			}
 | |
| 
 | |
| 			if (exfat_get_cluster(mydata, curclust, tmp_buffer,
 | |
| 					mydata->clust_size * mydata->sect_size) != 0) {
 | |
| 				debug("Error: reading directory block\n");
 | |
| 				return -1;
 | |
| 			}
 | |
| 
 | |
| 			name_deptr2 = (exfat_name_dentry *)(tmp_buffer + ((__u8 *)name_deptr - buflimit));
 | |
| 
 | |
| 			while (counter) {
 | |
| 				exfat_slot2str(name_deptr2, l_name, &idx);
 | |
| 				name_deptr2++;
 | |
| 				counter--;
 | |
| 			}
 | |
| 			free(tmp_buffer);
 | |
| 		} else {
 | |
| 			exfat_slot2str(name_deptr, l_name, &idx);
 | |
| 			name_deptr++;
 | |
| 			counter--;
 | |
| 		}
 | |
| 	}
 | |
| 	l_name[idx] = '\0';
 | |
| 	downcase(l_name, EXFAT_MAXLEN_BYTES);
 | |
| 
 | |
| 	if ((__u8 *)strm_deptr < buflimit) {
 | |
| 		memcpy(ret_strmptr, strm_deptr, sizeof(exfat_stream_dentry));
 | |
| 	} else {
 | |
| 		__u8 *tmp_buffer;
 | |
| 		tmp_buffer = malloc_cache_aligned(sizeof(exfat_stream_dentry));
 | |
| 		if (!tmp_buffer) {
 | |
| 			debug("Error: allocating buffer\n");
 | |
| 			return -ENOMEM;
 | |
| 		}
 | |
| 
 | |
| 		memcpy(ret_strmptr, tmp_buffer, sizeof(exfat_stream_dentry));
 | |
| 
 | |
| 		free(tmp_buffer);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Read boot sector and volume info from a FAT filesystem
 | |
|  */
 | |
| static int
 | |
| exfat_read_bootsectandvi(exfat_boot_sector *bs, exfat_volume_info *volinfo, int *fatsize)
 | |
| {
 | |
| 	__u8 *block;
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	if (cur_dev == NULL) {
 | |
| 		debug("Error: no device selected\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	/* block = memalign(ARCH_DMA_MINALIGN, cur_dev->blksz); */
 | |
| 	block = malloc_cache_aligned(cur_dev->blksz); //new
 | |
| 	if (block == NULL) {
 | |
| 		debug("Error: allocating block\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	if (disk_read(0, 1, block) < 0) {
 | |
| 		debug("Error: reading block\n");
 | |
| 		goto fail;
 | |
| 	}
 | |
| 
 | |
| 	//dump_buf(block, 512);
 | |
| 
 | |
| 	memcpy(bs, block, sizeof(exfat_boot_sector));
 | |
| 	bs->partition_offset[0] = FAT2CPU32(bs->partition_offset[0]);
 | |
| 	bs->partition_offset[1] = FAT2CPU32(bs->partition_offset[1]);
 | |
| 	bs->volume_length[0] = FAT2CPU32(bs->volume_length[0]);
 | |
| 	bs->volume_length[1] = FAT2CPU32(bs->volume_length[1]);
 | |
| 	bs->fat_offset = FAT2CPU32(bs->fat_offset);
 | |
| 	bs->fat_length = FAT2CPU32(bs->fat_length);
 | |
| 	bs->cluster_heap_offset = FAT2CPU32(bs->cluster_heap_offset);
 | |
| 	bs->cluster_count = FAT2CPU32(bs->cluster_count);
 | |
| 	bs->first_clus_of_root_dir = FAT2CPU32(bs->first_clus_of_root_dir);
 | |
| 	bs->volume_serial_number = FAT2CPU32(bs->volume_serial_number);
 | |
| 	bs->file_system_revision = FAT2CPU16(bs->file_system_revision);
 | |
| 	bs->volume_flags = FAT2CPU16(bs->volume_flags);
 | |
| 
 | |
| 	*fatsize = 32;
 | |
| 
 | |
| 	memcpy(volinfo->fs_type, bs->file_system_name, sizeof(bs->file_system_name));
 | |
| 
 | |
| 	if (strncmp(EXFAT_SIGN, bs->file_system_name, EXFAT_SIGNLEN) == 0)
 | |
| 		goto exit;
 | |
| 
 | |
| 	debug("Error: broken fs_type sign\n");
 | |
| fail:
 | |
| 	ret = -1;
 | |
| exit:
 | |
| 	free(block);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| __u8 do_exfat_read_at_block[EXFAT_MAX_CLUSTSIZE]
 | |
| 	__aligned(ARCH_DMA_MINALIGN);
 | |
| 
 | |
| long
 | |
| do_exfat_read_at(const char *filename, loff_t pos, void *buffer,
 | |
| 	       loff_t maxsize, int dols, int dogetsize)
 | |
| {
 | |
| 	char fnamecopy[2048];
 | |
| 	exfat_boot_sector bs;
 | |
| 	exfat_volume_info volinfo;
 | |
| 	exfat_fsdata datablock;
 | |
| 	exfat_fsdata *mydata = &datablock;
 | |
| 	//exfat_dir_entry *dentptr = NULL;
 | |
| 	exfat_entry_set *exdentptr = NULL;
 | |
| 	exfat_stream_dentry strmdent;
 | |
| 	//char *subname = "";
 | |
| 	__u32 cursect;
 | |
| 	int idx, isdir = 0;
 | |
| 	int files = 0, dirs = 0;
 | |
| 	long ret = -1;
 | |
| 	//int firsttime;
 | |
| 	__u32 root_cluster = 0;
 | |
| 	int j;
 | |
| 	int sect_per_clus;
 | |
| 
 | |
| 	if (exfat_read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) {
 | |
| 		debug("Error: reading boot sector\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	sect_per_clus = 1 << bs.sector_per_clus_shift;
 | |
| 	root_cluster = bs.first_clus_of_root_dir;
 | |
| 
 | |
| 	mydata->fatlength = bs.fat_length;
 | |
| 	mydata->fat_sect = bs.fat_offset;
 | |
| 
 | |
| 	mydata->fats = bs.number_of_fats;
 | |
| 
 | |
| 	cursect = mydata->rootdir_sect
 | |
| 		= bs.cluster_heap_offset + (bs.first_clus_of_root_dir - 2) * sect_per_clus;
 | |
| 
 | |
| 	mydata->sect_size = 1 << bs.byte_per_sector_shift;
 | |
| 	mydata->clust_size = sect_per_clus;
 | |
| 	if (mydata->sect_size != cur_part_info.blksz) {
 | |
| 		printf("Error: FAT sector size mismatch (fs=%hu, dev=%lu)\n",
 | |
| 				mydata->sect_size, cur_part_info.blksz);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	if (mydata->clust_size * mydata->sect_size > EXFAT_MAX_CLUSTSIZE) {
 | |
| 		printf("Error: clust_size(%d) > MAX(%d) bytes\n",
 | |
| 				mydata->clust_size * mydata->sect_size,	EXFAT_MAX_CLUSTSIZE);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	mydata->data_begin = mydata->rootdir_sect -	(mydata->clust_size * root_cluster); //relative to root cluster
 | |
| 
 | |
| 	mydata->fatbufnum = -1;
 | |
| 	mydata->fatbuf = memalign(ARCH_DMA_MINALIGN, FATBUFSIZE);
 | |
| 	if (mydata->fatbuf == NULL) {
 | |
| 		debug("Error: allocating memory\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	//if (vfat_enabled)
 | |
| 	//	debug("VFAT Support enabled\n");
 | |
| 
 | |
| 	debug("FAT%d, fat_sect: %d, fatlength: %d\n",
 | |
| 	       mydata->fatsize, mydata->fat_sect, mydata->fatlength);
 | |
| 	debug("Rootdir begins at cluster: %d, sector: %d, offset: %x\n"
 | |
| 	       "Data begins at: %d\n",
 | |
| 	       root_cluster,
 | |
| 	       mydata->rootdir_sect,
 | |
| 	       mydata->rootdir_sect * mydata->sect_size, mydata->data_begin);
 | |
| 	debug("Sector size: %d, cluster size: %d\n", mydata->sect_size,
 | |
| 	      mydata->clust_size);
 | |
| 
 | |
| 	/* "cwd" is always the root... */
 | |
| 	while (ISDIRDELIM(*filename))
 | |
| 		filename++;
 | |
| 
 | |
| 	/* Make a copy of the filename and convert it to lowercase */
 | |
| 	strcpy(fnamecopy, filename);
 | |
| 	downcase(fnamecopy, 2048);
 | |
| 
 | |
| 	if (*fnamecopy == '\0') {
 | |
| 		if (!dols)
 | |
| 			goto exit;
 | |
| 
 | |
| 		dols = LS_ROOT;
 | |
| 	} else if ((idx = dirdelim(fnamecopy)) >= 0) {
 | |
| 		#if 1
 | |
| 		printf("Error: Only supports root files\r\n");
 | |
| 		goto exit;
 | |
| 		#else
 | |
| 		isdir = 1;
 | |
| 		fnamecopy[idx] = '\0';
 | |
| 		subname = fnamecopy + idx + 1;
 | |
| 
 | |
| 		/* Handle multiple delimiters */
 | |
| 		while (ISDIRDELIM(*subname))
 | |
| 			subname++;
 | |
| 		#endif
 | |
| 	} else if (dols) {
 | |
| 		isdir = 1;
 | |
| 	}
 | |
| 
 | |
| 	j = 0;
 | |
| 	while (1) {
 | |
| 		int i;
 | |
| 
 | |
| 		if (j == 0) {
 | |
| 			debug("FAT read sect=%d, clust_size=%d, EXDIRENTSPERBLOCK=%zd\n",
 | |
| 				cursect, mydata->clust_size, EXFAT_DIRENTSPERBLOCK);
 | |
| 
 | |
| 			if (disk_read(cursect,
 | |
| 					mydata->clust_size,
 | |
| 					do_exfat_read_at_block) < 0) {
 | |
| 				debug("Error: reading rootdir block\n");
 | |
| 				goto exit;
 | |
| 			}
 | |
| 
 | |
| 			//dentptr = (exfat_dir_entry *) do_exfat_read_at_block;
 | |
| 			exdentptr = (exfat_entry_set *) do_exfat_read_at_block;
 | |
| 		}
 | |
| 
 | |
| 		for (i = 0; i < EXFAT_DIRENTSPERCLUST; i++) {
 | |
| 			char l_name[EXFAT_MAXLEN_BYTES];
 | |
| 
 | |
| 			l_name[0] = '\0';
 | |
| 			/*
 | |
| 			if (dentptr->name[0] == DELETED_FLAG) {
 | |
| 				dentptr++;
 | |
| 				continue;
 | |
| 			}
 | |
| 			*/
 | |
| 			if (exdentptr->file_dentry.entry_type == 0) { //search end
 | |
| 				debug("RootDentname == NULL - %d\n", i);
 | |
| 				if (dols == LS_ROOT) {
 | |
| 					printf("\n%d file(s), %d dir(s)\n\n",
 | |
| 						files, dirs);
 | |
| 					ret = 0;
 | |
| 				}
 | |
| 				goto exit;
 | |
| 			}
 | |
| 
 | |
| 			if (exdentptr->file_dentry.entry_type != EXFAT_ENTRY_TYPE_FILE) {
 | |
| 				exdentptr = (exfat_entry_set *)((char *)exdentptr + EXFAT_SIZE_PER_ENTRY);
 | |
| 				continue;
 | |
| 			}
 | |
| 
 | |
| 			//dump_buf(exdentptr, 512);
 | |
| 			get_exfatname(mydata,
 | |
| 						root_cluster,
 | |
| 						do_exfat_read_at_block,
 | |
| 						exdentptr, l_name, &strmdent);
 | |
| 			debug("Rootexfatname: |%s|\n", l_name);
 | |
| 
 | |
| 			if (dols == LS_ROOT) {
 | |
| 				char dirc;
 | |
| 				int doit = 0;
 | |
| 				int isdir =
 | |
| 					(exdentptr->file_dentry.attrib & ATTR_DIR);
 | |
| 
 | |
| 				if (isdir) {
 | |
| 					dirs++;
 | |
| 					dirc = '/';
 | |
| 					doit = 1;
 | |
| 				} else {
 | |
| 					dirc = ' ';
 | |
| 					if (l_name[0] != 0) {
 | |
| 						files++;
 | |
| 						doit = 1;
 | |
| 					}
 | |
| 				}
 | |
| 				if (doit) {
 | |
| 					if (dirc == ' ') {
 | |
| 						printf(" %8lld   %s%c\n",
 | |
| 							EXFAT_SIZE(&strmdent),
 | |
| 							l_name,
 | |
| 							dirc);
 | |
| 					} else {
 | |
| 						printf("            %s%c\n",
 | |
| 							l_name,
 | |
| 							dirc);
 | |
| 					}
 | |
| 				}
 | |
| 				exdentptr = (exfat_entry_set *)((char *)exdentptr + EXFAT_SIZE_PER_ENTRY);
 | |
| 				continue;
 | |
| 			} else { //not dols, should read a file
 | |
| 				if (strcmp(fnamecopy, l_name)) {
 | |
| 					debug("RootMismatch: %s|%s|\n", fnamecopy, l_name);
 | |
| 					exdentptr = (exfat_entry_set *)((char *)exdentptr + EXFAT_SIZE_PER_ENTRY);
 | |
| 					continue;
 | |
| 				}
 | |
| 
 | |
| 				if (isdir && !(exdentptr->file_dentry.attrib & ATTR_DIR))
 | |
| 					goto exit;
 | |
| 
 | |
| 				debug("RootName: %s", l_name);
 | |
| 				debug(", start: 0x%x", EXFAT_START(&strmdent));
 | |
| 				debug(", size:  0x%llx %s\n",
 | |
| 				       EXFAT_SIZE(&strmdent),
 | |
| 				       isdir ? "(DIR)" : "");
 | |
| 
 | |
| 				goto rootdir_done;	/* We got a match */
 | |
| 			}
 | |
| 		}
 | |
| 		debug("END LOOP: j=%d   clust_size=%d\n", j,
 | |
| 		       mydata->clust_size);
 | |
| 
 | |
| 		/*
 | |
| 		 * we must fetch the entries for the next
 | |
| 		 * root directory clusters when a cluster has been
 | |
| 		 * completely processed.
 | |
| 		 */
 | |
| 		j += mydata->clust_size;
 | |
| 		int rootdir_end = 0;
 | |
| 
 | |
| 		if (j == mydata->clust_size) {
 | |
| 			int nxtsect = 0;
 | |
| 			int nxt_clust = 0;
 | |
| 
 | |
| 			nxt_clust = exfat_get_fatent(mydata, root_cluster, NULL);
 | |
| 			rootdir_end = EXFAT_CHECK_CLUST(nxt_clust, 32);
 | |
| 
 | |
| 			nxtsect = mydata->data_begin +
 | |
| 				(nxt_clust * mydata->clust_size);
 | |
| 
 | |
| 			root_cluster = nxt_clust;
 | |
| 
 | |
| 			cursect = nxtsect;
 | |
| 			j = 0;
 | |
| 		}
 | |
| 
 | |
| 		/* If end of rootdir reached */
 | |
| 		if (rootdir_end) {
 | |
| 			if (dols == LS_ROOT) {
 | |
| 				printf("\n%d file(s), %d dir(s)\n\n",
 | |
| 				       files, dirs);
 | |
| 				ret = 0;
 | |
| 			}
 | |
| 			goto exit;
 | |
| 		}
 | |
| 	}
 | |
| rootdir_done:
 | |
| 
 | |
| #if 1
 | |
| 	if(dogetsize)
 | |
| 		ret = EXFAT_SIZE(&strmdent);
 | |
| 	else
 | |
| 		ret = exfat_get_contents(mydata, &strmdent, pos, buffer, maxsize);
 | |
| 
 | |
| 	debug("Size: %lld, got: %ld\n", EXFAT_SIZE(&strmdent), ret);
 | |
| 	//dump_buf(buffer, 512);
 | |
| #else
 | |
| 	firsttime = 1;
 | |
| 
 | |
| 	while (isdir) {
 | |
| 		int startsect = mydata->data_begin
 | |
| 			+ EXFAT_START(exdentptr) * mydata->clust_size;
 | |
| 		exfat_dir_entry dent;
 | |
| 		char *nextname = NULL;
 | |
| 
 | |
| 		dent = *dentptr;
 | |
| 		dentptr = &dent;
 | |
| 
 | |
| 		idx = dirdelim(subname);
 | |
| 
 | |
| 		if (idx >= 0) {
 | |
| 			subname[idx] = '\0';
 | |
| 			nextname = subname + idx + 1;
 | |
| 			/* Handle multiple delimiters */
 | |
| 			while (ISDIRDELIM(*nextname))
 | |
| 				nextname++;
 | |
| 			if (dols && *nextname == '\0')
 | |
| 				firsttime = 0;
 | |
| 		} else {
 | |
| 			if (dols && firsttime) {
 | |
| 				firsttime = 0;
 | |
| 			} else {
 | |
| 				isdir = 0;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (get_dentfromdir(mydata, startsect, subname, dentptr,
 | |
| 				     isdir ? 0 : dols) == NULL) {
 | |
| 			if (dols && !isdir)
 | |
| 				ret = 0;
 | |
| 			goto exit;
 | |
| 		}
 | |
| 
 | |
| 		if (isdir && !(dentptr->attr & ATTR_DIR))
 | |
| 			goto exit;
 | |
| 
 | |
| 		if (idx >= 0)
 | |
| 			subname = nextname;
 | |
| 	}
 | |
| 
 | |
| 	if (dogetsize)
 | |
| 		ret = FAT2CPU32(dentptr->size);
 | |
| 	else
 | |
| 		ret = get_contents(mydata, dentptr, pos, buffer, maxsize);
 | |
| 	debug("Size: %d, got: %ld\n", FAT2CPU32(dentptr->size), ret);
 | |
| #endif
 | |
| 
 | |
| exit:
 | |
| 	free(mydata->fatbuf);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| long
 | |
| do_exfat_read(const char *filename, void *buffer, loff_t maxsize, int dols)
 | |
| {
 | |
| 	return do_exfat_read_at(filename, 0, buffer, maxsize, dols, 0);
 | |
| }
 | |
| 
 | |
| int file_exfat_detectfs(void)
 | |
| {
 | |
| 	exfat_boot_sector bs;
 | |
| 	exfat_volume_info volinfo;
 | |
| 	int fatsize;
 | |
| 	char vol_label[12];
 | |
| 
 | |
| 	if (cur_dev == NULL) {
 | |
| 		printf("No current device\n");
 | |
| 		return 1;
 | |
| 	}
 | |
| 
 | |
| #if defined(CONFIG_IDE) || \
 | |
|     defined(CONFIG_SATA) || \
 | |
|     defined(CONFIG_SCSI) || \
 | |
|     defined(CONFIG_CMD_USB) || \
 | |
|     defined(CONFIG_MMC)
 | |
| 	printf("Interface:  ");
 | |
| 	switch (cur_dev->if_type) {
 | |
| 	case IF_TYPE_IDE:
 | |
| 		printf("IDE");
 | |
| 		break;
 | |
| 	case IF_TYPE_SATA:
 | |
| 		printf("SATA");
 | |
| 		break;
 | |
| 	case IF_TYPE_SCSI:
 | |
| 		printf("SCSI");
 | |
| 		break;
 | |
| 	case IF_TYPE_ATAPI:
 | |
| 		printf("ATAPI");
 | |
| 		break;
 | |
| 	case IF_TYPE_USB:
 | |
| 		printf("USB");
 | |
| 		break;
 | |
| 	case IF_TYPE_DOC:
 | |
| 		printf("DOC");
 | |
| 		break;
 | |
| 	case IF_TYPE_MMC:
 | |
| 		printf("MMC");
 | |
| 		break;
 | |
| 	default:
 | |
| 		printf("Unknown");
 | |
| 	}
 | |
| 
 | |
| 	printf("\n  Device %d: ", cur_dev->devnum);
 | |
| 	dev_print(cur_dev);
 | |
| #endif
 | |
| 
 | |
| 	if (exfat_read_bootsectandvi(&bs, &volinfo, &fatsize)) {
 | |
| 		printf("\nNo valid FAT fs found\n");
 | |
| 		return 1;
 | |
| 	}
 | |
| 
 | |
| 	memcpy(vol_label, volinfo.volume_label, 11);
 | |
| 	vol_label[11] = '\0';
 | |
| 	volinfo.fs_type[5] = '\0';
 | |
| 
 | |
| 	printf("Filesystem: %s \"%s\"\n", volinfo.fs_type, vol_label);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int exfat_ls(const char *dir)
 | |
| {
 | |
| 	return do_exfat_read(dir, NULL, 0, LS_YES);
 | |
| }
 | |
| 
 | |
| int exfat_exists(const char *filename)
 | |
| {
 | |
| 	long ret;
 | |
| 	ret = do_exfat_read_at(filename, 0, NULL, 0, LS_NO, 1);
 | |
| 	return ret >= 0;
 | |
| }
 | |
| 
 | |
| int exfat_size(const char *filename, loff_t *size)
 | |
| {
 | |
| 	long ret;
 | |
| 
 | |
| 	*size = 0;
 | |
| 	ret = do_exfat_read_at(filename, 0, NULL, 0, LS_NO, 1);
 | |
| 
 | |
| 	if (ret == -1) {
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	*size = (loff_t)ret;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int file_exfat_read_at(const char *filename, loff_t pos, void *buffer,
 | |
| 		     loff_t maxsize, loff_t *actread)
 | |
| {
 | |
| 	long ret = -1;
 | |
| 
 | |
| 	*actread = 0;
 | |
| 	ret = do_exfat_read_at(filename, pos, buffer, maxsize, LS_NO, 0);
 | |
| 
 | |
| 	if (ret == -1) {
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	*actread = (loff_t)ret;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int file_exfat_read(const char *filename, void *buffer, int maxsize)
 | |
| {
 | |
| 	loff_t actread;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret =  file_exfat_read_at(filename, 0, buffer, maxsize, &actread);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 	else
 | |
| 		return actread;
 | |
| }
 | |
| 
 | |
| int exfat_read_file(const char *filename, void *buf, loff_t offset, loff_t len,
 | |
| 		  loff_t *actread)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = file_exfat_read_at(filename, offset, buf, len, actread);
 | |
| 	if (ret)
 | |
| 		printf("** Unable to read file %s **\n", filename);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| int exfat_opendir(const char *filename, struct fs_dir_stream **dirsp)
 | |
| {
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int exfat_readdir(struct fs_dir_stream *dirs, struct fs_dirent **dentp)
 | |
| {
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| void exfat_closedir(struct fs_dir_stream *dirs)
 | |
| {
 | |
| }
 | |
| 
 | |
| void exfat_close(void)
 | |
| {
 | |
| }
 | 
