]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - fs/fat/fat.c
mmc: omap_hsmmc: enable 8bit interface for eMMC for AM43xx
[karo-tx-uboot.git] / fs / fat / fat.c
index 9f835725247eaae1b96250b3c2b14eadcd34e980..bccc3e3ed8fd0929c7f1adf33b2125178be75ccb 100644 (file)
@@ -6,23 +6,7 @@
  * 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
+ * SPDX-License-Identifier:    GPL-2.0+
  */
 
 #include <common.h>
 #include <part.h>
 #include <malloc.h>
 #include <linux/compiler.h>
+#include <linux/ctype.h>
+
+#ifdef CONFIG_SUPPORT_VFAT
+static const int vfat_enabled = 1;
+#else
+static const int vfat_enabled = 0;
+#endif
 
 /*
  * Convert a string to lowercase.
 static void downcase(char *str)
 {
        while (*str != '\0') {
-               TOLOWER(*str);
+               *str = tolower(*str);
                str++;
        }
 }
 
 static block_dev_desc_t *cur_dev;
-static unsigned int cur_part_nr;
 static disk_partition_t cur_part_info;
 
 #define DOS_BOOT_MAGIC_OFFSET  0x1fe
@@ -62,43 +52,12 @@ static int disk_read(__u32 block, __u32 nr_blocks, void *buf)
                        cur_part_info.start + block, nr_blocks, buf);
 }
 
-int fat_register_device(block_dev_desc_t * dev_desc, int part_no)
+int fat_set_blk_dev(block_dev_desc_t *dev_desc, disk_partition_t *info)
 {
        ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, dev_desc->blksz);
 
-       /* First close any currently found FAT filesystem */
-       cur_dev = NULL;
-
-#if (defined(CONFIG_CMD_IDE) || \
-     defined(CONFIG_CMD_SATA) || \
-     defined(CONFIG_CMD_SCSI) || \
-     defined(CONFIG_CMD_USB) || \
-     defined(CONFIG_MMC) || \
-     defined(CONFIG_SYSTEMACE) )
-
-       /* 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) {
-                       printf("** Partition %d not valid on device %d **\n",
-                                       part_no, dev_desc->dev);
-                       return -1;
-               }
-
-               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));
-       }
+       cur_dev = dev_desc;
+       cur_part_info = *info;
 
        /* Make sure it has a valid FAT header */
        if (disk_read(0, 1, buffer) != 1) {
@@ -122,6 +81,34 @@ int fat_register_device(block_dev_desc_t * dev_desc, int part_no)
        return -1;
 }
 
+int fat_register_device(block_dev_desc_t *dev_desc, int part_no)
+{
+       disk_partition_t info;
+
+       /* First close any currently found FAT filesystem */
+       cur_dev = NULL;
+
+       /* Read the partition table, if present */
+       if (get_partition_info(dev_desc, part_no, &info)) {
+               if (part_no != 0) {
+                       printf("** Partition %d not valid on device %d **\n",
+                                       part_no, dev_desc->dev);
+                       return -1;
+               }
+
+               info.start = 0;
+               info.size = dev_desc->lba;
+               info.blksz = dev_desc->blksz;
+               info.name[0] = 0;
+               info.type[0] = 0;
+               info.bootable = 0;
+#ifdef CONFIG_PARTITION_UUIDS
+               info.uuid[0] = 0;
+#endif
+       }
+
+       return fat_set_blk_dev(dev_desc, &info);
+}
 
 /*
  * Get the first occurence of a directory delimiter ('/' or '\') in a string.
@@ -207,10 +194,9 @@ static __u32 get_fatent(fsdata *mydata, __u32 entry)
                __u32 fatlength = mydata->fatlength;
                __u32 startblock = bufnum * FATBUFBLOCKS;
 
-               if (getsize > fatlength)
-                       getsize = fatlength;
+               if (startblock + getsize > fatlength)
+                       getsize = fatlength - startblock;
 
-               fatlength *= mydata->sect_size; /* We want it in bytes now */
                startblock += mydata->fat_sect; /* Offset from start of disk */
 
                if (disk_read(startblock, getsize, bufptr) < 0) {
@@ -274,7 +260,6 @@ get_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size)
 {
        __u32 idx = 0;
        __u32 startsect;
-       __u32 nr_sect;
        int ret;
 
        if (clustnum > 0) {
@@ -286,51 +271,120 @@ get_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size)
 
        debug("gc - clustnum: %d, startsect: %d\n", clustnum, startsect);
 
-       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);
-               return -1;
-       }
-       if (size % mydata->sect_size) {
+       if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
                ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
 
+               printf("FAT: Misaligned buffer address (%p)\n", buffer);
+
+               while (size >= mydata->sect_size) {
+                       ret = disk_read(startsect++, 1, tmpbuf);
+                       if (ret != 1) {
+                               debug("Error reading data (got %d)\n", ret);
+                               return -1;
+                       }
+
+                       memcpy(buffer, tmpbuf, mydata->sect_size);
+                       buffer += mydata->sect_size;
+                       size -= mydata->sect_size;
+               }
+       } else {
                idx = size / mydata->sect_size;
-               ret = disk_read(startsect + idx, 1, tmpbuf);
+               ret = disk_read(startsect, idx, buffer);
+               if (ret != idx) {
+                       debug("Error reading data (got %d)\n", ret);
+                       return -1;
+               }
+               startsect += idx;
+               idx *= mydata->sect_size;
+               buffer += idx;
+               size -= idx;
+       }
+       if (size) {
+               ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
+
+               ret = disk_read(startsect, 1, tmpbuf);
                if (ret != 1) {
                        debug("Error reading data (got %d)\n", ret);
                        return -1;
                }
-               buffer += idx * mydata->sect_size;
 
-               memcpy(buffer, tmpbuf, size % mydata->sect_size);
-               return 0;
+               memcpy(buffer, tmpbuf, size);
        }
 
        return 0;
 }
 
 /*
- * 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.
+ * Update the number of bytes read in *gotsize or return -1 on fatal errors.
  */
