summaryrefslogtreecommitdiff
path: root/fs/fat
diff options
context:
space:
mode:
authorBenoît Thébaudeau <benoit.thebaudeau@advansee.com>2012-09-18 08:14:56 +0000
committerTom Rini <trini@ti.com>2012-09-26 11:11:32 -0700
commit1170e634dd2a6fdd541ae2153fd3fd73919da8fc (patch)
treedd1cdb0854199fad95296c84e54eeb319818b71b /fs/fat
parent9aa90c1df09d6c0a43a5e000ba9d66d6f13b29cb (diff)
FAT: Make it possible to read from any file position
When storage devices contain files larger than the embedded RAM, it is useful to be able to read these files by chunks, e.g. for a software update to the embedded NAND Flash from an external storage device (USB stick, SD card, etc.). Hence, this patch makes it possible by adding a new FAT API to read files from a given position. This patch also adds this feature to the fatload command. Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com> Cc: Wolfgang Denk <wd@denx.de> Signed-off-by: Tom Rini <trini@ti.com>
Diffstat (limited to 'fs/fat')
-rw-r--r--fs/fat/fat.c98
-rw-r--r--fs/fat/fat_write.c18
2 files changed, 88 insertions, 28 deletions
diff --git a/fs/fat/fat.c b/fs/fat/fat.c
index 19f6a8c0af..41ae15eeb9 100644
--- a/fs/fat/fat.c
+++ b/fs/fat/fat.c
@@ -328,13 +328,16 @@ get_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size)
}
/*
- * Read at most 'maxsize' bytes from the file associated with 'dentptr'
+ * Read at most 'maxsize' bytes from 'pos' in the file associated with 'dentptr'
* into 'buffer'.
* Return the number of bytes read or -1 on fatal errors.
*/
+__u8 get_contents_vfatname_block[MAX_CLUSTSIZE]
+ __aligned(ARCH_DMA_MINALIGN);
+
static long
-get_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer,
- unsigned long maxsize)
+get_contents(fsdata *mydata, dir_entry *dentptr, unsigned long pos,
+ __u8 *buffer, unsigned long maxsize)
{
unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0;
unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
@@ -344,12 +347,59 @@ get_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer,
debug("Filesize: %ld bytes\n", filesize);
- if (maxsize > 0 && filesize > maxsize)
- filesize = maxsize;
+ if (pos >= filesize) {
+ debug("Read position past EOF: %lu\n", pos);
+ return gotsize;
+ }
+
+ if (maxsize > 0 && filesize > pos + maxsize)
+ filesize = pos + maxsize;
debug("%ld bytes\n", filesize);
actsize = bytesperclust;
+
+ /* go to cluster at pos */
+ while (actsize <= pos) {
+ curclust = get_fatent(mydata, curclust);
+ 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) {
+ actsize = min(filesize, bytesperclust);
+ if (get_cluster(mydata, curclust, get_contents_vfatname_block,
+ (int)actsize) != 0) {
+ printf("Error reading cluster\n");
+ return -1;
+ }
+ filesize -= actsize;
+ actsize -= pos;
+ memcpy(buffer, get_contents_vfatname_block + pos, actsize);
+ gotsize += actsize;
+ if (!filesize)
+ return gotsize;
+ buffer += actsize;
+
+ curclust = get_fatent(mydata, curclust);
+ if (CHECK_CLUST(curclust, mydata->fatsize)) {
+ debug("curclust: 0x%x\n", curclust);
+ debug("Invalid FAT entry\n");
+ return gotsize;
+ }
+ }
+
+ actsize = bytesperclust;
endclust = curclust;
do {
@@ -433,9 +483,6 @@ static int slot2str(dir_slot *slotptr, char *l_name, int *idx)
* into 'retdent'
* Return 0 on success, -1 otherwise.
*/
-__u8 get_vfatname_block[MAX_CLUSTSIZE]
- __aligned(ARCH_DMA_MINALIGN);
-
static int
get_vfatname(fsdata *mydata, int curclust, __u8 *cluster,
dir_entry *retdent, char *l_name)
@@ -474,13 +521,13 @@ get_vfatname(fsdata *mydata, int curclust, __u8 *cluster,
return -1;
}
- if (get_cluster(mydata, curclust, get_vfatname_block,
+ if (get_cluster(mydata, curclust, get_contents_vfatname_block,
mydata->clust_size * mydata->sect_size) != 0) {
debug("Error: reading directory block\n");
return -1;
}
- slotptr2 = (dir_slot *)get_vfatname_block;
+ slotptr2 = (dir_slot *)get_contents_vfatname_block;
while (counter > 0) {
if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK)
& 0xff) != counter)
@@ -491,7 +538,7 @@ get_vfatname(fsdata *mydata, int curclust, __u8 *cluster,
/* Save the real directory entry */
realdent = (dir_entry *)slotptr2;
- while ((__u8 *)slotptr2 > get_vfatname_block) {
+ while ((__u8 *)slotptr2 > get_contents_vfatname_block) {
slotptr2--;
slot2str(slotptr2, l_name, &idx);
}
@@ -770,11 +817,12 @@ exit:
return ret;
}
-__u8 do_fat_read_block[MAX_CLUSTSIZE]
+__u8 do_fat_read_at_block[MAX_CLUSTSIZE]
__aligned(ARCH_DMA_MINALIGN);
long
-do_fat_read(const char *filename, void *buffer, unsigned long maxsize, int dols)
+do_fat_read_at(const char *filename, unsigned long pos, void *buffer,
+ unsigned long maxsize, int dols)
{
char fnamecopy[2048];
boot_sector bs;
@@ -888,12 +936,12 @@ do_fat_read(const char *filename, void *buffer, unsigned long maxsize, int dols)
(mydata->fatsize == 32) ?
(mydata->clust_size) :
PREFETCH_BLOCKS,
- do_fat_read_block) < 0) {
+ do_fat_read_at_block) < 0) {
debug("Error: reading rootdir block\n");
goto exit;
}
- dentptr = (dir_entry *) do_fat_read_block;
+ dentptr = (dir_entry *) do_fat_read_at_block;
}
for (i = 0; i < DIRENTSPERBLOCK; i++) {
@@ -913,7 +961,7 @@ do_fat_read(const char *filename, void *buffer, unsigned long maxsize, int dols)
get_vfatname(mydata,
root_cluster,
- do_fat_read_block,
+ do_fat_read_at_block,
dentptr, l_name);
if (dols == LS_ROOT) {
@@ -1116,7 +1164,7 @@ rootdir_done:
subname = nextname;
}
- ret = get_contents(mydata, dentptr, buffer, maxsize);
+ ret = get_contents(mydata, dentptr, pos, buffer, maxsize);
debug("Size: %d, got: %ld\n", FAT2CPU32(dentptr->size), ret);
exit:
@@ -1124,6 +1172,12 @@ exit:
return ret;
}
+long
+do_fat_read(const char *filename, void *buffer, unsigned long maxsize, int dols)
+{
+ return do_fat_read_at(filename, 0, buffer, maxsize, dols);
+}
+
int file_fat_detectfs(void)
{
boot_sector bs;
@@ -1192,8 +1246,14 @@ int file_fat_ls(const char *dir)
return do_fat_read(dir, NULL, 0, LS_YES);
}
-long file_fat_read(const char *filename, void *buffer, unsigned long maxsize)
+long file_fat_read_at(const char *filename, unsigned long pos, void *buffer,
+ unsigned long maxsize)
{
printf("reading %s\n", filename);
- return do_fat_read(filename, buffer, maxsize, LS_NO);
+ return do_fat_read_at(filename, pos, buffer, maxsize, LS_NO);
+}
+
+long file_fat_read(const char *filename, void *buffer, unsigned long maxsize)
+{
+ return file_fat_read_at(filename, 0, buffer, maxsize);
}
diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c
index a6181e71b5..5829adf1a1 100644
--- a/fs/fat/fat_write.c
+++ b/fs/fat/fat_write.c
@@ -328,7 +328,7 @@ static void flush_dir_table(fsdata *mydata, dir_entry **dentptr);
static void
fill_dir_slot(fsdata *mydata, dir_entry **dentptr, const char *l_name)
{
- dir_slot *slotptr = (dir_slot *)get_vfatname_block;
+ dir_slot *slotptr = (dir_slot *)get_contents_vfatname_block;
__u8 counter = 0, checksum;
int idx = 0, ret;
char s_name[16];
@@ -373,7 +373,7 @@ static __u32 dir_curclust;
* a slot) into 'l_name'. If successful also copy the real directory entry
* into 'retdent'
* If additional adjacent cluster for directory entries is read into memory,
- * then 'get_vfatname_block' is copied into 'get_dentfromdir_block' and
+ * then 'get_contents_vfatname_block' is copied into 'get_dentfromdir_block' and
* the location of the real directory entry is returned by 'retdent'
* Return 0 on success, -1 otherwise.
*/
@@ -416,13 +416,13 @@ get_long_file_name(fsdata *mydata, int curclust, __u8 *cluster,
dir_curclust = curclust;
- if (get_cluster(mydata, curclust, get_vfatname_block,
+ if (get_cluster(mydata, curclust, get_contents_vfatname_block,
mydata->clust_size * mydata->sect_size) != 0) {
debug("Error: reading directory block\n");
return -1;
}
- slotptr2 = (dir_slot *)get_vfatname_block;
+ slotptr2 = (dir_slot *)get_contents_vfatname_block;
while (counter > 0) {
if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK)
& 0xff) != counter)
@@ -433,7 +433,7 @@ get_long_file_name(fsdata *mydata, int curclust, __u8 *cluster,
/* Save the real directory entry */
realdent = (dir_entry *)slotptr2;
- while ((__u8 *)slotptr2 > get_vfatname_block) {
+ while ((__u8 *)slotptr2 > get_contents_vfatname_block) {
slotptr2--;
slot2str(slotptr2, l_name, &idx);
}
@@ -459,9 +459,9 @@ get_long_file_name(fsdata *mydata, int curclust, __u8 *cluster,
*retdent = realdent;
if (slotptr2) {
- memcpy(get_dentfromdir_block, get_vfatname_block,
+ memcpy(get_dentfromdir_block, get_contents_vfatname_block,
mydata->clust_size * mydata->sect_size);
- cur_position = (__u8 *)realdent - get_vfatname_block;
+ cur_position = (__u8 *)realdent - get_contents_vfatname_block;
*retdent = (dir_entry *) &get_dentfromdir_block[cur_position];
}
@@ -980,11 +980,11 @@ static int do_fat_write(const char *filename, void *buffer,
if (disk_read(cursect,
(mydata->fatsize == 32) ?
(mydata->clust_size) :
- PREFETCH_BLOCKS, do_fat_read_block) < 0) {
+ PREFETCH_BLOCKS, do_fat_read_at_block) < 0) {
debug("Error: reading rootdir block\n");
goto exit;
}
- dentptr = (dir_entry *) do_fat_read_block;
+ dentptr = (dir_entry *) do_fat_read_at_block;
name_len = strlen(filename);
if (name_len >= VFAT_MAXLEN_BYTES)