fat: root directory cluster only makes sense for FAT32
[oweals/u-boot.git] / fs / fat / fat.c
index a75e4f258aa1ec768f28d3fe3f8ac2a1bcc9a87d..aa0be18b8f3c3b5671888bcf93c3502720c6642f 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <common.h>
 #include <config.h>
+#include <exports.h>
 #include <fat.h>
 #include <asm/byteorder.h>
 #include <part.h>
@@ -69,8 +70,7 @@ static int disk_read (__u32 startblock, __u32 getsize, __u8 * bufptr)
 
 int fat_register_device (block_dev_desc_t * dev_desc, int part_no)
 {
-       unsigned char buffer[SECTOR_SIZE];
-
+       unsigned char buffer[dev_desc->blksz];
        disk_partition_t info;
 
        if (!dev_desc->block_read)
@@ -209,16 +209,17 @@ static __u32 get_fatent (fsdata *mydata, __u32 entry)
 
        /* Read a new block of FAT entries into the cache. */
        if (bufnum != mydata->fatbufnum) {
-               int getsize = FATBUFSIZE / FS_BLOCK_SIZE;
+               __u32 getsize = FATBUFBLOCKS;
                __u8 *bufptr = mydata->fatbuf;
                __u32 fatlength = mydata->fatlength;
                __u32 startblock = bufnum * FATBUFBLOCKS;
 
-               fatlength *= SECTOR_SIZE;       /* We want it in bytes now */
-               startblock += mydata->fat_sect; /* Offset from start of disk */
-
                if (getsize > fatlength)
                        getsize = fatlength;
+
+               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) {
                        debug("Error reading FAT blocks\n");
                        return ret;
@@ -279,7 +280,7 @@ static int
 get_cluster (fsdata *mydata, __u32 clustnum, __u8 *buffer,
             unsigned long size)
 {
-       int idx = 0;
+       __u32 idx = 0;
        __u32 startsect;
 
        if (clustnum > 0) {
@@ -291,21 +292,21 @@ get_cluster (fsdata *mydata, __u32 clustnum, __u8 *buffer,
 
        debug("gc - clustnum: %d, startsect: %d\n", clustnum, startsect);
 
-       if (disk_read(startsect, size / FS_BLOCK_SIZE, buffer) < 0) {
+       if (disk_read(startsect, size / mydata->sect_size, buffer) < 0) {
                debug("Error reading data\n");
                return -1;
        }
-       if (size % FS_BLOCK_SIZE) {
-               __u8 tmpbuf[FS_BLOCK_SIZE];
+       if (size % mydata->sect_size) {
+               __u8 tmpbuf[mydata->sect_size];
 
-               idx = size / FS_BLOCK_SIZE;
+               idx = size / mydata->sect_size;
                if (disk_read(startsect + idx, 1, tmpbuf) < 0) {
                        debug("Error reading data\n");
                        return -1;
                }
-               buffer += idx * FS_BLOCK_SIZE;
+               buffer += idx * mydata->sect_size;
 
-               memcpy(buffer, tmpbuf, size % FS_BLOCK_SIZE);
+               memcpy(buffer, tmpbuf, size % mydata->sect_size);
                return 0;
        }
 
@@ -322,7 +323,7 @@ get_contents (fsdata *mydata, dir_entry *dentptr, __u8 *buffer,
              unsigned long maxsize)
 {
        unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0;
-       unsigned int bytesperclust = mydata->clust_size * SECTOR_SIZE;
+       unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
        __u32 curclust = START(dentptr);
        __u32 endclust, newclust;
        unsigned long actsize;
@@ -441,7 +442,7 @@ get_vfatname (fsdata *mydata, int curclust, __u8 *cluster,
        dir_slot *slotptr = (dir_slot *)retdent;
        __u8 *buflimit = cluster + ((curclust == 0) ?
                                        LINEAR_PREFETCH_SIZE :
-                                       (mydata->clust_size * SECTOR_SIZE)
+                                       (mydata->clust_size * mydata->sect_size)
                                   );
        __u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff;
        int idx = 0;
@@ -473,7 +474,7 @@ get_vfatname (fsdata *mydata, int curclust, __u8 *cluster,
                }
 
                if (get_cluster(mydata, curclust, get_vfatname_block,
-                               mydata->clust_size * SECTOR_SIZE) != 0) {
+                               mydata->clust_size * mydata->sect_size) != 0) {
                        debug("Error: reading directory block\n");
                        return -1;
                }
@@ -555,7 +556,7 @@ static dir_entry *get_dentfromdir (fsdata *mydata, int startsect,
                int i;
 
                if (get_cluster(mydata, curclust, get_dentfromdir_block,
-                               mydata->clust_size * SECTOR_SIZE) != 0) {
+                               mydata->clust_size * mydata->sect_size) != 0) {
                        debug("Error: reading directory block\n");
                        return NULL;
                }
@@ -702,13 +703,24 @@ static dir_entry *get_dentfromdir (fsdata *mydata, int startsect,
 static int
 read_bootsectandvi (boot_sector *bs, volume_info *volinfo, int *fatsize)
 {
-       __u8 block[FS_BLOCK_SIZE];
-
+       __u8 *block;
        volume_info *vistart;
+       int ret = 0;
+
+       if (cur_dev == NULL) {
+               debug("Error: no device selected\n");
+               return -1;
+       }
+
+       block = malloc(cur_dev->blksz);
+       if (block == NULL) {
+               debug("Error: allocating block\n");
+               return -1;
+       }
 
        if (disk_read (0, 1, block) < 0) {
                debug("Error: reading block\n");
-               return -1;
+               goto fail;
        }
 
        memcpy(bs, block, sizeof(boot_sector));
@@ -736,20 +748,24 @@ read_bootsectandvi (boot_sector *bs, volume_info *volinfo, int *fatsize)
 
        if (*fatsize == 32) {
                if (strncmp(FAT32_SIGN, vistart->fs_type, SIGNLEN) == 0)
-                       return 0;
+                       goto exit;
        } else {
                if (strncmp(FAT12_SIGN, vistart->fs_type, SIGNLEN) == 0) {
                        *fatsize = 12;
-                       return 0;
+                       goto exit;
                }
                if (strncmp(FAT16_SIGN, vistart->fs_type, SIGNLEN) == 0) {
                        *fatsize = 16;
-                       return 0;
+                       goto exit;
                }
        }
 
        debug("Error: broken fs_type sign\n");
-       return -1;
+fail:
+       ret = -1;
+exit:
+       free(block);
+       return ret;
 }
 
 __attribute__ ((__aligned__ (__alignof__ (dir_entry))))
@@ -767,12 +783,13 @@ do_fat_read (const char *filename, void *buffer, unsigned long maxsize,
        dir_entry *dentptr;
        __u16 prevcksum = 0xffff;
        char *subname = "";
-       int cursect;
+       __u32 cursect;
        int idx, isdir = 0;
        int files = 0, dirs = 0;
-       long ret = 0;
+       long ret = -1;
        int firsttime;
-       int root_cluster;
+       __u32 root_cluster = 0;
+       int rootdir_size = 0;
        int j;
 
        if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) {
@@ -780,36 +797,40 @@ do_fat_read (const char *filename, void *buffer, unsigned long maxsize,
                return -1;
        }
 
-       root_cluster = bs.root_cluster;
-
-       if (mydata->fatsize == 32)
+       if (mydata->fatsize == 32) {
+               root_cluster = bs.root_cluster;
                mydata->fatlength = bs.fat32_length;
-       else
+       } else {
                mydata->fatlength = bs.fat_length;
+       }
 
        mydata->fat_sect = bs.reserved;
 
        cursect = mydata->rootdir_sect
                = mydata->fat_sect + mydata->fatlength * bs.fats;
 
+       mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0];
        mydata->clust_size = bs.cluster_size;
 
        if (mydata->fatsize == 32) {
                mydata->data_begin = mydata->rootdir_sect -
                                        (mydata->clust_size * 2);
        } else {
-               int rootdir_size;
-
                rootdir_size = ((bs.dir_entries[1]  * (int)256 +
                                 bs.dir_entries[0]) *
                                 sizeof(dir_entry)) /
-                                SECTOR_SIZE;
+                                mydata->sect_size;
                mydata->data_begin = mydata->rootdir_sect +
                                        rootdir_size -
                                        (mydata->clust_size * 2);
        }
 
        mydata->fatbufnum = -1;
+       mydata->fatbuf = malloc(FATBUFSIZE);
+       if (mydata->fatbuf == NULL) {
+               debug("Error: allocating memory\n");
+               return -1;
+       }
 
 #ifdef CONFIG_SUPPORT_VFAT
        debug("VFAT Support enabled\n");
@@ -820,8 +841,9 @@ do_fat_read (const char *filename, void *buffer, unsigned long maxsize,
               "Data begins at: %d\n",
               root_cluster,
               mydata->rootdir_sect,
-              mydata->rootdir_sect * SECTOR_SIZE, mydata->data_begin);
-       debug("Cluster size: %d\n", mydata->clust_size);
+              mydata->rootdir_sect * mydata->sect_size, mydata->data_begin);
+       debug("Sector size: %d, cluster size: %d\n", mydata->sect_size,
+             mydata->clust_size);
 
        /* "cwd" is always the root... */
        while (ISDIRDELIM(*filename))
@@ -833,7 +855,7 @@ do_fat_read (const char *filename, void *buffer, unsigned long maxsize,
 
        if (*fnamecopy == '\0') {
                if (!dols)
-                       return -1;
+                       goto exit;
 
                dols = LS_ROOT;
        } else if ((idx = dirdelim(fnamecopy)) >= 0) {
@@ -858,10 +880,10 @@ do_fat_read (const char *filename, void *buffer, unsigned long maxsize,
                if (disk_read(cursect,
                                (mydata->fatsize == 32) ?
                                (mydata->clust_size) :
-                               LINEAR_PREFETCH_SIZE / SECTOR_SIZE,
+                               LINEAR_PREFETCH_SIZE / mydata->sect_size,
                                do_fat_read_block) < 0) {
                        debug("Error: reading rootdir block\n");
-                       return -1;
+                       goto exit;
                }
 
                dentptr = (dir_entry *) do_fat_read_block;
@@ -882,9 +904,7 @@ do_fat_read (const char *filename, void *buffer, unsigned long maxsize,
                                                ((dir_slot *)dentptr)->alias_checksum;
 
                                        get_vfatname(mydata,
-                                                    (mydata->fatsize == 32) ?
-                                                    root_cluster :
-                                                    0,
+                                                    root_cluster,
                                                     do_fat_read_block,
                                                     dentptr, l_name);
 
@@ -934,9 +954,9 @@ do_fat_read (const char *filename, void *buffer, unsigned long maxsize,
                                if (dols == LS_ROOT) {
                                        printf("\n%d file(s), %d dir(s)\n\n",
                                                files, dirs);
-                                       return 0;
+                                       ret = 0;
                                }
-                               return -1;
+                               goto exit;
                        }
 #ifdef CONFIG_SUPPORT_VFAT
                        else if (dols == LS_ROOT &&
@@ -988,7 +1008,7 @@ do_fat_read (const char *filename, void *buffer, unsigned long maxsize,
                        }
 
                        if (isdir && !(dentptr->attr & ATTR_DIR))
-                               return -1;
+                               goto exit;
 
                        debug("RootName: %s", s_name);
                        debug(", start: 0x%x", START(dentptr));
@@ -1006,20 +1026,18 @@ do_fat_read (const char *filename, void *buffer, unsigned long maxsize,
                 * root directory clusters when a cluster has been
                 * completely processed.
                 */
-               if ((mydata->fatsize == 32) && (++j == mydata->clust_size)) {
-                       int nxtsect;
-                       int nxt_clust;
+               ++j;
+               int fat32_end = 0;
+               if ((mydata->fatsize == 32) && (j == mydata->clust_size)) {
+                       int nxtsect = 0;
+                       int nxt_clust = 0;
 
                        nxt_clust = get_fatent(mydata, root_cluster);
+                       fat32_end = CHECK_CLUST(nxt_clust, 32);
 
                        nxtsect = mydata->data_begin +
                                (nxt_clust * mydata->clust_size);
 
-                       debug("END LOOP: sect=%d, root_clust=%d, "
-                             "n_sect=%d, n_clust=%d\n",
-                             cursect, root_cluster,
-                             nxtsect, nxt_clust);
-
                        root_cluster = nxt_clust;
 
                        cursect = nxtsect;
@@ -1027,6 +1045,17 @@ do_fat_read (const char *filename, void *buffer, unsigned long maxsize,
                } else {
                        cursect++;
                }
+
+               /* 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);
+                               ret = 0;
+                       }
+                       goto exit;
+               }
        }
 rootdir_done:
 
@@ -1062,13 +1091,13 @@ rootdir_done:
                if (get_dentfromdir(mydata, startsect, subname, dentptr,
                                     isdir ? 0 : dols) == NULL) {
                        if (dols && !isdir)
-                               return 0;
-                       return -1;
+                               ret = 0;
+                       goto exit;
                }
 
                if (idx >= 0) {
                        if (!(dentptr->attr & ATTR_DIR))
-                               return -1;
+                               goto exit;
                        subname = nextname;
                }
        }
@@ -1076,6 +1105,8 @@ rootdir_done:
        ret = get_contents(mydata, dentptr, buffer, maxsize);
        debug("Size: %d, got: %ld\n", FAT2CPU32(dentptr->size), ret);
 
+exit:
+       free(mydata->fatbuf);
        return ret;
 }