-static long
-get_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer,
-            unsigned long maxsize)
+__u8 get_contents_vfatname_block[MAX_CLUSTSIZE]
+       __aligned(ARCH_DMA_MINALIGN);
+
+static int get_contents(fsdata *mydata, dir_entry *dentptr, loff_t pos,
+                       __u8 *buffer, loff_t maxsize, loff_t *gotsize)
 {
-       unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0;
+       loff_t filesize = FAT2CPU32(dentptr->size);
        unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
        __u32 curclust = START(dentptr);
        __u32 endclust, newclust;
-       unsigned long actsize;
+       loff_t actsize;
 
-       debug("Filesize: %ld bytes\n", filesize);
+       *gotsize = 0;
+       debug("Filesize: %llu bytes\n", filesize);
 
-       if (maxsize > 0 && filesize > maxsize)
-               filesize = maxsize;
+       if (pos >= filesize) {
+               debug("Read position past EOF: %llu\n", pos);
+               return 0;
+       }
+
+       if (maxsize > 0 && filesize > pos + maxsize)
+               filesize = pos + maxsize;
 
-       debug("%ld bytes\n", filesize);
+       debug("%llu 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 0;
+               }
+               actsize += bytesperclust;
+       }
+
+       /* actsize > pos */
+       actsize -= bytesperclust;
+       filesize -= actsize;
+       pos -= actsize;
+
+       /* align to beginning of next cluster if any */
+       if (pos) {
+               actsize = min(filesize, (loff_t)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 0;
+               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 0;
+               }
+       }
 
        actsize = bytesperclust;
        endclust = curclust;
@@ -344,38 +398,26 @@ get_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer,
                        if (CHECK_CLUST(newclust, mydata->fatsize)) {
                                debug("curclust: 0x%x\n", newclust);
                                debug("Invalid FAT entry\n");
-                               return gotsize;
+                               return 0;
                        }
                        endclust = newclust;
                        actsize += bytesperclust;
                }
 
-               /* actsize >= file size */
-               actsize -= bytesperclust;
-
-               /* get remaining clusters */
-               if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
-                       printf("Error reading cluster\n");
-                       return -1;
-               }
-
                /* get remaining bytes */
-               gotsize += (int)actsize;
-               filesize -= actsize;
-               buffer += actsize;
                actsize = filesize;
-               if (get_cluster(mydata, endclust, buffer, (int)actsize) != 0) {
+               if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
                        printf("Error reading cluster\n");
                        return -1;
                }
-               gotsize += actsize;
-               return gotsize;
+               *gotsize += actsize;
+               return 0;
 getit:
                if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
                        printf("Error reading cluster\n");
                        return -1;
                }
-               gotsize += (int)actsize;
+               *gotsize += (int)actsize;
                filesize -= actsize;
                buffer += actsize;
 
