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)
|
|
{
|
|
}
|