2003-06-15 22:40:42 +00:00
|
|
|
/*
|
|
|
|
* fat.c
|
|
|
|
*
|
|
|
|
* R/O (V)FAT 12/16/32 filesystem implementation by Marcus Sundberg
|
|
|
|
*
|
|
|
|
* 2002-07-28 - rjones@nexus-tech.net - ported to ppcboot v1.1.6
|
|
|
|
* 2003-03-10 - kharris@nexus-tech.net - ported to uboot
|
|
|
|
*
|
|
|
|
* See file CREDITS for list of people who contributed to this
|
|
|
|
* project.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License as
|
|
|
|
* published by the Free Software Foundation; either version 2 of
|
|
|
|
* the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
|
|
|
* MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <common.h>
|
|
|
|
#include <config.h>
|
2011-08-08 09:38:33 +00:00
|
|
|
#include <exports.h>
|
2003-06-15 22:40:42 +00:00
|
|
|
#include <fat.h>
|
|
|
|
#include <asm/byteorder.h>
|
2003-09-10 22:30:53 +00:00
|
|
|
#include <part.h>
|
2012-04-11 04:08:53 +00:00
|
|
|
#include <malloc.h>
|
|
|
|
#include <linux/compiler.h>
|
2003-06-15 22:40:42 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert a string to lowercase.
|
|
|
|
*/
|
2010-07-19 09:37:00 +00:00
|
|
|
static void downcase (char *str)
|
2003-06-15 22:40:42 +00:00
|
|
|
{
|
|
|
|
while (*str != '\0') {
|
|
|
|
TOLOWER(*str);
|
|
|
|
str++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-21 07:08:10 +00:00
|
|
|
static block_dev_desc_t *cur_dev;
|
|
|
|
static unsigned int cur_part_nr;
|
|
|
|
static disk_partition_t cur_part_info;
|
2010-07-19 09:37:00 +00:00
|
|
|
|
2011-12-21 07:08:10 +00:00
|
|
|
#define DOS_BOOT_MAGIC_OFFSET 0x1fe
|
2003-09-10 22:30:53 +00:00
|
|
|
#define DOS_FS_TYPE_OFFSET 0x36
|
2010-07-19 09:36:57 +00:00
|
|
|
#define DOS_FS32_TYPE_OFFSET 0x52
|
2003-06-15 22:40:42 +00:00
|
|
|
|
2011-12-21 07:08:10 +00:00
|
|
|
static int disk_read(__u32 block, __u32 nr_blocks, void *buf)
|
2003-06-15 22:40:42 +00:00
|
|
|
{
|
2011-12-21 07:08:10 +00:00
|
|
|
if (!cur_dev || !cur_dev->block_read)
|
2003-09-10 22:30:53 +00:00
|
|
|
return -1;
|
2010-07-19 09:37:00 +00:00
|
|
|
|
2011-12-21 07:08:10 +00:00
|
|
|
return cur_dev->block_read(cur_dev->dev,
|
|
|
|
cur_part_info.start + block, nr_blocks, buf);
|
2003-06-15 22:40:42 +00:00
|
|
|
}
|
|
|
|
|
2010-07-19 09:37:00 +00:00
|
|
|
int fat_register_device (block_dev_desc_t * dev_desc, int part_no)
|
2003-06-15 22:40:42 +00:00
|
|
|
{
|
2012-04-11 04:08:53 +00:00
|
|
|
ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz);
|
2003-09-10 22:30:53 +00:00
|
|
|
|
2011-12-21 07:08:10 +00:00
|
|
|
/* First close any currently found FAT filesystem */
|
|
|
|
cur_dev = NULL;
|
2010-07-19 09:37:00 +00:00
|
|
|
|
2007-07-09 22:56:50 +00:00
|
|
|
#if (defined(CONFIG_CMD_IDE) || \
|
2008-12-10 04:20:18 +00:00
|
|
|
defined(CONFIG_CMD_SATA) || \
|
2007-07-09 22:56:50 +00:00
|
|
|
defined(CONFIG_CMD_SCSI) || \
|
|
|
|
defined(CONFIG_CMD_USB) || \
|
2008-01-09 19:51:32 +00:00
|
|
|
defined(CONFIG_MMC) || \
|
|
|
|
defined(CONFIG_SYSTEMACE) )
|
2011-12-21 07:08:10 +00:00
|
|
|
|
|
|
|
/* Read the partition table, if present */
|
|
|
|
if (!get_partition_info(dev_desc, part_no, &cur_part_info)) {
|
|
|
|
cur_dev = dev_desc;
|
|
|
|
cur_part_nr = part_no;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Otherwise it might be a superfloppy (whole-disk FAT filesystem) */
|
|
|
|
if (!cur_dev) {
|
|
|
|
if (part_no != 1) {
|
2011-11-04 15:55:15 +00:00
|
|
|
printf("** Partition %d not valid on device %d **\n",
|
2011-12-21 07:08:10 +00:00
|
|
|
part_no, dev_desc->dev);
|
2011-11-04 15:55:15 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2011-12-21 07:08:10 +00:00
|
|
|
|
|
|
|
cur_dev = dev_desc;
|
|
|
|
cur_part_nr = 1;
|
|
|
|
cur_part_info.start = 0;
|
|
|
|
cur_part_info.size = dev_desc->lba;
|
|
|
|
cur_part_info.blksz = dev_desc->blksz;
|
|
|
|
memset(cur_part_info.name, 0, sizeof(cur_part_info.name));
|
|
|
|
memset(cur_part_info.type, 0, sizeof(cur_part_info.type));
|
2008-01-09 19:51:32 +00:00
|
|
|
}
|
2011-12-21 07:08:10 +00:00
|
|
|
|
|
|
|
/* Make sure it has a valid FAT header */
|
|
|
|
if (disk_read(0, 1, buffer) != 1) {
|
|
|
|
cur_dev = NULL;
|
|
|
|
return -1;
|
2007-08-07 14:02:13 +00:00
|
|
|
}
|
2011-12-21 07:08:10 +00:00
|
|
|
|
|
|
|
/* 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 FAT12/FAT16/FAT32 filesystem */
|
|
|
|
if (!memcmp(buffer + DOS_FS_TYPE_OFFSET, "FAT", 3))
|
|
|
|
return 0;
|
|
|
|
if (!memcmp(buffer + DOS_FS32_TYPE_OFFSET, "FAT32", 5))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cur_dev = NULL;
|
|
|
|
return -1;
|
2003-06-15 22:40:42 +00:00
|
|
|
}
|
|
|
|
|
2011-12-21 07:08:10 +00:00
|
|
|
|
2003-06-15 22:40:42 +00:00
|
|
|
/*
|
|
|
|
* Get the first occurence of a directory delimiter ('/' or '\') in a string.
|
|
|
|
* Return index into string if found, -1 otherwise.
|
|
|
|
*/
|
2010-07-19 09:37:00 +00:00
|
|
|
static int dirdelim (char *str)
|
2003-06-15 22:40:42 +00:00
|
|
|
{
|
|
|
|
char *start = str;
|
|
|
|
|
|
|
|
while (*str != '\0') {
|
2010-07-19 09:37:00 +00:00
|
|
|
if (ISDIRDELIM(*str))
|
|
|
|
return str - start;
|
2003-06-15 22:40:42 +00:00
|
|
|
str++;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Extract zero terminated short name from a directory entry.
|
|
|
|
*/
|
|
|
|
static void get_name (dir_entry *dirent, char *s_name)
|
|
|
|
{
|
|
|
|
char *ptr;
|
|
|
|
|
2010-07-19 09:37:00 +00:00
|
|
|
memcpy(s_name, dirent->name, 8);
|
2003-06-15 22:40:42 +00:00
|
|
|
s_name[8] = '\0';
|
|
|
|
ptr = s_name;
|
|
|
|
while (*ptr && *ptr != ' ')
|
|
|
|
ptr++;
|
|
|
|
if (dirent->ext[0] && dirent->ext[0] != ' ') {
|
|
|
|
*ptr = '.';
|
|
|
|
ptr++;
|
2010-07-19 09:37:00 +00:00
|
|
|
memcpy(ptr, dirent->ext, 3);
|
2003-06-15 22:40:42 +00:00
|
|
|
ptr[3] = '\0';
|
|
|
|
while (*ptr && *ptr != ' ')
|
|
|
|
ptr++;
|
|
|
|
}
|
|
|
|
*ptr = '\0';
|
|
|
|
if (*s_name == DELETED_FLAG)
|
|
|
|
*s_name = '\0';
|
|
|
|
else if (*s_name == aRING)
|
2008-11-27 21:30:27 +00:00
|
|
|
*s_name = DELETED_FLAG;
|
2010-07-19 09:37:00 +00:00
|
|
|
downcase(s_name);
|
2003-06-15 22:40:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the entry at index 'entry' in a FAT (12/16/32) table.
|
|
|
|
* On failure 0x00 is returned.
|
|
|
|
*/
|
2010-07-19 09:37:00 +00:00
|
|
|
static __u32 get_fatent (fsdata *mydata, __u32 entry)
|
2003-06-15 22:40:42 +00:00
|
|
|
{
|
|
|
|
__u32 bufnum;
|
2010-07-19 09:37:00 +00:00
|
|
|
__u32 off16, offset;
|
2003-06-15 22:40:42 +00:00
|
|
|
__u32 ret = 0x00;
|
2010-07-19 09:37:00 +00:00
|
|
|
__u16 val1, val2;
|
2003-06-15 22:40:42 +00:00
|
|
|
|
|
|
|
switch (mydata->fatsize) {
|
|
|
|
case 32:
|
|
|
|
bufnum = entry / FAT32BUFSIZE;
|
|
|
|
offset = entry - bufnum * FAT32BUFSIZE;
|
|
|
|
break;
|
|
|
|
case 16:
|
|
|
|
bufnum = entry / FAT16BUFSIZE;
|
|
|
|
offset = entry - bufnum * FAT16BUFSIZE;
|
|
|
|
break;
|
|
|
|
case 12:
|
|
|
|
bufnum = entry / FAT12BUFSIZE;
|
|
|
|
offset = entry - bufnum * FAT12BUFSIZE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* Unsupported FAT size */
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-07-19 09:37:00 +00:00
|
|
|
debug("FAT%d: entry: 0x%04x = %d, offset: 0x%04x = %d\n",
|
|
|
|
mydata->fatsize, entry, entry, offset, offset);
|
2010-07-19 09:36:58 +00:00
|
|
|
|
2003-06-15 22:40:42 +00:00
|
|
|
/* Read a new block of FAT entries into the cache. */
|
|
|
|
if (bufnum != mydata->fatbufnum) {
|
2011-08-08 09:39:29 +00:00
|
|
|
__u32 getsize = FATBUFBLOCKS;
|
2003-06-15 22:40:42 +00:00
|
|
|
__u8 *bufptr = mydata->fatbuf;
|
|
|
|
__u32 fatlength = mydata->fatlength;
|
|
|
|
__u32 startblock = bufnum * FATBUFBLOCKS;
|
|
|
|
|
2011-08-08 09:39:29 +00:00
|
|
|
if (getsize > fatlength)
|
|
|
|
getsize = fatlength;
|
|
|
|
|
2011-08-08 09:38:33 +00:00
|
|
|
fatlength *= mydata->sect_size; /* We want it in bytes now */
|
2003-06-15 22:40:42 +00:00
|
|
|
startblock += mydata->fat_sect; /* Offset from start of disk */
|
|
|
|
|
|
|
|
if (disk_read(startblock, getsize, bufptr) < 0) {
|
2010-07-19 09:37:00 +00:00
|
|
|
debug("Error reading FAT blocks\n");
|
2003-06-15 22:40:42 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
mydata->fatbufnum = bufnum;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the actual entry from the table */
|
|
|
|
switch (mydata->fatsize) {
|
|
|
|
case 32:
|
2010-07-19 09:37:00 +00:00
|
|
|
ret = FAT2CPU32(((__u32 *) mydata->fatbuf)[offset]);
|
2003-06-15 22:40:42 +00:00
|
|
|
break;
|
|
|
|
case 16:
|
2010-07-19 09:37:00 +00:00
|
|
|
ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[offset]);
|
2003-06-15 22:40:42 +00:00
|
|
|
break;
|
2010-07-19 09:37:00 +00:00
|
|
|
case 12:
|
|
|
|
off16 = (offset * 3) / 4;
|
2003-06-15 22:40:42 +00:00
|
|
|
|
|
|
|
switch (offset & 0x3) {
|
|
|
|
case 0:
|
2010-07-19 09:37:00 +00:00
|
|
|
ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[off16]);
|
2003-06-15 22:40:42 +00:00
|
|
|
ret &= 0xfff;
|
|
|
|
break;
|
|
|
|
case 1:
|
2010-07-19 09:37:00 +00:00
|
|
|
val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
|
2003-06-15 22:40:42 +00:00
|
|
|
val1 &= 0xf000;
|
2010-07-19 09:37:00 +00:00
|
|
|
val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]);
|
2003-06-15 22:40:42 +00:00
|
|
|
val2 &= 0x00ff;
|
|
|
|
ret = (val2 << 4) | (val1 >> 12);
|
|
|
|
break;
|
|
|
|
case 2:
|
2010-07-19 09:37:00 +00:00
|
|
|
val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
|
2003-06-15 22:40:42 +00:00
|
|
|
val1 &= 0xff00;
|
2010-07-19 09:37:00 +00:00
|
|
|
val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]);
|
2003-06-15 22:40:42 +00:00
|
|
|
val2 &= 0x000f;
|
|
|
|
ret = (val2 << 8) | (val1 >> 8);
|
|
|
|
break;
|
|
|
|
case 3:
|
2010-07-19 09:37:00 +00:00
|
|
|
ret = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
|
2003-06-15 22:40:42 +00:00
|
|
|
ret = (ret & 0xfff0) >> 4;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2010-07-19 09:37:00 +00:00
|
|
|
break;
|
2003-06-15 22:40:42 +00:00
|
|
|
}
|
2010-07-19 09:37:00 +00:00
|
|
|
debug("FAT%d: ret: %08x, offset: %04x\n",
|
|
|
|
mydata->fatsize, ret, offset);
|
2003-06-15 22:40:42 +00:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read at most 'size' bytes from the specified cluster into 'buffer'.
|
|
|
|
* Return 0 on success, -1 otherwise.
|
|
|
|
*/
|
|
|
|
static int
|
2010-07-19 09:37:00 +00:00
|
|
|
get_cluster (fsdata *mydata, __u32 clustnum, __u8 *buffer,
|
|
|
|
unsigned long size)
|
2003-06-15 22:40:42 +00:00
|
|
|
{
|
2011-03-24 09:15:37 +00:00
|
|
|
__u32 idx = 0;
|
2003-06-15 22:40:42 +00:00
|
|
|
__u32 startsect;
|
2011-12-20 07:41:13 +00:00
|
|
|
__u32 nr_sect;
|
|
|
|
int ret;
|
2003-06-15 22:40:42 +00:00
|
|
|
|
|
|
|
if (clustnum > 0) {
|
2010-07-19 09:37:00 +00:00
|
|
|
startsect = mydata->data_begin +
|
|
|
|
clustnum * mydata->clust_size;
|
2003-06-15 22:40:42 +00:00
|
|
|
} else {
|
|
|
|
startsect = mydata->rootdir_sect;
|
|
|
|
}
|
|
|
|
|
2010-07-19 09:37:00 +00:00
|
|
|
debug("gc - clustnum: %d, startsect: %d\n", clustnum, startsect);
|
|
|
|
|
2011-12-20 07:41:13 +00:00
|
|
|
nr_sect = size / mydata->sect_size;
|
|
|
|
ret = disk_read(startsect, nr_sect, buffer);
|
|
|
|
if (ret != nr_sect) {
|
|
|
|
debug("Error reading data (got %d)\n", ret);
|
2003-09-10 22:30:53 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2011-08-08 09:38:33 +00:00
|
|
|
if (size % mydata->sect_size) {
|
2012-04-11 04:08:53 +00:00
|
|
|
ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
|
2010-07-19 09:37:00 +00:00
|
|
|
|
2011-08-08 09:38:33 +00:00
|
|
|
idx = size / mydata->sect_size;
|
2011-12-20 07:41:13 +00:00
|
|
|
ret = disk_read(startsect + idx, 1, tmpbuf);
|
|
|
|
if (ret != 1) {
|
|
|
|
debug("Error reading data (got %d)\n", ret);
|
2003-09-10 22:30:53 +00:00
|
|
|
return -1;
|
2003-06-15 22:40:42 +00:00
|
|
|
}
|
2011-08-08 09:38:33 +00:00
|
|
|
buffer += idx * mydata->sect_size;
|
2003-09-10 22:30:53 +00:00
|
|
|
|
2011-08-08 09:38:33 +00:00
|
|
|
memcpy(buffer, tmpbuf, size % mydata->sect_size);
|
2003-09-10 22:30:53 +00:00
|
|
|
return 0;
|
2003-06-15 22:40:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read at most 'maxsize' bytes from the file associated with 'dentptr'
|
|
|
|
* into 'buffer'.
|
|
|
|
* Return the number of bytes read or -1 on fatal errors.
|
|
|
|
*/
|
|
|
|
static long
|
2010-07-19 09:37:00 +00:00
|
|
|
get_contents (fsdata *mydata, dir_entry *dentptr, __u8 *buffer,
|
|
|
|
unsigned long maxsize)
|
2003-06-15 22:40:42 +00:00
|
|
|
{
|
|
|
|
unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0;
|
2011-08-08 09:38:33 +00:00
|
|
|
unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
|
2003-06-15 22:40:42 +00:00
|
|
|
__u32 curclust = START(dentptr);
|
2003-09-10 22:30:53 +00:00
|
|
|
__u32 endclust, newclust;
|
|
|
|
unsigned long actsize;
|
2003-06-15 22:40:42 +00:00
|
|
|
|
2010-07-19 09:37:00 +00:00
|
|
|
debug("Filesize: %ld bytes\n", filesize);
|
2003-06-15 22:40:42 +00:00
|
|
|
|
2010-07-19 09:37:00 +00:00
|
|
|
if (maxsize > 0 && filesize > maxsize)
|
|
|
|
filesize = maxsize;
|
2003-06-15 22:40:42 +00:00
|
|
|
|
2010-07-19 09:37:00 +00:00
|
|
|
debug("%ld bytes\n", filesize);
|
|
|
|
|
|
|
|
actsize = bytesperclust;
|
|
|
|
endclust = curclust;
|
2003-06-15 22:40:42 +00:00
|
|
|
|
|
|
|
do {
|
2003-09-10 22:30:53 +00:00
|
|
|
/* search for consecutive clusters */
|
2010-07-19 09:37:00 +00:00
|
|
|
while (actsize < filesize) {
|
2003-09-10 22:30:53 +00:00
|
|
|
newclust = get_fatent(mydata, endclust);
|
2010-07-19 09:37:00 +00:00
|
|
|
if ((newclust - 1) != endclust)
|
2003-09-10 22:30:53 +00:00
|
|
|
goto getit;
|
2008-03-02 22:33:46 +00:00
|
|
|
if (CHECK_CLUST(newclust, mydata->fatsize)) {
|
2010-07-19 09:37:00 +00:00
|
|
|
debug("curclust: 0x%x\n", newclust);
|
|
|
|
debug("Invalid FAT entry\n");
|
2003-09-10 22:30:53 +00:00
|
|
|
return gotsize;
|
|
|
|
}
|
2010-07-19 09:37:00 +00:00
|
|
|
endclust = newclust;
|
|
|
|
actsize += bytesperclust;
|
2003-09-10 22:30:53 +00:00
|
|
|
}
|
2010-07-19 09:37:00 +00:00
|
|
|
|
2003-09-10 22:30:53 +00:00
|
|
|
/* actsize >= file size */
|
|
|
|
actsize -= bytesperclust;
|
2010-07-19 09:37:00 +00:00
|
|
|
|
2003-09-10 22:30:53 +00:00
|
|
|
/* get remaining clusters */
|
|
|
|
if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
|
2010-07-19 09:37:00 +00:00
|
|
|
printf("Error reading cluster\n");
|
2003-06-15 22:40:42 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2010-07-19 09:37:00 +00:00
|
|
|
|
2003-09-10 22:30:53 +00:00
|
|
|
/* get remaining bytes */
|
|
|
|
gotsize += (int)actsize;
|
|
|
|
filesize -= actsize;
|
|
|
|
buffer += actsize;
|
2010-07-19 09:37:00 +00:00
|
|
|
actsize = filesize;
|
2003-09-10 22:30:53 +00:00
|
|
|
if (get_cluster(mydata, endclust, buffer, (int)actsize) != 0) {
|
2010-07-19 09:37:00 +00:00
|
|
|
printf("Error reading cluster\n");
|
2003-09-10 22:30:53 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2010-07-19 09:37:00 +00:00
|
|
|
gotsize += actsize;
|
2003-09-10 22:30:53 +00:00
|
|
|
return gotsize;
|
|
|
|
getit:
|
|
|
|
if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
|
2010-07-19 09:37:00 +00:00
|
|
|
printf("Error reading cluster\n");
|
2003-09-10 22:30:53 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
gotsize += (int)actsize;
|
|
|
|
filesize -= actsize;
|
|
|
|
buffer += actsize;
|
2010-07-19 09:37:00 +00:00
|
|
|
|
2003-09-10 22:30:53 +00:00
|
|
|
curclust = get_fatent(mydata, endclust);
|
2008-03-02 22:33:46 +00:00
|
|
|
if (CHECK_CLUST(curclust, mydata->fatsize)) {
|
2010-07-19 09:37:00 +00:00
|
|
|
debug("curclust: 0x%x\n", curclust);
|
|
|
|
printf("Invalid FAT entry\n");
|
2003-06-15 22:40:42 +00:00
|
|
|
return gotsize;
|
|
|
|
}
|
2010-07-19 09:37:00 +00:00
|
|
|
actsize = bytesperclust;
|
|
|
|
endclust = curclust;
|
2003-06-15 22:40:42 +00:00
|
|
|
} while (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_SUPPORT_VFAT
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2010-07-19 09:37:00 +00:00
|
|
|
static int slot2str (dir_slot *slotptr, char *l_name, int *idx)
|
2003-06-15 22:40:42 +00:00
|
|
|
{
|
|
|
|
int j;
|
|
|
|
|
|
|
|
for (j = 0; j <= 8; j += 2) {
|
|
|
|
l_name[*idx] = slotptr->name0_4[j];
|
2010-07-19 09:37:00 +00:00
|
|
|
if (l_name[*idx] == 0x00)
|
|
|
|
return 1;
|
2003-06-15 22:40:42 +00:00
|
|
|
(*idx)++;
|
|
|
|
}
|
|
|
|
for (j = 0; j <= 10; j += 2) {
|
|
|
|
l_name[*idx] = slotptr->name5_10[j];
|
2010-07-19 09:37:00 +00:00
|
|
|
if (l_name[*idx] == 0x00)
|
|
|
|
return 1;
|
2003-06-15 22:40:42 +00:00
|
|
|
(*idx)++;
|
|
|
|
}
|
|
|
|
for (j = 0; j <= 2; j += 2) {
|
|
|
|
l_name[*idx] = slotptr->name11_12[j];
|
2010-07-19 09:37:00 +00:00
|
|
|
if (l_name[*idx] == 0x00)
|
|
|
|
return 1;
|
2003-06-15 22:40:42 +00:00
|
|
|
(*idx)++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Extract the full long filename starting at 'retdent' (which is really
|
|
|
|
* a slot) into 'l_name'. If successful also copy the real directory entry
|
|
|
|
* into 'retdent'
|
|
|
|
* Return 0 on success, -1 otherwise.
|
|
|
|
*/
|
2012-04-11 04:08:53 +00:00
|
|
|
__u8 get_vfatname_block[MAX_CLUSTSIZE]
|
|
|
|
__aligned(ARCH_DMA_MINALIGN);
|
2010-07-19 09:37:00 +00:00
|
|
|
|
2003-06-15 22:40:42 +00:00
|
|
|
static int
|
2010-07-19 09:37:00 +00:00
|
|
|
get_vfatname (fsdata *mydata, int curclust, __u8 *cluster,
|
|
|
|
dir_entry *retdent, char *l_name)
|
2003-06-15 22:40:42 +00:00
|
|
|
{
|
|
|
|
dir_entry *realdent;
|
2010-07-19 09:37:00 +00:00
|
|
|
dir_slot *slotptr = (dir_slot *)retdent;
|
2011-08-19 09:37:46 +00:00
|
|
|
__u8 *buflimit = cluster + mydata->sect_size * ((curclust == 0) ?
|
|
|
|
PREFETCH_BLOCKS :
|
|
|
|
mydata->clust_size);
|
2010-07-19 09:37:00 +00:00
|
|
|
__u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff;
|
2003-06-15 22:40:42 +00:00
|
|
|
int idx = 0;
|
|
|
|
|
VFAT: fix processing of scattered long file name entries
The U-Boot code has the following bugs related to the processing of Long File
Name (LFN) entries scattered across several clusters/sectors :
1) get_vfatname() function is designed to gather scattered LFN entries by
cluster chain processing - that doesn't work for FAT12/16 root directory.
In other words, the function expects the following input data:
1.1) FAT32 directory (which is cluster chain based);
OR
1.2) FAT12/16 non-root directory (which is also cluster chain based);
OR
1.3) FAT12/16 root directory (allocated as contiguous sectors area), but
all necessary information MUST be within the input buffer of filesystem cluster
size (thus cluster-chain jump is never initiated).
In order to accomplish the last condition, root directory parsing code in
do_fat_read() uses the following trick: read-out cluster-size block, process
only first sector (512 bytes), then shift 512 forward, read-out cluster-size
block and so on. This works great unless cluster size is equal to 512 bytes
(in a case you have a small partition), or long file name entries are scattered
across three sectors, see 4) for details.
2) Despite of the fact that get_vfatname() supports FAT32 root directory
browsing, do_fat_read() function doesn't send current cluster number correctly,
so root directory look-up doesn't work correctly.
3) get_vfatname() doesn't gather scattered entries correctly also is the case
when all LFN entries are located at the end of the source cluster, but real
directory entry (which must be returned) is at the only beginning of the
next one. No error detected, the resulting directory entry returned contains
a semi-random information (wrong size, wrong start cluster number and so on)
i.e. the entry is not accessible.
4) LFN (VFAT) allows up to 20 entries (slots) each containing 26 bytes (13
UTF-16 code units) to represent a single long file name i.e. up to 520 bytes.
U-Boot allocates 256 bytes buffer instead, i.e. 10 or more LFN slots record
may cause buffer overflow / memory corruption.
Also, it's worth to mention that 20+1 slots occupy 672 bytes space which may
take more than one cluster of 512 bytes (medium-size FAT32 or small FAT16
partition) - get_vfatname() function doesn't support such case as well.
The patch attached fixes these problems in the following way:
- keep using 256 bytes buffer for a long file name, but safely prevent a
possible buffer overflow (skip LFN processing, if it contains 10 or more
slots).
- explicitly specify FAT12/16 root directory parsing buffer size, instead
of relying on cluster size. The value used is a double sector size (to store
current sector and the next one). This fixes the first problem and increases
performance on big FAT12/16 partitions;
- send current cluster number (FAT32) to get_vfatname() during root
directory processing;
- use LFN counter to seek the real directory entry in get_vfatname() - fixes the
third problem;
- skip deleted entries in the root directory (to prevent bogus buffer
overflow detection and LFN counter steps).
Note: it's not advised to split up the patch, because a separate part may
operate incorrectly.
Signed-off-by: Mikhail Zolotaryov <lebon@lebon.org.ua>
2010-09-08 14:06:03 +00:00
|
|
|
if (counter > VFAT_MAXSEQ) {
|
|
|
|
debug("Error: VFAT name is too long\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((__u8 *)slotptr < buflimit) {
|
2010-07-19 09:37:00 +00:00
|
|
|
if (counter == 0)
|
|
|
|
break;
|
* Patch by Thomas Elste, 10 Feb 2004:
Add support for NET+50 CPU and ModNET50 board
* Patch by Sam Song, 10 Feb 2004:
Fix typos in cfi_flash.c
* Patch by Leon Kukovec, 10 Feb 2004
Fixed long dir entry slot id calculation in get_vfatname
* Patch by Robin Gilks, 10 Feb 2004:
add "itest" command (operators: -eq, -ne, -lt, -gt, -le, -ge, ==,
!=, <>, <, >, <=, >=)
2004-02-23 19:30:57 +00:00
|
|
|
if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter)
|
|
|
|
return -1;
|
2003-06-15 22:40:42 +00:00
|
|
|
slotptr++;
|
|
|
|
counter--;
|
|
|
|
}
|
|
|
|
|
VFAT: fix processing of scattered long file name entries
The U-Boot code has the following bugs related to the processing of Long File
Name (LFN) entries scattered across several clusters/sectors :
1) get_vfatname() function is designed to gather scattered LFN entries by
cluster chain processing - that doesn't work for FAT12/16 root directory.
In other words, the function expects the following input data:
1.1) FAT32 directory (which is cluster chain based);
OR
1.2) FAT12/16 non-root directory (which is also cluster chain based);
OR
1.3) FAT12/16 root directory (allocated as contiguous sectors area), but
all necessary information MUST be within the input buffer of filesystem cluster
size (thus cluster-chain jump is never initiated).
In order to accomplish the last condition, root directory parsing code in
do_fat_read() uses the following trick: read-out cluster-size block, process
only first sector (512 bytes), then shift 512 forward, read-out cluster-size
block and so on. This works great unless cluster size is equal to 512 bytes
(in a case you have a small partition), or long file name entries are scattered
across three sectors, see 4) for details.
2) Despite of the fact that get_vfatname() supports FAT32 root directory
browsing, do_fat_read() function doesn't send current cluster number correctly,
so root directory look-up doesn't work correctly.
3) get_vfatname() doesn't gather scattered entries correctly also is the case
when all LFN entries are located at the end of the source cluster, but real
directory entry (which must be returned) is at the only beginning of the
next one. No error detected, the resulting directory entry returned contains
a semi-random information (wrong size, wrong start cluster number and so on)
i.e. the entry is not accessible.
4) LFN (VFAT) allows up to 20 entries (slots) each containing 26 bytes (13
UTF-16 code units) to represent a single long file name i.e. up to 520 bytes.
U-Boot allocates 256 bytes buffer instead, i.e. 10 or more LFN slots record
may cause buffer overflow / memory corruption.
Also, it's worth to mention that 20+1 slots occupy 672 bytes space which may
take more than one cluster of 512 bytes (medium-size FAT32 or small FAT16
partition) - get_vfatname() function doesn't support such case as well.
The patch attached fixes these problems in the following way:
- keep using 256 bytes buffer for a long file name, but safely prevent a
possible buffer overflow (skip LFN processing, if it contains 10 or more
slots).
- explicitly specify FAT12/16 root directory parsing buffer size, instead
of relying on cluster size. The value used is a double sector size (to store
current sector and the next one). This fixes the first problem and increases
performance on big FAT12/16 partitions;
- send current cluster number (FAT32) to get_vfatname() during root
directory processing;
- use LFN counter to seek the real directory entry in get_vfatname() - fixes the
third problem;
- skip deleted entries in the root directory (to prevent bogus buffer
overflow detection and LFN counter steps).
Note: it's not advised to split up the patch, because a separate part may
operate incorrectly.
Signed-off-by: Mikhail Zolotaryov <lebon@lebon.org.ua>
2010-09-08 14:06:03 +00:00
|
|
|
if ((__u8 *)slotptr >= buflimit) {
|
2003-06-15 22:40:42 +00:00
|
|
|
dir_slot *slotptr2;
|
|
|
|
|
VFAT: fix processing of scattered long file name entries
The U-Boot code has the following bugs related to the processing of Long File
Name (LFN) entries scattered across several clusters/sectors :
1) get_vfatname() function is designed to gather scattered LFN entries by
cluster chain processing - that doesn't work for FAT12/16 root directory.
In other words, the function expects the following input data:
1.1) FAT32 directory (which is cluster chain based);
OR
1.2) FAT12/16 non-root directory (which is also cluster chain based);
OR
1.3) FAT12/16 root directory (allocated as contiguous sectors area), but
all necessary information MUST be within the input buffer of filesystem cluster
size (thus cluster-chain jump is never initiated).
In order to accomplish the last condition, root directory parsing code in
do_fat_read() uses the following trick: read-out cluster-size block, process
only first sector (512 bytes), then shift 512 forward, read-out cluster-size
block and so on. This works great unless cluster size is equal to 512 bytes
(in a case you have a small partition), or long file name entries are scattered
across three sectors, see 4) for details.
2) Despite of the fact that get_vfatname() supports FAT32 root directory
browsing, do_fat_read() function doesn't send current cluster number correctly,
so root directory look-up doesn't work correctly.
3) get_vfatname() doesn't gather scattered entries correctly also is the case
when all LFN entries are located at the end of the source cluster, but real
directory entry (which must be returned) is at the only beginning of the
next one. No error detected, the resulting directory entry returned contains
a semi-random information (wrong size, wrong start cluster number and so on)
i.e. the entry is not accessible.
4) LFN (VFAT) allows up to 20 entries (slots) each containing 26 bytes (13
UTF-16 code units) to represent a single long file name i.e. up to 520 bytes.
U-Boot allocates 256 bytes buffer instead, i.e. 10 or more LFN slots record
may cause buffer overflow / memory corruption.
Also, it's worth to mention that 20+1 slots occupy 672 bytes space which may
take more than one cluster of 512 bytes (medium-size FAT32 or small FAT16
partition) - get_vfatname() function doesn't support such case as well.
The patch attached fixes these problems in the following way:
- keep using 256 bytes buffer for a long file name, but safely prevent a
possible buffer overflow (skip LFN processing, if it contains 10 or more
slots).
- explicitly specify FAT12/16 root directory parsing buffer size, instead
of relying on cluster size. The value used is a double sector size (to store
current sector and the next one). This fixes the first problem and increases
performance on big FAT12/16 partitions;
- send current cluster number (FAT32) to get_vfatname() during root
directory processing;
- use LFN counter to seek the real directory entry in get_vfatname() - fixes the
third problem;
- skip deleted entries in the root directory (to prevent bogus buffer
overflow detection and LFN counter steps).
Note: it's not advised to split up the patch, because a separate part may
operate incorrectly.
Signed-off-by: Mikhail Zolotaryov <lebon@lebon.org.ua>
2010-09-08 14:06:03 +00:00
|
|
|
if (curclust == 0)
|
|
|
|
return -1;
|
2003-06-15 22:40:42 +00:00
|
|
|
curclust = get_fatent(mydata, curclust);
|
2008-03-02 22:33:46 +00:00
|
|
|
if (CHECK_CLUST(curclust, mydata->fatsize)) {
|
2010-07-19 09:37:00 +00:00
|
|
|
debug("curclust: 0x%x\n", curclust);
|
|
|
|
printf("Invalid FAT entry\n");
|
2003-06-15 22:40:42 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2010-07-19 09:37:00 +00:00
|
|
|
|
2003-10-29 23:18:55 +00:00
|
|
|
if (get_cluster(mydata, curclust, get_vfatname_block,
|
2011-08-08 09:38:33 +00:00
|
|
|
mydata->clust_size * mydata->sect_size) != 0) {
|
2010-07-19 09:37:00 +00:00
|
|
|
debug("Error: reading directory block\n");
|
2003-06-15 22:40:42 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2010-07-19 09:37:00 +00:00
|
|
|
|
|
|
|
slotptr2 = (dir_slot *)get_vfatname_block;
|
VFAT: fix processing of scattered long file name entries
The U-Boot code has the following bugs related to the processing of Long File
Name (LFN) entries scattered across several clusters/sectors :
1) get_vfatname() function is designed to gather scattered LFN entries by
cluster chain processing - that doesn't work for FAT12/16 root directory.
In other words, the function expects the following input data:
1.1) FAT32 directory (which is cluster chain based);
OR
1.2) FAT12/16 non-root directory (which is also cluster chain based);
OR
1.3) FAT12/16 root directory (allocated as contiguous sectors area), but
all necessary information MUST be within the input buffer of filesystem cluster
size (thus cluster-chain jump is never initiated).
In order to accomplish the last condition, root directory parsing code in
do_fat_read() uses the following trick: read-out cluster-size block, process
only first sector (512 bytes), then shift 512 forward, read-out cluster-size
block and so on. This works great unless cluster size is equal to 512 bytes
(in a case you have a small partition), or long file name entries are scattered
across three sectors, see 4) for details.
2) Despite of the fact that get_vfatname() supports FAT32 root directory
browsing, do_fat_read() function doesn't send current cluster number correctly,
so root directory look-up doesn't work correctly.
3) get_vfatname() doesn't gather scattered entries correctly also is the case
when all LFN entries are located at the end of the source cluster, but real
directory entry (which must be returned) is at the only beginning of the
next one. No error detected, the resulting directory entry returned contains
a semi-random information (wrong size, wrong start cluster number and so on)
i.e. the entry is not accessible.
4) LFN (VFAT) allows up to 20 entries (slots) each containing 26 bytes (13
UTF-16 code units) to represent a single long file name i.e. up to 520 bytes.
U-Boot allocates 256 bytes buffer instead, i.e. 10 or more LFN slots record
may cause buffer overflow / memory corruption.
Also, it's worth to mention that 20+1 slots occupy 672 bytes space which may
take more than one cluster of 512 bytes (medium-size FAT32 or small FAT16
partition) - get_vfatname() function doesn't support such case as well.
The patch attached fixes these problems in the following way:
- keep using 256 bytes buffer for a long file name, but safely prevent a
possible buffer overflow (skip LFN processing, if it contains 10 or more
slots).
- explicitly specify FAT12/16 root directory parsing buffer size, instead
of relying on cluster size. The value used is a double sector size (to store
current sector and the next one). This fixes the first problem and increases
performance on big FAT12/16 partitions;
- send current cluster number (FAT32) to get_vfatname() during root
directory processing;
- use LFN counter to seek the real directory entry in get_vfatname() - fixes the
third problem;
- skip deleted entries in the root directory (to prevent bogus buffer
overflow detection and LFN counter steps).
Note: it's not advised to split up the patch, because a separate part may
operate incorrectly.
Signed-off-by: Mikhail Zolotaryov <lebon@lebon.org.ua>
2010-09-08 14:06:03 +00:00
|
|
|
while (counter > 0) {
|
|
|
|
if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK)
|
|
|
|
& 0xff) != counter)
|
|
|
|
return -1;
|
2003-06-15 22:40:42 +00:00
|
|
|
slotptr2++;
|
VFAT: fix processing of scattered long file name entries
The U-Boot code has the following bugs related to the processing of Long File
Name (LFN) entries scattered across several clusters/sectors :
1) get_vfatname() function is designed to gather scattered LFN entries by
cluster chain processing - that doesn't work for FAT12/16 root directory.
In other words, the function expects the following input data:
1.1) FAT32 directory (which is cluster chain based);
OR
1.2) FAT12/16 non-root directory (which is also cluster chain based);
OR
1.3) FAT12/16 root directory (allocated as contiguous sectors area), but
all necessary information MUST be within the input buffer of filesystem cluster
size (thus cluster-chain jump is never initiated).
In order to accomplish the last condition, root directory parsing code in
do_fat_read() uses the following trick: read-out cluster-size block, process
only first sector (512 bytes), then shift 512 forward, read-out cluster-size
block and so on. This works great unless cluster size is equal to 512 bytes
(in a case you have a small partition), or long file name entries are scattered
across three sectors, see 4) for details.
2) Despite of the fact that get_vfatname() supports FAT32 root directory
browsing, do_fat_read() function doesn't send current cluster number correctly,
so root directory look-up doesn't work correctly.
3) get_vfatname() doesn't gather scattered entries correctly also is the case
when all LFN entries are located at the end of the source cluster, but real
directory entry (which must be returned) is at the only beginning of the
next one. No error detected, the resulting directory entry returned contains
a semi-random information (wrong size, wrong start cluster number and so on)
i.e. the entry is not accessible.
4) LFN (VFAT) allows up to 20 entries (slots) each containing 26 bytes (13
UTF-16 code units) to represent a single long file name i.e. up to 520 bytes.
U-Boot allocates 256 bytes buffer instead, i.e. 10 or more LFN slots record
may cause buffer overflow / memory corruption.
Also, it's worth to mention that 20+1 slots occupy 672 bytes space which may
take more than one cluster of 512 bytes (medium-size FAT32 or small FAT16
partition) - get_vfatname() function doesn't support such case as well.
The patch attached fixes these problems in the following way:
- keep using 256 bytes buffer for a long file name, but safely prevent a
possible buffer overflow (skip LFN processing, if it contains 10 or more
slots).
- explicitly specify FAT12/16 root directory parsing buffer size, instead
of relying on cluster size. The value used is a double sector size (to store
current sector and the next one). This fixes the first problem and increases
performance on big FAT12/16 partitions;
- send current cluster number (FAT32) to get_vfatname() during root
directory processing;
- use LFN counter to seek the real directory entry in get_vfatname() - fixes the
third problem;
- skip deleted entries in the root directory (to prevent bogus buffer
overflow detection and LFN counter steps).
Note: it's not advised to split up the patch, because a separate part may
operate incorrectly.
Signed-off-by: Mikhail Zolotaryov <lebon@lebon.org.ua>
2010-09-08 14:06:03 +00:00
|
|
|
counter--;
|
|
|
|
}
|
2010-07-19 09:37:00 +00:00
|
|
|
|
2003-06-15 22:40:42 +00:00
|
|
|
/* Save the real directory entry */
|
VFAT: fix processing of scattered long file name entries
The U-Boot code has the following bugs related to the processing of Long File
Name (LFN) entries scattered across several clusters/sectors :
1) get_vfatname() function is designed to gather scattered LFN entries by
cluster chain processing - that doesn't work for FAT12/16 root directory.
In other words, the function expects the following input data:
1.1) FAT32 directory (which is cluster chain based);
OR
1.2) FAT12/16 non-root directory (which is also cluster chain based);
OR
1.3) FAT12/16 root directory (allocated as contiguous sectors area), but
all necessary information MUST be within the input buffer of filesystem cluster
size (thus cluster-chain jump is never initiated).
In order to accomplish the last condition, root directory parsing code in
do_fat_read() uses the following trick: read-out cluster-size block, process
only first sector (512 bytes), then shift 512 forward, read-out cluster-size
block and so on. This works great unless cluster size is equal to 512 bytes
(in a case you have a small partition), or long file name entries are scattered
across three sectors, see 4) for details.
2) Despite of the fact that get_vfatname() supports FAT32 root directory
browsing, do_fat_read() function doesn't send current cluster number correctly,
so root directory look-up doesn't work correctly.
3) get_vfatname() doesn't gather scattered entries correctly also is the case
when all LFN entries are located at the end of the source cluster, but real
directory entry (which must be returned) is at the only beginning of the
next one. No error detected, the resulting directory entry returned contains
a semi-random information (wrong size, wrong start cluster number and so on)
i.e. the entry is not accessible.
4) LFN (VFAT) allows up to 20 entries (slots) each containing 26 bytes (13
UTF-16 code units) to represent a single long file name i.e. up to 520 bytes.
U-Boot allocates 256 bytes buffer instead, i.e. 10 or more LFN slots record
may cause buffer overflow / memory corruption.
Also, it's worth to mention that 20+1 slots occupy 672 bytes space which may
take more than one cluster of 512 bytes (medium-size FAT32 or small FAT16
partition) - get_vfatname() function doesn't support such case as well.
The patch attached fixes these problems in the following way:
- keep using 256 bytes buffer for a long file name, but safely prevent a
possible buffer overflow (skip LFN processing, if it contains 10 or more
slots).
- explicitly specify FAT12/16 root directory parsing buffer size, instead
of relying on cluster size. The value used is a double sector size (to store
current sector and the next one). This fixes the first problem and increases
performance on big FAT12/16 partitions;
- send current cluster number (FAT32) to get_vfatname() during root
directory processing;
- use LFN counter to seek the real directory entry in get_vfatname() - fixes the
third problem;
- skip deleted entries in the root directory (to prevent bogus buffer
overflow detection and LFN counter steps).
Note: it's not advised to split up the patch, because a separate part may
operate incorrectly.
Signed-off-by: Mikhail Zolotaryov <lebon@lebon.org.ua>
2010-09-08 14:06:03 +00:00
|
|
|
realdent = (dir_entry *)slotptr2;
|
|
|
|
while ((__u8 *)slotptr2 > get_vfatname_block) {
|
2003-06-15 22:40:42 +00:00
|
|
|
slotptr2--;
|
VFAT: fix processing of scattered long file name entries
The U-Boot code has the following bugs related to the processing of Long File
Name (LFN) entries scattered across several clusters/sectors :
1) get_vfatname() function is designed to gather scattered LFN entries by
cluster chain processing - that doesn't work for FAT12/16 root directory.
In other words, the function expects the following input data:
1.1) FAT32 directory (which is cluster chain based);
OR
1.2) FAT12/16 non-root directory (which is also cluster chain based);
OR
1.3) FAT12/16 root directory (allocated as contiguous sectors area), but
all necessary information MUST be within the input buffer of filesystem cluster
size (thus cluster-chain jump is never initiated).
In order to accomplish the last condition, root directory parsing code in
do_fat_read() uses the following trick: read-out cluster-size block, process
only first sector (512 bytes), then shift 512 forward, read-out cluster-size
block and so on. This works great unless cluster size is equal to 512 bytes
(in a case you have a small partition), or long file name entries are scattered
across three sectors, see 4) for details.
2) Despite of the fact that get_vfatname() supports FAT32 root directory
browsing, do_fat_read() function doesn't send current cluster number correctly,
so root directory look-up doesn't work correctly.
3) get_vfatname() doesn't gather scattered entries correctly also is the case
when all LFN entries are located at the end of the source cluster, but real
directory entry (which must be returned) is at the only beginning of the
next one. No error detected, the resulting directory entry returned contains
a semi-random information (wrong size, wrong start cluster number and so on)
i.e. the entry is not accessible.
4) LFN (VFAT) allows up to 20 entries (slots) each containing 26 bytes (13
UTF-16 code units) to represent a single long file name i.e. up to 520 bytes.
U-Boot allocates 256 bytes buffer instead, i.e. 10 or more LFN slots record
may cause buffer overflow / memory corruption.
Also, it's worth to mention that 20+1 slots occupy 672 bytes space which may
take more than one cluster of 512 bytes (medium-size FAT32 or small FAT16
partition) - get_vfatname() function doesn't support such case as well.
The patch attached fixes these problems in the following way:
- keep using 256 bytes buffer for a long file name, but safely prevent a
possible buffer overflow (skip LFN processing, if it contains 10 or more
slots).
- explicitly specify FAT12/16 root directory parsing buffer size, instead
of relying on cluster size. The value used is a double sector size (to store
current sector and the next one). This fixes the first problem and increases
performance on big FAT12/16 partitions;
- send current cluster number (FAT32) to get_vfatname() during root
directory processing;
- use LFN counter to seek the real directory entry in get_vfatname() - fixes the
third problem;
- skip deleted entries in the root directory (to prevent bogus buffer
overflow detection and LFN counter steps).
Note: it's not advised to split up the patch, because a separate part may
operate incorrectly.
Signed-off-by: Mikhail Zolotaryov <lebon@lebon.org.ua>
2010-09-08 14:06:03 +00:00
|
|
|
slot2str(slotptr2, l_name, &idx);
|
2003-06-15 22:40:42 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Save the real directory entry */
|
2010-07-19 09:37:00 +00:00
|
|
|
realdent = (dir_entry *)slotptr;
|
2003-06-15 22:40:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
|
|
|
slotptr--;
|
2010-07-19 09:37:00 +00:00
|
|
|
if (slot2str(slotptr, l_name, &idx))
|
|
|
|
break;
|
* Patch by Thomas Elste, 10 Feb 2004:
Add support for NET+50 CPU and ModNET50 board
* Patch by Sam Song, 10 Feb 2004:
Fix typos in cfi_flash.c
* Patch by Leon Kukovec, 10 Feb 2004
Fixed long dir entry slot id calculation in get_vfatname
* Patch by Robin Gilks, 10 Feb 2004:
add "itest" command (operators: -eq, -ne, -lt, -gt, -le, -ge, ==,
!=, <>, <, >, <=, >=)
2004-02-23 19:30:57 +00:00
|
|
|
} while (!(slotptr->id & LAST_LONG_ENTRY_MASK));
|
2003-06-15 22:40:42 +00:00
|
|
|
|
|
|
|
l_name[idx] = '\0';
|
2010-07-19 09:37:00 +00:00
|
|
|
if (*l_name == DELETED_FLAG)
|
|
|
|
*l_name = '\0';
|
|
|
|
else if (*l_name == aRING)
|
|
|
|
*l_name = DELETED_FLAG;
|
2003-06-15 22:40:42 +00:00
|
|
|
downcase(l_name);
|
|
|
|
|
|
|
|
/* Return the real directory entry */
|
|
|
|
memcpy(retdent, realdent, sizeof(dir_entry));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Calculate short name checksum */
|
2010-07-19 09:37:00 +00:00
|
|
|
static __u8 mkcksum (const char *str)
|
2003-06-15 22:40:42 +00:00
|
|
|
{
|
|
|
|
int i;
|
2010-07-19 09:37:00 +00:00
|
|
|
|
2003-06-15 22:40:42 +00:00
|
|
|
__u8 ret = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < 11; i++) {
|
2010-07-19 09:37:00 +00:00
|
|
|
ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + str[i];
|
2003-06-15 22:40:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2010-07-19 09:37:00 +00:00
|
|
|
#endif /* CONFIG_SUPPORT_VFAT */
|
2003-06-15 22:40:42 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the directory entry associated with 'filename' from the directory
|
|
|
|
* starting at 'startsect'
|
|
|
|
*/
|
2012-04-11 04:08:53 +00:00
|
|
|
__u8 get_dentfromdir_block[MAX_CLUSTSIZE]
|
|
|
|
__aligned(ARCH_DMA_MINALIGN);
|
2010-07-19 09:37:00 +00:00
|
|
|
|
|
|
|
static dir_entry *get_dentfromdir (fsdata *mydata, int startsect,
|
|
|
|
char *filename, dir_entry *retdent,
|
2003-06-15 22:40:42 +00:00
|
|
|
int dols)
|
|
|
|
{
|
2010-07-19 09:37:00 +00:00
|
|
|
__u16 prevcksum = 0xffff;
|
|
|
|
__u32 curclust = START(retdent);
|
|
|
|
int files = 0, dirs = 0;
|
2003-06-15 22:40:42 +00:00
|
|
|
|
2010-07-19 09:37:00 +00:00
|
|
|
debug("get_dentfromdir: %s\n", filename);
|
2003-06-15 22:40:42 +00:00
|
|
|
|
2010-07-19 09:37:00 +00:00
|
|
|
while (1) {
|
|
|
|
dir_entry *dentptr;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (get_cluster(mydata, curclust, get_dentfromdir_block,
|
2011-08-08 09:38:33 +00:00
|
|
|
mydata->clust_size * mydata->sect_size) != 0) {
|
2010-07-19 09:37:00 +00:00
|
|
|
debug("Error: reading directory block\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
dentptr = (dir_entry *)get_dentfromdir_block;
|
|
|
|
|
|
|
|
for (i = 0; i < DIRENTSPERCLUST; i++) {
|
VFAT: fix processing of scattered long file name entries
The U-Boot code has the following bugs related to the processing of Long File
Name (LFN) entries scattered across several clusters/sectors :
1) get_vfatname() function is designed to gather scattered LFN entries by
cluster chain processing - that doesn't work for FAT12/16 root directory.
In other words, the function expects the following input data:
1.1) FAT32 directory (which is cluster chain based);
OR
1.2) FAT12/16 non-root directory (which is also cluster chain based);
OR
1.3) FAT12/16 root directory (allocated as contiguous sectors area), but
all necessary information MUST be within the input buffer of filesystem cluster
size (thus cluster-chain jump is never initiated).
In order to accomplish the last condition, root directory parsing code in
do_fat_read() uses the following trick: read-out cluster-size block, process
only first sector (512 bytes), then shift 512 forward, read-out cluster-size
block and so on. This works great unless cluster size is equal to 512 bytes
(in a case you have a small partition), or long file name entries are scattered
across three sectors, see 4) for details.
2) Despite of the fact that get_vfatname() supports FAT32 root directory
browsing, do_fat_read() function doesn't send current cluster number correctly,
so root directory look-up doesn't work correctly.
3) get_vfatname() doesn't gather scattered entries correctly also is the case
when all LFN entries are located at the end of the source cluster, but real
directory entry (which must be returned) is at the only beginning of the
next one. No error detected, the resulting directory entry returned contains
a semi-random information (wrong size, wrong start cluster number and so on)
i.e. the entry is not accessible.
4) LFN (VFAT) allows up to 20 entries (slots) each containing 26 bytes (13
UTF-16 code units) to represent a single long file name i.e. up to 520 bytes.
U-Boot allocates 256 bytes buffer instead, i.e. 10 or more LFN slots record
may cause buffer overflow / memory corruption.
Also, it's worth to mention that 20+1 slots occupy 672 bytes space which may
take more than one cluster of 512 bytes (medium-size FAT32 or small FAT16
partition) - get_vfatname() function doesn't support such case as well.
The patch attached fixes these problems in the following way:
- keep using 256 bytes buffer for a long file name, but safely prevent a
possible buffer overflow (skip LFN processing, if it contains 10 or more
slots).
- explicitly specify FAT12/16 root directory parsing buffer size, instead
of relying on cluster size. The value used is a double sector size (to store
current sector and the next one). This fixes the first problem and increases
performance on big FAT12/16 partitions;
- send current cluster number (FAT32) to get_vfatname() during root
directory processing;
- use LFN counter to seek the real directory entry in get_vfatname() - fixes the
third problem;
- skip deleted entries in the root directory (to prevent bogus buffer
overflow detection and LFN counter steps).
Note: it's not advised to split up the patch, because a separate part may
operate incorrectly.
Signed-off-by: Mikhail Zolotaryov <lebon@lebon.org.ua>
2010-09-08 14:06:03 +00:00
|
|
|
char s_name[14], l_name[VFAT_MAXLEN_BYTES];
|
2010-07-19 09:37:00 +00:00
|
|
|
|
|
|
|
l_name[0] = '\0';
|
|
|
|
if (dentptr->name[0] == DELETED_FLAG) {
|
|
|
|
dentptr++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ((dentptr->attr & ATTR_VOLUME)) {
|
2003-06-15 22:40:42 +00:00
|
|
|
#ifdef CONFIG_SUPPORT_VFAT
|
2011-10-19 07:43:08 +00:00
|
|
|
if ((dentptr->attr & ATTR_VFAT) == ATTR_VFAT &&
|
|
|
|
(dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
|
2010-07-19 09:37:00 +00:00
|
|
|
prevcksum = ((dir_slot *)dentptr)->alias_checksum;
|
|
|
|
get_vfatname(mydata, curclust,
|
|
|
|
get_dentfromdir_block,
|
|
|
|
dentptr, l_name);
|
|
|
|
if (dols) {
|
|
|
|
int isdir;
|
|
|
|
char dirc;
|
|
|
|
int doit = 0;
|
|
|
|
|
|
|
|
isdir = (dentptr->attr & ATTR_DIR);
|
|
|
|
|
|
|
|
if (isdir) {
|
|
|
|
dirs++;
|
|
|
|
dirc = '/';
|
|
|
|
doit = 1;
|
|
|
|
} else {
|
|
|
|
dirc = ' ';
|
|
|
|
if (l_name[0] != 0) {
|
|
|
|
files++;
|
|
|
|
doit = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (doit) {
|
|
|
|
if (dirc == ' ') {
|
|
|
|
printf(" %8ld %s%c\n",
|
|
|
|
(long)FAT2CPU32(dentptr->size),
|
|
|
|
l_name,
|
|
|
|
dirc);
|
|
|
|
} else {
|
|
|
|
printf(" %s%c\n",
|
|
|
|
l_name,
|
|
|
|
dirc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dentptr++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
debug("vfatname: |%s|\n", l_name);
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
/* Volume label or VFAT entry */
|
|
|
|
dentptr++;
|
|
|
|
continue;
|
|
|
|
}
|
2003-06-15 22:40:42 +00:00
|
|
|
}
|
2010-07-19 09:37:00 +00:00
|
|
|
if (dentptr->name[0] == 0) {
|
|
|
|
if (dols) {
|
|
|
|
printf("\n%d file(s), %d dir(s)\n\n",
|
|
|
|
files, dirs);
|
|
|
|
}
|
|
|
|
debug("Dentname == NULL - %d\n", i);
|
|
|
|
return NULL;
|
2003-06-15 22:40:42 +00:00
|
|
|
}
|
|
|
|
#ifdef CONFIG_SUPPORT_VFAT
|
2010-07-19 09:37:00 +00:00
|
|
|
if (dols && mkcksum(dentptr->name) == prevcksum) {
|
2012-01-02 06:54:29 +00:00
|
|
|
prevcksum = 0xffff;
|
2010-07-19 09:37:00 +00:00
|
|
|
dentptr++;
|
|
|
|
continue;
|
|
|
|
}
|
2003-06-15 22:40:42 +00:00
|
|
|
#endif
|
2010-07-19 09:37:00 +00:00
|
|
|
get_name(dentptr, s_name);
|
|
|
|
if (dols) {
|
|
|
|
int isdir = (dentptr->attr & ATTR_DIR);
|
|
|
|
char dirc;
|
|
|
|
int doit = 0;
|
|
|
|
|
|
|
|
if (isdir) {
|
|
|
|
dirs++;
|
|
|
|
dirc = '/';
|
|
|
|
doit = 1;
|
|
|
|
} else {
|
|
|
|
dirc = ' ';
|
|
|
|
if (s_name[0] != 0) {
|
|
|
|
files++;
|
|
|
|
doit = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (doit) {
|
|
|
|
if (dirc == ' ') {
|
|
|
|
printf(" %8ld %s%c\n",
|
|
|
|
(long)FAT2CPU32(dentptr->size),
|
|
|
|
s_name, dirc);
|
|
|
|
} else {
|
|
|
|
printf(" %s%c\n",
|
|
|
|
s_name, dirc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dentptr++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(filename, s_name)
|
|
|
|
&& strcmp(filename, l_name)) {
|
|
|
|
debug("Mismatch: |%s|%s|\n", s_name, l_name);
|
|
|
|
dentptr++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(retdent, dentptr, sizeof(dir_entry));
|
|
|
|
|
|
|
|
debug("DentName: %s", s_name);
|
|
|
|
debug(", start: 0x%x", START(dentptr));
|
|
|
|
debug(", size: 0x%x %s\n",
|
|
|
|
FAT2CPU32(dentptr->size),
|
|
|
|
(dentptr->attr & ATTR_DIR) ? "(DIR)" : "");
|
|
|
|
|
|
|
|
return retdent;
|
2003-06-15 22:40:42 +00:00
|
|
|
}
|
2010-07-19 09:37:00 +00:00
|
|
|
|
|
|
|
curclust = get_fatent(mydata, curclust);
|
|
|
|
if (CHECK_CLUST(curclust, mydata->fatsize)) {
|
|
|
|
debug("curclust: 0x%x\n", curclust);
|
|
|
|
printf("Invalid FAT entry\n");
|
|
|
|
return NULL;
|
2003-06-15 22:40:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-19 09:37:00 +00:00
|
|
|
return NULL;
|
2003-06-15 22:40:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read boot sector and volume info from a FAT filesystem
|
|
|
|
*/
|
|
|
|
static int
|
2010-07-19 09:37:00 +00:00
|
|
|
read_bootsectandvi (boot_sector *bs, volume_info *volinfo, int *fatsize)
|
2003-06-15 22:40:42 +00:00
|
|
|
{
|
2011-08-08 09:38:33 +00:00
|
|
|
__u8 *block;
|
2003-06-15 22:40:42 +00:00
|
|
|
volume_info *vistart;
|
2011-08-08 09:38:33 +00:00
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (cur_dev == NULL) {
|
|
|
|
debug("Error: no device selected\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-04-11 04:08:53 +00:00
|
|
|
block = memalign(ARCH_DMA_MINALIGN, cur_dev->blksz);
|
2011-08-08 09:38:33 +00:00
|
|
|
if (block == NULL) {
|
|
|
|
debug("Error: allocating block\n");
|
|
|
|
return -1;
|
|
|
|
}
|
2003-06-15 22:40:42 +00:00
|
|
|
|
2010-07-19 09:37:00 +00:00
|
|
|
if (disk_read (0, 1, block) < 0) {
|
|
|
|
debug("Error: reading block\n");
|
2011-08-08 09:38:33 +00:00
|
|
|
goto fail;
|
2003-06-15 22:40:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(bs, block, sizeof(boot_sector));
|
2010-07-19 09:37:00 +00:00
|
|
|
bs->reserved = FAT2CPU16(bs->reserved);
|
|
|
|
bs->fat_length = FAT2CPU16(bs->fat_length);
|
|
|
|
bs->secs_track = FAT2CPU16(bs->secs_track);
|
|
|
|
bs->heads = FAT2CPU16(bs->heads);
|
|
|
|
bs->total_sect = FAT2CPU32(bs->total_sect);
|
2003-06-15 22:40:42 +00:00
|
|
|
|
|
|
|
/* FAT32 entries */
|
|
|
|
if (bs->fat_length == 0) {
|
|
|
|
/* Assume FAT32 */
|
|
|
|
bs->fat32_length = FAT2CPU32(bs->fat32_length);
|
2010-07-19 09:37:00 +00:00
|
|
|
bs->flags = FAT2CPU16(bs->flags);
|
2003-06-15 22:40:42 +00:00
|
|
|
bs->root_cluster = FAT2CPU32(bs->root_cluster);
|
2010-07-19 09:37:00 +00:00
|
|
|
bs->info_sector = FAT2CPU16(bs->info_sector);
|
|
|
|
bs->backup_boot = FAT2CPU16(bs->backup_boot);
|
|
|
|
vistart = (volume_info *)(block + sizeof(boot_sector));
|
2003-06-15 22:40:42 +00:00
|
|
|
*fatsize = 32;
|
|
|
|
} else {
|
2010-07-19 09:37:00 +00:00
|
|
|
vistart = (volume_info *)&(bs->fat32_length);
|
2003-06-15 22:40:42 +00:00
|
|
|
*fatsize = 0;
|
|
|
|
}
|
|
|
|
memcpy(volinfo, vistart, sizeof(volume_info));
|
|
|
|
|
|
|
|
if (*fatsize == 32) {
|
2010-07-19 09:37:00 +00:00
|
|
|
if (strncmp(FAT32_SIGN, vistart->fs_type, SIGNLEN) == 0)
|
2011-08-08 09:38:33 +00:00
|
|
|
goto exit;
|
2003-06-15 22:40:42 +00:00
|
|
|
} else {
|
2009-05-20 12:55:41 +00:00
|
|
|
if (strncmp(FAT12_SIGN, vistart->fs_type, SIGNLEN) == 0) {
|
2003-06-15 22:40:42 +00:00
|
|
|
*fatsize = 12;
|
2011-08-08 09:38:33 +00:00
|
|
|
goto exit;
|
2003-06-15 22:40:42 +00:00
|
|
|
}
|
2009-05-20 12:55:41 +00:00
|
|
|
if (strncmp(FAT16_SIGN, vistart->fs_type, SIGNLEN) == 0) {
|
2003-06-15 22:40:42 +00:00
|
|
|
*fatsize = 16;
|
2011-08-08 09:38:33 +00:00
|
|
|
goto exit;
|
2003-06-15 22:40:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-19 09:37:00 +00:00
|
|
|
debug("Error: broken fs_type sign\n");
|
2011-08-08 09:38:33 +00:00
|
|
|
fail:
|
|
|
|
ret = -1;
|
|
|
|
exit:
|
|
|
|
free(block);
|
|
|
|
return ret;
|
2003-06-15 22:40:42 +00:00
|
|
|
}
|
|
|
|
|
2012-04-11 04:08:53 +00:00
|
|
|
__u8 do_fat_read_block[MAX_CLUSTSIZE]
|
|
|
|
__aligned(ARCH_DMA_MINALIGN);
|
2010-07-19 09:37:00 +00:00
|
|
|
|
2004-12-16 17:57:26 +00:00
|
|
|
long
|
2003-06-15 22:40:42 +00:00
|
|
|
do_fat_read (const char *filename, void *buffer, unsigned long maxsize,
|
|
|
|
int dols)
|
|
|
|
{
|
2010-07-19 09:37:00 +00:00
|
|
|
char fnamecopy[2048];
|
|
|
|
boot_sector bs;
|
|
|
|
volume_info volinfo;
|
|
|
|
fsdata datablock;
|
|
|
|
fsdata *mydata = &datablock;
|
|
|
|
dir_entry *dentptr;
|
|
|
|
__u16 prevcksum = 0xffff;
|
|
|
|
char *subname = "";
|
2011-03-24 09:15:37 +00:00
|
|
|
__u32 cursect;
|
2010-07-19 09:37:00 +00:00
|
|
|
int idx, isdir = 0;
|
|
|
|
int files = 0, dirs = 0;
|
2011-08-08 09:38:33 +00:00
|
|
|
long ret = -1;
|
2010-07-19 09:37:00 +00:00
|
|
|
int firsttime;
|
2011-08-19 09:32:34 +00:00
|
|
|
__u32 root_cluster = 0;
|
2011-03-24 09:15:37 +00:00
|
|
|
int rootdir_size = 0;
|
2010-07-19 09:37:00 +00:00
|
|
|
int j;
|
|
|
|
|
|
|
|
if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) {
|
|
|
|
debug("Error: reading boot sector\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2011-08-19 09:32:34 +00:00
|
|
|
if (mydata->fatsize == 32) {
|
|
|
|
root_cluster = bs.root_cluster;
|
2010-07-19 09:37:00 +00:00
|
|
|
mydata->fatlength = bs.fat32_length;
|
2011-08-19 09:32:34 +00:00
|
|
|
} else {
|
2010-07-19 09:37:00 +00:00
|
|
|
mydata->fatlength = bs.fat_length;
|
2011-08-19 09:32:34 +00:00
|
|
|
}
|
2010-07-19 09:37:00 +00:00
|
|
|
|
|
|
|
mydata->fat_sect = bs.reserved;
|
|
|
|
|
|
|
|
cursect = mydata->rootdir_sect
|
|
|
|
= mydata->fat_sect + mydata->fatlength * bs.fats;
|
|
|
|
|
2011-08-08 09:38:33 +00:00
|
|
|
mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0];
|
2010-07-19 09:37:00 +00:00
|
|
|
mydata->clust_size = bs.cluster_size;
|
2011-12-20 07:41:13 +00:00
|
|
|
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;
|
|
|
|
}
|
2010-07-19 09:37:00 +00:00
|
|
|
|
|
|
|
if (mydata->fatsize == 32) {
|
|
|
|
mydata->data_begin = mydata->rootdir_sect -
|
|
|
|
(mydata->clust_size * 2);
|
|
|
|
} else {
|
|
|
|
rootdir_size = ((bs.dir_entries[1] * (int)256 +
|
|
|
|
bs.dir_entries[0]) *
|
|
|
|
sizeof(dir_entry)) /
|
2011-08-08 09:38:33 +00:00
|
|
|
mydata->sect_size;
|
2010-07-19 09:37:00 +00:00
|
|
|
mydata->data_begin = mydata->rootdir_sect +
|
|
|
|
rootdir_size -
|
|
|
|
(mydata->clust_size * 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
mydata->fatbufnum = -1;
|
2012-04-11 04:08:53 +00:00
|
|
|
mydata->fatbuf = memalign(ARCH_DMA_MINALIGN, FATBUFSIZE);
|
2011-08-08 09:38:33 +00:00
|
|
|
if (mydata->fatbuf == NULL) {
|
|
|
|
debug("Error: allocating memory\n");
|
|
|
|
return -1;
|
|
|
|
}
|
2003-06-15 22:40:42 +00:00
|
|
|
|
2010-07-19 09:36:58 +00:00
|
|
|
#ifdef CONFIG_SUPPORT_VFAT
|
2010-07-19 09:37:00 +00:00
|
|
|
debug("VFAT Support enabled\n");
|
2010-07-19 09:36:58 +00:00
|
|
|
#endif
|
2010-07-19 09:37:00 +00:00
|
|
|
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,
|
2011-08-08 09:38:33 +00:00
|
|
|
mydata->rootdir_sect * mydata->sect_size, mydata->data_begin);
|
|
|
|
debug("Sector size: %d, cluster size: %d\n", mydata->sect_size,
|
|
|
|
mydata->clust_size);
|
2010-07-19 09:37:00 +00:00
|
|
|
|
|
|
|
/* "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);
|
|
|
|
|
|
|
|
if (*fnamecopy == '\0') {
|
|
|
|
if (!dols)
|
2011-08-08 09:38:33 +00:00
|
|
|
goto exit;
|
2003-06-15 22:40:42 +00:00
|
|
|
|
2010-07-19 09:37:00 +00:00
|
|
|
dols = LS_ROOT;
|
|
|
|
} else if ((idx = dirdelim(fnamecopy)) >= 0) {
|
|
|
|
isdir = 1;
|
|
|
|
fnamecopy[idx] = '\0';
|
|
|
|
subname = fnamecopy + idx + 1;
|
|
|
|
|
|
|
|
/* Handle multiple delimiters */
|
|
|
|
while (ISDIRDELIM(*subname))
|
|
|
|
subname++;
|
|
|
|
} else if (dols) {
|
|
|
|
isdir = 1;
|
2003-06-15 22:40:42 +00:00
|
|
|
}
|
|
|
|
|
2010-07-19 09:37:00 +00:00
|
|
|
j = 0;
|
|
|
|
while (1) {
|
|
|
|
int i;
|
|
|
|
|
2011-12-15 08:56:54 +00:00
|
|
|
debug("FAT read sect=%d, clust_size=%d, DIRENTSPERBLOCK=%zd\n",
|
2010-07-19 09:37:00 +00:00
|
|
|
cursect, mydata->clust_size, DIRENTSPERBLOCK);
|
|
|
|
|
VFAT: fix processing of scattered long file name entries
The U-Boot code has the following bugs related to the processing of Long File
Name (LFN) entries scattered across several clusters/sectors :
1) get_vfatname() function is designed to gather scattered LFN entries by
cluster chain processing - that doesn't work for FAT12/16 root directory.
In other words, the function expects the following input data:
1.1) FAT32 directory (which is cluster chain based);
OR
1.2) FAT12/16 non-root directory (which is also cluster chain based);
OR
1.3) FAT12/16 root directory (allocated as contiguous sectors area), but
all necessary information MUST be within the input buffer of filesystem cluster
size (thus cluster-chain jump is never initiated).
In order to accomplish the last condition, root directory parsing code in
do_fat_read() uses the following trick: read-out cluster-size block, process
only first sector (512 bytes), then shift 512 forward, read-out cluster-size
block and so on. This works great unless cluster size is equal to 512 bytes
(in a case you have a small partition), or long file name entries are scattered
across three sectors, see 4) for details.
2) Despite of the fact that get_vfatname() supports FAT32 root directory
browsing, do_fat_read() function doesn't send current cluster number correctly,
so root directory look-up doesn't work correctly.
3) get_vfatname() doesn't gather scattered entries correctly also is the case
when all LFN entries are located at the end of the source cluster, but real
directory entry (which must be returned) is at the only beginning of the
next one. No error detected, the resulting directory entry returned contains
a semi-random information (wrong size, wrong start cluster number and so on)
i.e. the entry is not accessible.
4) LFN (VFAT) allows up to 20 entries (slots) each containing 26 bytes (13
UTF-16 code units) to represent a single long file name i.e. up to 520 bytes.
U-Boot allocates 256 bytes buffer instead, i.e. 10 or more LFN slots record
may cause buffer overflow / memory corruption.
Also, it's worth to mention that 20+1 slots occupy 672 bytes space which may
take more than one cluster of 512 bytes (medium-size FAT32 or small FAT16
partition) - get_vfatname() function doesn't support such case as well.
The patch attached fixes these problems in the following way:
- keep using 256 bytes buffer for a long file name, but safely prevent a
possible buffer overflow (skip LFN processing, if it contains 10 or more
slots).
- explicitly specify FAT12/16 root directory parsing buffer size, instead
of relying on cluster size. The value used is a double sector size (to store
current sector and the next one). This fixes the first problem and increases
performance on big FAT12/16 partitions;
- send current cluster number (FAT32) to get_vfatname() during root
directory processing;
- use LFN counter to seek the real directory entry in get_vfatname() - fixes the
third problem;
- skip deleted entries in the root directory (to prevent bogus buffer
overflow detection and LFN counter steps).
Note: it's not advised to split up the patch, because a separate part may
operate incorrectly.
Signed-off-by: Mikhail Zolotaryov <lebon@lebon.org.ua>
2010-09-08 14:06:03 +00:00
|
|
|
if (disk_read(cursect,
|
|
|
|
(mydata->fatsize == 32) ?
|
|
|
|
(mydata->clust_size) :
|
2011-08-19 09:37:46 +00:00
|
|
|
PREFETCH_BLOCKS,
|
VFAT: fix processing of scattered long file name entries
The U-Boot code has the following bugs related to the processing of Long File
Name (LFN) entries scattered across several clusters/sectors :
1) get_vfatname() function is designed to gather scattered LFN entries by
cluster chain processing - that doesn't work for FAT12/16 root directory.
In other words, the function expects the following input data:
1.1) FAT32 directory (which is cluster chain based);
OR
1.2) FAT12/16 non-root directory (which is also cluster chain based);
OR
1.3) FAT12/16 root directory (allocated as contiguous sectors area), but
all necessary information MUST be within the input buffer of filesystem cluster
size (thus cluster-chain jump is never initiated).
In order to accomplish the last condition, root directory parsing code in
do_fat_read() uses the following trick: read-out cluster-size block, process
only first sector (512 bytes), then shift 512 forward, read-out cluster-size
block and so on. This works great unless cluster size is equal to 512 bytes
(in a case you have a small partition), or long file name entries are scattered
across three sectors, see 4) for details.
2) Despite of the fact that get_vfatname() supports FAT32 root directory
browsing, do_fat_read() function doesn't send current cluster number correctly,
so root directory look-up doesn't work correctly.
3) get_vfatname() doesn't gather scattered entries correctly also is the case
when all LFN entries are located at the end of the source cluster, but real
directory entry (which must be returned) is at the only beginning of the
next one. No error detected, the resulting directory entry returned contains
a semi-random information (wrong size, wrong start cluster number and so on)
i.e. the entry is not accessible.
4) LFN (VFAT) allows up to 20 entries (slots) each containing 26 bytes (13
UTF-16 code units) to represent a single long file name i.e. up to 520 bytes.
U-Boot allocates 256 bytes buffer instead, i.e. 10 or more LFN slots record
may cause buffer overflow / memory corruption.
Also, it's worth to mention that 20+1 slots occupy 672 bytes space which may
take more than one cluster of 512 bytes (medium-size FAT32 or small FAT16
partition) - get_vfatname() function doesn't support such case as well.
The patch attached fixes these problems in the following way:
- keep using 256 bytes buffer for a long file name, but safely prevent a
possible buffer overflow (skip LFN processing, if it contains 10 or more
slots).
- explicitly specify FAT12/16 root directory parsing buffer size, instead
of relying on cluster size. The value used is a double sector size (to store
current sector and the next one). This fixes the first problem and increases
performance on big FAT12/16 partitions;
- send current cluster number (FAT32) to get_vfatname() during root
directory processing;
- use LFN counter to seek the real directory entry in get_vfatname() - fixes the
third problem;
- skip deleted entries in the root directory (to prevent bogus buffer
overflow detection and LFN counter steps).
Note: it's not advised to split up the patch, because a separate part may
operate incorrectly.
Signed-off-by: Mikhail Zolotaryov <lebon@lebon.org.ua>
2010-09-08 14:06:03 +00:00
|
|
|
do_fat_read_block) < 0) {
|
2010-07-19 09:37:00 +00:00
|
|
|
debug("Error: reading rootdir block\n");
|
2011-08-08 09:38:33 +00:00
|
|
|
goto exit;
|
2010-07-19 09:37:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
dentptr = (dir_entry *) do_fat_read_block;
|
|
|
|
|
|
|
|
for (i = 0; i < DIRENTSPERBLOCK; i++) {
|
VFAT: fix processing of scattered long file name entries
The U-Boot code has the following bugs related to the processing of Long File
Name (LFN) entries scattered across several clusters/sectors :
1) get_vfatname() function is designed to gather scattered LFN entries by
cluster chain processing - that doesn't work for FAT12/16 root directory.
In other words, the function expects the following input data:
1.1) FAT32 directory (which is cluster chain based);
OR
1.2) FAT12/16 non-root directory (which is also cluster chain based);
OR
1.3) FAT12/16 root directory (allocated as contiguous sectors area), but
all necessary information MUST be within the input buffer of filesystem cluster
size (thus cluster-chain jump is never initiated).
In order to accomplish the last condition, root directory parsing code in
do_fat_read() uses the following trick: read-out cluster-size block, process
only first sector (512 bytes), then shift 512 forward, read-out cluster-size
block and so on. This works great unless cluster size is equal to 512 bytes
(in a case you have a small partition), or long file name entries are scattered
across three sectors, see 4) for details.
2) Despite of the fact that get_vfatname() supports FAT32 root directory
browsing, do_fat_read() function doesn't send current cluster number correctly,
so root directory look-up doesn't work correctly.
3) get_vfatname() doesn't gather scattered entries correctly also is the case
when all LFN entries are located at the end of the source cluster, but real
directory entry (which must be returned) is at the only beginning of the
next one. No error detected, the resulting directory entry returned contains
a semi-random information (wrong size, wrong start cluster number and so on)
i.e. the entry is not accessible.
4) LFN (VFAT) allows up to 20 entries (slots) each containing 26 bytes (13
UTF-16 code units) to represent a single long file name i.e. up to 520 bytes.
U-Boot allocates 256 bytes buffer instead, i.e. 10 or more LFN slots record
may cause buffer overflow / memory corruption.
Also, it's worth to mention that 20+1 slots occupy 672 bytes space which may
take more than one cluster of 512 bytes (medium-size FAT32 or small FAT16
partition) - get_vfatname() function doesn't support such case as well.
The patch attached fixes these problems in the following way:
- keep using 256 bytes buffer for a long file name, but safely prevent a
possible buffer overflow (skip LFN processing, if it contains 10 or more
slots).
- explicitly specify FAT12/16 root directory parsing buffer size, instead
of relying on cluster size. The value used is a double sector size (to store
current sector and the next one). This fixes the first problem and increases
performance on big FAT12/16 partitions;
- send current cluster number (FAT32) to get_vfatname() during root
directory processing;
- use LFN counter to seek the real directory entry in get_vfatname() - fixes the
third problem;
- skip deleted entries in the root directory (to prevent bogus buffer
overflow detection and LFN counter steps).
Note: it's not advised to split up the patch, because a separate part may
operate incorrectly.
Signed-off-by: Mikhail Zolotaryov <lebon@lebon.org.ua>
2010-09-08 14:06:03 +00:00
|
|
|
char s_name[14], l_name[VFAT_MAXLEN_BYTES];
|
2010-07-19 09:37:00 +00:00
|
|
|
|
|
|
|
l_name[0] = '\0';
|
VFAT: fix processing of scattered long file name entries
The U-Boot code has the following bugs related to the processing of Long File
Name (LFN) entries scattered across several clusters/sectors :
1) get_vfatname() function is designed to gather scattered LFN entries by
cluster chain processing - that doesn't work for FAT12/16 root directory.
In other words, the function expects the following input data:
1.1) FAT32 directory (which is cluster chain based);
OR
1.2) FAT12/16 non-root directory (which is also cluster chain based);
OR
1.3) FAT12/16 root directory (allocated as contiguous sectors area), but
all necessary information MUST be within the input buffer of filesystem cluster
size (thus cluster-chain jump is never initiated).
In order to accomplish the last condition, root directory parsing code in
do_fat_read() uses the following trick: read-out cluster-size block, process
only first sector (512 bytes), then shift 512 forward, read-out cluster-size
block and so on. This works great unless cluster size is equal to 512 bytes
(in a case you have a small partition), or long file name entries are scattered
across three sectors, see 4) for details.
2) Despite of the fact that get_vfatname() supports FAT32 root directory
browsing, do_fat_read() function doesn't send current cluster number correctly,
so root directory look-up doesn't work correctly.
3) get_vfatname() doesn't gather scattered entries correctly also is the case
when all LFN entries are located at the end of the source cluster, but real
directory entry (which must be returned) is at the only beginning of the
next one. No error detected, the resulting directory entry returned contains
a semi-random information (wrong size, wrong start cluster number and so on)
i.e. the entry is not accessible.
4) LFN (VFAT) allows up to 20 entries (slots) each containing 26 bytes (13
UTF-16 code units) to represent a single long file name i.e. up to 520 bytes.
U-Boot allocates 256 bytes buffer instead, i.e. 10 or more LFN slots record
may cause buffer overflow / memory corruption.
Also, it's worth to mention that 20+1 slots occupy 672 bytes space which may
take more than one cluster of 512 bytes (medium-size FAT32 or small FAT16
partition) - get_vfatname() function doesn't support such case as well.
The patch attached fixes these problems in the following way:
- keep using 256 bytes buffer for a long file name, but safely prevent a
possible buffer overflow (skip LFN processing, if it contains 10 or more
slots).
- explicitly specify FAT12/16 root directory parsing buffer size, instead
of relying on cluster size. The value used is a double sector size (to store
current sector and the next one). This fixes the first problem and increases
performance on big FAT12/16 partitions;
- send current cluster number (FAT32) to get_vfatname() during root
directory processing;
- use LFN counter to seek the real directory entry in get_vfatname() - fixes the
third problem;
- skip deleted entries in the root directory (to prevent bogus buffer
overflow detection and LFN counter steps).
Note: it's not advised to split up the patch, because a separate part may
operate incorrectly.
Signed-off-by: Mikhail Zolotaryov <lebon@lebon.org.ua>
2010-09-08 14:06:03 +00:00
|
|
|
if (dentptr->name[0] == DELETED_FLAG) {
|
|
|
|
dentptr++;
|
|
|
|
continue;
|
|
|
|
}
|
2010-07-19 09:37:00 +00:00
|
|
|
if ((dentptr->attr & ATTR_VOLUME)) {
|
2003-06-15 22:40:42 +00:00
|
|
|
#ifdef CONFIG_SUPPORT_VFAT
|
2011-10-19 07:43:08 +00:00
|
|
|
if ((dentptr->attr & ATTR_VFAT) == ATTR_VFAT &&
|
2010-07-19 09:37:00 +00:00
|
|
|
(dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
|
|
|
|
prevcksum =
|
|
|
|
((dir_slot *)dentptr)->alias_checksum;
|
|
|
|
|
VFAT: fix processing of scattered long file name entries
The U-Boot code has the following bugs related to the processing of Long File
Name (LFN) entries scattered across several clusters/sectors :
1) get_vfatname() function is designed to gather scattered LFN entries by
cluster chain processing - that doesn't work for FAT12/16 root directory.
In other words, the function expects the following input data:
1.1) FAT32 directory (which is cluster chain based);
OR
1.2) FAT12/16 non-root directory (which is also cluster chain based);
OR
1.3) FAT12/16 root directory (allocated as contiguous sectors area), but
all necessary information MUST be within the input buffer of filesystem cluster
size (thus cluster-chain jump is never initiated).
In order to accomplish the last condition, root directory parsing code in
do_fat_read() uses the following trick: read-out cluster-size block, process
only first sector (512 bytes), then shift 512 forward, read-out cluster-size
block and so on. This works great unless cluster size is equal to 512 bytes
(in a case you have a small partition), or long file name entries are scattered
across three sectors, see 4) for details.
2) Despite of the fact that get_vfatname() supports FAT32 root directory
browsing, do_fat_read() function doesn't send current cluster number correctly,
so root directory look-up doesn't work correctly.
3) get_vfatname() doesn't gather scattered entries correctly also is the case
when all LFN entries are located at the end of the source cluster, but real
directory entry (which must be returned) is at the only beginning of the
next one. No error detected, the resulting directory entry returned contains
a semi-random information (wrong size, wrong start cluster number and so on)
i.e. the entry is not accessible.
4) LFN (VFAT) allows up to 20 entries (slots) each containing 26 bytes (13
UTF-16 code units) to represent a single long file name i.e. up to 520 bytes.
U-Boot allocates 256 bytes buffer instead, i.e. 10 or more LFN slots record
may cause buffer overflow / memory corruption.
Also, it's worth to mention that 20+1 slots occupy 672 bytes space which may
take more than one cluster of 512 bytes (medium-size FAT32 or small FAT16
partition) - get_vfatname() function doesn't support such case as well.
The patch attached fixes these problems in the following way:
- keep using 256 bytes buffer for a long file name, but safely prevent a
possible buffer overflow (skip LFN processing, if it contains 10 or more
slots).
- explicitly specify FAT12/16 root directory parsing buffer size, instead
of relying on cluster size. The value used is a double sector size (to store
current sector and the next one). This fixes the first problem and increases
performance on big FAT12/16 partitions;
- send current cluster number (FAT32) to get_vfatname() during root
directory processing;
- use LFN counter to seek the real directory entry in get_vfatname() - fixes the
third problem;
- skip deleted entries in the root directory (to prevent bogus buffer
overflow detection and LFN counter steps).
Note: it's not advised to split up the patch, because a separate part may
operate incorrectly.
Signed-off-by: Mikhail Zolotaryov <lebon@lebon.org.ua>
2010-09-08 14:06:03 +00:00
|
|
|
get_vfatname(mydata,
|
2011-08-19 09:32:34 +00:00
|
|
|
root_cluster,
|
2010-07-19 09:37:00 +00:00
|
|
|
do_fat_read_block,
|
|
|
|
dentptr, l_name);
|
|
|
|
|
|
|
|
if (dols == LS_ROOT) {
|
|
|
|
char dirc;
|
|
|
|
int doit = 0;
|
|
|
|
int isdir =
|
|
|
|
(dentptr->attr & ATTR_DIR);
|
|
|
|
|
|
|
|
if (isdir) {
|
|
|
|
dirs++;
|
|
|
|
dirc = '/';
|
|
|
|
doit = 1;
|
|
|
|
} else {
|
|
|
|
dirc = ' ';
|
|
|
|
if (l_name[0] != 0) {
|
|
|
|
files++;
|
|
|
|
doit = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (doit) {
|
|
|
|
if (dirc == ' ') {
|
|
|
|
printf(" %8ld %s%c\n",
|
|
|
|
(long)FAT2CPU32(dentptr->size),
|
|
|
|
l_name,
|
|
|
|
dirc);
|
|
|
|
} else {
|
|
|
|
printf(" %s%c\n",
|
|
|
|
l_name,
|
|
|
|
dirc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dentptr++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
debug("Rootvfatname: |%s|\n",
|
|
|
|
l_name);
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
/* Volume label or VFAT entry */
|
|
|
|
dentptr++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else if (dentptr->name[0] == 0) {
|
|
|
|
debug("RootDentname == NULL - %d\n", i);
|
|
|
|
if (dols == LS_ROOT) {
|
|
|
|
printf("\n%d file(s), %d dir(s)\n\n",
|
|
|
|
files, dirs);
|
2011-08-08 09:38:33 +00:00
|
|
|
ret = 0;
|
2010-07-19 09:37:00 +00:00
|
|
|
}
|
2011-08-08 09:38:33 +00:00
|
|
|
goto exit;
|
2003-06-15 22:40:42 +00:00
|
|
|
}
|
2010-07-19 09:37:00 +00:00
|
|
|
#ifdef CONFIG_SUPPORT_VFAT
|
|
|
|
else if (dols == LS_ROOT &&
|
|
|
|
mkcksum(dentptr->name) == prevcksum) {
|
2012-01-02 06:54:29 +00:00
|
|
|
prevcksum = 0xffff;
|
2010-07-19 09:37:00 +00:00
|
|
|
dentptr++;
|
|
|
|
continue;
|
2003-06-15 22:40:42 +00:00
|
|
|
}
|
|
|
|
#endif
|
2010-07-19 09:37:00 +00:00
|
|
|
get_name(dentptr, s_name);
|
|
|
|
|
|
|
|
if (dols == LS_ROOT) {
|
|
|
|
int isdir = (dentptr->attr & ATTR_DIR);
|
|
|
|
char dirc;
|
|
|
|
int doit = 0;
|
|
|
|
|
|
|
|
if (isdir) {
|
|
|
|
dirc = '/';
|
|
|
|
if (s_name[0] != 0) {
|
|
|
|
dirs++;
|
|
|
|
doit = 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
dirc = ' ';
|
|
|
|
if (s_name[0] != 0) {
|
|
|
|
files++;
|
|
|
|
doit = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (doit) {
|
|
|
|
if (dirc == ' ') {
|
|
|
|
printf(" %8ld %s%c\n",
|
|
|
|
(long)FAT2CPU32(dentptr->size),
|
|
|
|
s_name, dirc);
|
|
|
|
} else {
|
|
|
|
printf(" %s%c\n",
|
|
|
|
s_name, dirc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dentptr++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(fnamecopy, s_name)
|
|
|
|
&& strcmp(fnamecopy, l_name)) {
|
|
|
|
debug("RootMismatch: |%s|%s|\n", s_name,
|
|
|
|
l_name);
|
|
|
|
dentptr++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isdir && !(dentptr->attr & ATTR_DIR))
|
2011-08-08 09:38:33 +00:00
|
|
|
goto exit;
|
2010-07-19 09:37:00 +00:00
|
|
|
|
|
|
|
debug("RootName: %s", s_name);
|
|
|
|
debug(", start: 0x%x", START(dentptr));
|
|
|
|
debug(", size: 0x%x %s\n",
|
|
|
|
FAT2CPU32(dentptr->size),
|
|
|
|
isdir ? "(DIR)" : "");
|
|
|
|
|
|
|
|
goto rootdir_done; /* We got a match */
|
2003-06-15 22:40:42 +00:00
|
|
|
}
|
2010-07-19 09:37:00 +00:00
|
|
|
debug("END LOOP: j=%d clust_size=%d\n", j,
|
|
|
|
mydata->clust_size);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* On FAT32 we must fetch the FAT entries for the next
|
|
|
|
* root directory clusters when a cluster has been
|
|
|
|
* completely processed.
|
|
|
|
*/
|
2011-03-24 09:15:37 +00:00
|
|
|
++j;
|
|
|
|
int fat32_end = 0;
|
|
|
|
if ((mydata->fatsize == 32) && (j == mydata->clust_size)) {
|
|
|
|
int nxtsect = 0;
|
|
|
|
int nxt_clust = 0;
|
2010-07-19 09:37:00 +00:00
|
|
|
|
|
|
|
nxt_clust = get_fatent(mydata, root_cluster);
|
2011-03-24 09:15:37 +00:00
|
|
|
fat32_end = CHECK_CLUST(nxt_clust, 32);
|
2010-07-19 09:37:00 +00:00
|
|
|
|
|
|
|
nxtsect = mydata->data_begin +
|
|
|
|
(nxt_clust * mydata->clust_size);
|
|
|
|
|
|
|
|
root_cluster = nxt_clust;
|
|
|
|
|
|
|
|
cursect = nxtsect;
|
|
|
|
j = 0;
|
2003-06-15 22:40:42 +00:00
|
|
|
} else {
|
2010-07-19 09:37:00 +00:00
|
|
|
cursect++;
|
2003-06-15 22:40:42 +00:00
|
|
|
}
|
2011-03-24 09:15:37 +00:00
|
|
|
|
|
|
|
/* If end of rootdir reached */
|
|
|
|
if ((mydata->fatsize == 32 && fat32_end) ||
|
|
|
|
(mydata->fatsize != 32 && j == rootdir_size)) {
|
|
|
|
if (dols == LS_ROOT) {
|
|
|
|
printf("\n%d file(s), %d dir(s)\n\n",
|
|
|
|
files, dirs);
|
2011-08-08 09:38:33 +00:00
|
|
|
ret = 0;
|
2011-03-24 09:15:37 +00:00
|
|
|
}
|
2011-08-08 09:38:33 +00:00
|
|
|
goto exit;
|
2011-03-24 09:15:37 +00:00
|
|
|
}
|
2010-07-19 09:37:00 +00:00
|
|
|
}
|
|
|
|
rootdir_done:
|
2003-06-15 22:40:42 +00:00
|
|
|
|
2010-07-19 09:37:00 +00:00
|
|
|
firsttime = 1;
|
2003-06-15 22:40:42 +00:00
|
|
|
|
2010-07-19 09:37:00 +00:00
|
|
|
while (isdir) {
|
|
|
|
int startsect = mydata->data_begin
|
|
|
|
+ START(dentptr) * mydata->clust_size;
|
|
|
|
dir_entry dent;
|
|
|
|
char *nextname = NULL;
|
2003-06-15 22:40:42 +00:00
|
|
|
|
2010-07-19 09:37:00 +00:00
|
|
|
dent = *dentptr;
|
|
|
|
dentptr = &dent;
|
2003-06-15 22:40:42 +00:00
|
|
|
|
2010-07-19 09:37:00 +00:00
|
|
|
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)
|
2011-08-08 09:38:33 +00:00
|
|
|
ret = 0;
|
|
|
|
goto exit;
|
2010-07-19 09:37:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (idx >= 0) {
|
|
|
|
if (!(dentptr->attr & ATTR_DIR))
|
2011-08-08 09:38:33 +00:00
|
|
|
goto exit;
|
2010-07-19 09:37:00 +00:00
|
|
|
subname = nextname;
|
|
|
|
}
|
2003-06-15 22:40:42 +00:00
|
|
|
}
|
|
|
|
|
2010-07-19 09:37:00 +00:00
|
|
|
ret = get_contents(mydata, dentptr, buffer, maxsize);
|
|
|
|
debug("Size: %d, got: %ld\n", FAT2CPU32(dentptr->size), ret);
|
2003-06-15 22:40:42 +00:00
|
|
|
|
2011-08-08 09:38:33 +00:00
|
|
|
exit:
|
|
|
|
free(mydata->fatbuf);
|
2010-07-19 09:37:00 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2003-06-15 22:40:42 +00:00
|
|
|
|
2010-07-19 09:37:00 +00:00
|
|
|
int file_fat_detectfs (void)
|
2003-06-15 22:40:42 +00:00
|
|
|
{
|
2010-07-19 09:37:00 +00:00
|
|
|
boot_sector bs;
|
|
|
|
volume_info volinfo;
|
|
|
|
int fatsize;
|
|
|
|
char vol_label[12];
|
2003-06-15 22:40:42 +00:00
|
|
|
|
2010-07-19 09:37:00 +00:00
|
|
|
if (cur_dev == NULL) {
|
2003-09-10 22:30:53 +00:00
|
|
|
printf("No current device\n");
|
|
|
|
return 1;
|
|
|
|
}
|
2010-07-19 09:37:00 +00:00
|
|
|
|
2007-07-09 22:56:50 +00:00
|
|
|
#if defined(CONFIG_CMD_IDE) || \
|
2008-12-10 04:20:18 +00:00
|
|
|
defined(CONFIG_CMD_SATA) || \
|
2007-07-09 22:56:50 +00:00
|
|
|
defined(CONFIG_CMD_SCSI) || \
|
|
|
|
defined(CONFIG_CMD_USB) || \
|
2008-01-16 19:06:59 +00:00
|
|
|
defined(CONFIG_MMC)
|
2003-09-10 22:30:53 +00:00
|
|
|
printf("Interface: ");
|
2010-07-19 09:37:00 +00:00
|
|
|
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");
|
2003-09-10 22:30:53 +00:00
|
|
|
}
|
2010-07-19 09:37:00 +00:00
|
|
|
|
|
|
|
printf("\n Device %d: ", cur_dev->dev);
|
2003-09-10 22:30:53 +00:00
|
|
|
dev_print(cur_dev);
|
|
|
|
#endif
|
2010-07-19 09:37:00 +00:00
|
|
|
|
|
|
|
if (read_bootsectandvi(&bs, &volinfo, &fatsize)) {
|
2003-09-10 22:30:53 +00:00
|
|
|
printf("\nNo valid FAT fs found\n");
|
|
|
|
return 1;
|
|
|
|
}
|
2010-07-19 09:37:00 +00:00
|
|
|
|
|
|
|
memcpy(vol_label, volinfo.volume_label, 11);
|
2003-09-10 22:30:53 +00:00
|
|
|
vol_label[11] = '\0';
|
2010-07-19 09:37:00 +00:00
|
|
|
volinfo.fs_type[5] = '\0';
|
|
|
|
|
2011-12-21 07:08:10 +00:00
|
|
|
printf("Partition %d: Filesystem: %s \"%s\"\n", cur_part_nr,
|
2010-07-19 09:37:00 +00:00
|
|
|
volinfo.fs_type, vol_label);
|
|
|
|
|
2003-09-10 22:30:53 +00:00
|
|
|
return 0;
|
2003-06-15 22:40:42 +00:00
|
|
|
}
|
|
|
|
|
2010-07-19 09:37:00 +00:00
|
|
|
int file_fat_ls (const char *dir)
|
2003-06-15 22:40:42 +00:00
|
|
|
{
|
|
|
|
return do_fat_read(dir, NULL, 0, LS_YES);
|
|
|
|
}
|
|
|
|
|
2010-07-19 09:37:00 +00:00
|
|
|
long file_fat_read (const char *filename, void *buffer, unsigned long maxsize)
|
2003-06-15 22:40:42 +00:00
|
|
|
{
|
2010-07-19 09:37:00 +00:00
|
|
|
printf("reading %s\n", filename);
|
2003-06-15 22:40:42 +00:00
|
|
|
return do_fat_read(filename, buffer, maxsize, LS_NO);
|
|
|
|
}
|