@@ -383,14 +425,13 @@ getit:
                if (CHECK_CLUST(curclust, mydata->fatsize)) {
                        debug("curclust: 0x%x\n", curclust);
                        printf("Invalid FAT entry\n");
-                       return gotsize;
+                       return 0;
                }
                actsize = bytesperclust;
                endclust = curclust;
        } while (1);
 }
 
-#ifdef CONFIG_SUPPORT_VFAT
 /*
  * Extract the file name information from 'slotptr' into 'l_name',
  * starting at l_name[*idx].
@@ -428,9 +469,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)
@@ -469,13 +507,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)
@@ -486,7 +524,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);
                }
@@ -515,19 +553,19 @@ get_vfatname(fsdata *mydata, int curclust, __u8 *cluster,
 }
 
 /* Calculate short name checksum */
-static __u8 mkcksum(const char *str)
+static __u8 mkcksum(const char name[8], const char ext[3])
 {
        int i;
 
        __u8 ret = 0;
 
-       for (i = 0; i < 11; i++) {
-               ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + str[i];
-       }
+       for (i = 0; i < 8; i++)
+               ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + name[i];
+       for (i = 0; i < 3; i++)
+               ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + ext[i];
 
        return ret;
 }
-#endif /* CONFIG_SUPPORT_VFAT */
 
 /*
  * Get the directory entry associated with 'filename' from the directory
@@ -568,8 +606,8 @@ static dir_entry *get_dentfromdir(fsdata *mydata, int startsect,
                                continue;
                        }
                        if ((dentptr->attr & ATTR_VOLUME)) {
-#ifdef CONFIG_SUPPORT_VFAT
-                               if ((dentptr->attr & ATTR_VFAT) == ATTR_VFAT &&
+                               if (vfat_enabled &&
+                                   (dentptr->attr & ATTR_VFAT) == ATTR_VFAT &&
                                    (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
                                        prevcksum = ((dir_slot *)dentptr)->alias_checksum;
                                        get_vfatname(mydata, curclust,
@@ -595,8 +633,8 @@ static dir_entry *get_dentfromdir(fsdata *mydata, int startsect,
                                                }
                                                if (doit) {
                                                        if (dirc == ' ') {
-                                                               printf(" %8ld   %s%c\n",
-                                                                       (long)FAT2CPU32(dentptr->size),
+                                                               printf(" %8u   %s%c\n",
+                                                                      FAT2CPU32(dentptr->size),
                                                                        l_name,
                                                                        dirc);
                                                        } else {
@@ -609,9 +647,7 @@ static dir_entry *get_dentfromdir(fsdata *mydata, int startsect,
                                                continue;
                                        }
                                        debug("vfatname: |%s|\n", l_name);
-                               } else
-#endif
-                               {
+                               } else {
                                        /* Volume label or VFAT entry */
                                        dentptr++;
                                        continue;
@@ -625,13 +661,15 @@ static dir_entry *get_dentfromdir(fsdata *mydata, int startsect,
                                debug("Dentname == NULL - %d\n", i);
                                return NULL;
                        }
-#ifdef CONFIG_SUPPORT_VFAT
-                       if (dols && mkcksum(dentptr->name) == prevcksum) {
-                               prevcksum = 0xffff;
-                               dentptr++;
-                               continue;
+                       if (vfat_enabled) {
+                               __u8 csum = mkcksum(dentptr->name, dentptr->ext);
+                               if (dols && csum == prevcksum) {
+                                       prevcksum = 0xffff;
+                                       dentptr++;
+                                       continue;
+                               }
                        }
-#endif
+
                        get_name(dentptr, s_name);
                        if (dols) {
                                int isdir = (dentptr->attr & ATTR_DIR);
@@ -652,8 +690,8 @@ static dir_entry *get_dentfromdir(fsdata *mydata, int startsect,
 
                                if (doit) {
                                        if (dirc == ' ') {
-                                               printf(" %8ld   %s%c\n",
-                                                       (long)FAT2CPU32(dentptr->size),
+                                               printf(" %8u   %s%c\n",
+                                                      FAT2CPU32(dentptr->size),
                                                        s_name, dirc);
                                        } else {
                                                printf("            %s%c\n",
@@ -765,28 +803,31 @@ 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)
+int do_fat_read_at(const char *filename, loff_t pos, void *buffer,
+                  loff_t maxsize, int dols, int dogetsize, loff_t *size)
 {
        char fnamecopy[2048];
        boot_sector bs;
        volume_info volinfo;
        fsdata datablock;
        fsdata *mydata = &datablock;
-       dir_entry *dentptr;
+       dir_entry *dentptr = NULL;
        __u16 prevcksum = 0xffff;
        char *subname = "";
        __u32 cursect;
        int idx, isdir = 0;
        int files = 0, dirs = 0;
-       long ret = -1;
+       int ret = -1;
        int firsttime;
        __u32 root_cluster = 0;
+       __u32 read_blk;
        int rootdir_size = 0;
-       int j;
+       int buffer_blk_cnt;
+       int do_read;
+       __u8 *dir_ptr;
 
        if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) {
                debug("Error: reading boot sector\n");
@@ -833,9 +874,9 @@ do_fat_read(const char *filename, void *buffer, unsigned long maxsize, int dols)
                return -1;
        }
 
-#ifdef CONFIG_SUPPORT_VFAT
-       debug("VFAT Support enabled\n");
-#endif
+       if (vfat_enabled)
+               debug("VFAT Support enabled\n");
+
        debug("FAT%d, fat_sect: %d, fatlength: %d\n",
               mydata->fatsize, mydata->fat_sect, mydata->fatlength);
        debug("Rootdir begins at cluster: %d, sector: %d, offset: %x\n"
@@ -871,42 +912,79 @@ do_fat_read(const char *filename, void *buffer, unsigned long maxsize, int dols)
                isdir = 1;
        }
 
-       j = 0;
+       buffer_blk_cnt = 0;
+       firsttime = 1;
        while (1) {
                int i;
 
-               debug("FAT read sect=%d, clust_size=%d, DIRENTSPERBLOCK=%zd\n",
-                       cursect, mydata->clust_size, DIRENTSPERBLOCK);
-
-               if (disk_read(cursect,
-                               (mydata->fatsize == 32) ?
-                               (mydata->clust_size) :
-                               PREFETCH_BLOCKS,
-                               do_fat_read_block) < 0) {
-                       debug("Error: reading rootdir block\n");
-                       goto exit;
+               if (mydata->fatsize == 32 || firsttime) {
+                       dir_ptr = do_fat_read_at_block;
+                       firsttime = 0;
+               } else {
+                       /**
+                        * FAT16 sector buffer modification:
+                        * Each loop, the second buffered block is moved to
+                        * the buffer begin, and two next sectors are read
+                        * next to the previously moved one. So the sector
+                        * buffer keeps always 3 sectors for fat16.
+                        * And the current sector is the buffer second sector
+                        * beside the "firsttime" read, when it is the first one.
+                        *
+                        * PREFETCH_BLOCKS is 2 for FAT16 == loop[0:1]
+                        * n = computed root dir sector
+                        * loop |  cursect-1  | cursect    | cursect+1  |
+                        *   0  |  sector n+0 | sector n+1 | none       |
+                        *   1  |  none       | sector n+0 | sector n+1 |
+                        *   0  |  sector n+1 | sector n+2 | sector n+3 |
+                        *   1  |  sector n+3 | ...
+                       */
+                       dir_ptr = (do_fat_read_at_block + mydata->sect_size);
+                       memcpy(do_fat_read_at_block, dir_ptr, mydata->sect_size);
                }
 
-               dentptr = (dir_entry *) do_fat_read_block;
+               do_read = 1;
+
+               if (mydata->fatsize == 32 && buffer_blk_cnt)
+                       do_read = 0;
+
+               if (do_read) {
+                       read_blk = (mydata->fatsize == 32) ?
+                                   mydata->clust_size : PREFETCH_BLOCKS;
+
+                       debug("FAT read(sect=%d, cnt:%d), clust_size=%d, DIRENTSPERBLOCK=%zd\n",
+                               cursect, read_blk, mydata->clust_size, DIRENTSPERBLOCK);
+
+                       if (disk_read(cursect, read_blk, dir_ptr) < 0) {
+                               debug("Error: reading rootdir block\n");
+                               goto exit;
+                       }
+
+                       dentptr = (dir_entry *)dir_ptr;
+               }
 
                for (i = 0; i < DIRENTSPERBLOCK; i++) {
                        char s_name[14], l_name[VFAT_MAXLEN_BYTES];
+                       __u8 csum;
 
                        l_name[0] = '\0';
                        if (dentptr->name[0] == DELETED_FLAG) {
                                dentptr++;
                                continue;
                        }
-                       if ((dentptr->attr & ATTR_VOLUME)) {
-#ifdef CONFIG_SUPPORT_VFAT
-                               if ((dentptr->attr & ATTR_VFAT) == ATTR_VFAT &&
+
+                       if (vfat_enabled)
+                               csum = mkcksum(dentptr->name, dentptr->ext);
+
+                       if (dentptr->attr & ATTR_VOLUME) {
+                               if (vfat_enabled &&
+                                   (dentptr->attr & ATTR_VFAT) == ATTR_VFAT &&
                                    (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
                                        prevcksum =
                                                ((dir_slot *)dentptr)->alias_checksum;
 
                                        get_vfatname(mydata,
                                                     root_cluster,
-                                                    do_fat_read_block,
+                                                    dir_ptr,
                                                     dentptr, l_name);
 
                                        if (dols == LS_ROOT) {
@@ -928,8 +1006,8 @@ do_fat_read(const char *filename, void *buffer, unsigned long maxsize, int dols)
                                                }
                                                if (doit) {
                                                        if (dirc == ' ') {
-                                                               printf(" %8ld   %s%c\n",
-                                                                       (long)FAT2CPU32(dentptr->size),
+                                                               printf(" %8u   %s%c\n",
+                                                                      FAT2CPU32(dentptr->size),
                                                                        l_name,
                                                                        dirc);
                                                        } else {
@@ -943,9 +1021,7 @@ do_fat_read(const char *filename, void *buffer, unsigned long maxsize, int dols)
                                        }
                                        debug("Rootvfatname: |%s|\n",
                                               l_name);
-                               } else
-#endif
-                               {
+                               } else {
                                        /* Volume label or VFAT entry */
                                        dentptr++;
                                        continue;
@@ -959,14 +1035,13 @@ do_fat_read(const char *filename, void *buffer, unsigned long maxsize, int dols)
                                }
                                goto exit;
                        }
-#ifdef CONFIG_SUPPORT_VFAT
-                       else if (dols == LS_ROOT &&
-                                mkcksum(dentptr->name) == prevcksum) {
+                       else if (vfat_enabled &&
+                                dols == LS_ROOT && csum == prevcksum) {
                                prevcksum = 0xffff;
                                dentptr++;
                                continue;
                        }
-#endif
+
                        get_name(dentptr, s_name);
 
                        if (dols == LS_ROOT) {
@@ -989,8 +1064,8 @@ do_fat_read(const char *filename, void *buffer, unsigned long maxsize, int dols)
                                }
                                if (doit) {
                                        if (dirc == ' ') {
-                                               printf(" %8ld   %s%c\n",
-                                                       (long)FAT2CPU32(dentptr->size),
+                                               printf(" %8u   %s%c\n",
+                                                      FAT2CPU32(dentptr->size),
                                                        s_name, dirc);
                                        } else {
                                                printf("            %s%c\n",
@@ -1020,7 +1095,7 @@ do_fat_read(const char *filename, void *buffer, unsigned long maxsize, int dols)
 
                        goto rootdir_done;      /* We got a match */
                }
-               debug("END LOOP: j=%d   clust_size=%d\n", j,
+               debug("END LOOP: buffer_blk_cnt=%d   clust_size=%d\n", buffer_blk_cnt,
                       mydata->clust_size);
 
                /*
@@ -1028,33 +1103,38 @@ do_fat_read(const char *filename, void *buffer, unsigned long maxsize, int dols)
                 * root directory clusters when a cluster has been
                 * completely processed.
                 */
-               ++j;
-               int fat32_end = 0;
-               if ((mydata->fatsize == 32) && (j == mydata->clust_size)) {
-                       int nxtsect = 0;
-                       int nxt_clust = 0;
+               ++buffer_blk_cnt;
+               int rootdir_end = 0;
+               if (mydata->fatsize == 32) {
+                       if (buffer_blk_cnt == mydata->clust_size) {
+                               int nxtsect = 0;
+                               int nxt_clust = 0;
 
-                       nxt_clust = get_fatent(mydata, root_cluster);
-                       fat32_end = CHECK_CLUST(nxt_clust, 32);
+                               nxt_clust = get_fatent(mydata, root_cluster);
+                               rootdir_end = CHECK_CLUST(nxt_clust, 32);
 
-                       nxtsect = mydata->data_begin +
-                               (nxt_clust * mydata->clust_size);
+                               nxtsect = mydata->data_begin +
+                                       (nxt_clust * mydata->clust_size);
 
-                       root_cluster = nxt_clust;
+                               root_cluster = nxt_clust;
 
-                       cursect = nxtsect;
-                       j = 0;
+                               cursect = nxtsect;
+                               buffer_blk_cnt = 0;
+                       }
                } else {
-                       cursect++;
+                       if (buffer_blk_cnt == PREFETCH_BLOCKS)
+                               buffer_blk_cnt = 0;
+
+                       rootdir_end = (++cursect - mydata->rootdir_sect >=
+                                      rootdir_size);
                }
 
                /* If end of rootdir reached */
-               if ((mydata->fatsize == 32 && fat32_end) ||
-                   (mydata->fatsize != 32 && j == rootdir_size)) {
+               if (rootdir_end) {
                        if (dols == LS_ROOT) {
                                printf("\n%d file(s), %d dir(s)\n\n",
                                       files, dirs);
-                               ret = 0;
+                               *size = 0;
                        }
                        goto exit;
                }
@@ -1093,25 +1173,36 @@ rootdir_done:
                if (get_dentfromdir(mydata, startsect, subname, dentptr,
                                     isdir ? 0 : dols) == NULL) {
                        if (dols && !isdir)
-                               ret = 0;
+                               *size = 0;
                        goto exit;
                }
 
-               if (idx >= 0) {
-                       if (!(dentptr->attr & ATTR_DIR))
-                               goto exit;
+               if (isdir && !(dentptr->attr & ATTR_DIR))
+                       goto exit;
+
+               if (idx >= 0)
                        subname = nextname;
-               }
        }
 
-       ret = get_contents(mydata, dentptr, buffer, maxsize);
-       debug("Size: %d, got: %ld\n", FAT2CPU32(dentptr->size), ret);
+       if (dogetsize) {
+               *size = FAT2CPU32(dentptr->size);
+               ret = 0;
+       } else {
+               ret = get_contents(mydata, dentptr, pos, buffer, maxsize, size);
+       }
+       debug("Size: %u, got: %llu\n", FAT2CPU32(dentptr->size), *size);
 
 exit:
        free(mydata->fatbuf);
        return ret;
 }
 
+int do_fat_read(const char *filename, void *buffer, loff_t maxsize, int dols,
+               loff_t *actread)
+{
+       return do_fat_read_at(filename, 0, buffer, maxsize, dols, 0, actread);
+}
+
 int file_fat_detectfs(void)
 {
        boot_sector bs;
@@ -1169,19 +1260,64 @@ int file_fat_detectfs(void)
        vol_label[11] = '\0';
        volinfo.fs_type[5] = '\0';
 
-       printf("Partition %d: Filesystem: %s \"%s\"\n", cur_part_nr,
-               volinfo.fs_type, vol_label);
+       printf("Filesystem: %s \"%s\"\n", volinfo.fs_type, vol_label);
 
        return 0;
 }
 
 int file_fat_ls(const char *dir)
 {
-       return do_fat_read(dir, NULL, 0, LS_YES);
+       loff_t size;
+
+       return do_fat_read(dir, NULL, 0, LS_YES, &size);
 }
 
-long file_fat_read(const char *filename, void *buffer, unsigned long maxsize)
+int fat_exists(const char *filename)
+{
+       int ret;
+       loff_t size;
+
+       ret = do_fat_read_at(filename, 0, NULL, 0, LS_NO, 1, &size);
+       return ret == 0;
+}
+
+int fat_size(const char *filename, loff_t *size)
+{
+       return do_fat_read_at(filename, 0, NULL, 0, LS_NO, 1, size);
+}
+
+int file_fat_read_at(const char *filename, loff_t pos, void *buffer,
+                    loff_t maxsize, loff_t *actread)
 {
        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, 0,
+                             actread);
+}
+
+int file_fat_read(const char *filename, void *buffer, int maxsize)
+{
+       loff_t actread;
+       int ret;
+
+       ret =  file_fat_read_at(filename, 0, buffer, maxsize, &actread);
+       if (ret)
+               return ret;
+       else
+               return actread;
+}
+
+int fat_read_file(const char *filename, void *buf, loff_t offset, loff_t len,
+                 loff_t *actread)
+{
+       int ret;
+
+       ret = file_fat_read_at(filename, offset, buf, len, actread);
+       if (ret)
+               printf("** Unable to read file %s **\n", filename);
+
+       return ret;
+}
+
+void fat_close(void)
+{
 }