fat: fix crash with big sector size
authorSergei Shtylyov <sshtylyov@ru.mvista.com>
Mon, 8 Aug 2011 09:38:33 +0000 (09:38 +0000)
committerWolfgang Denk <wd@denx.de>
Sat, 1 Oct 2011 19:50:39 +0000 (21:50 +0200)
Apple iPod nanos have sector sizes of 2 or 4 KiB, which crashes U-Boot when it
tries to read the boot sector into 512-byte buffer situated on stack. Make the
FAT code indifferent to the sector size.

Signed-off-by: Sergei Shtylyov <sshtylyov@mvista.com>
fs/fat/fat.c
include/fat.h

index c450bf6..a344469 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,12 +209,12 @@ static __u32 get_fatent (fsdata *mydata, __u32 entry)
 
        /* Read a new block of FAT entries into the cache. */
        if (bufnum != mydata->fatbufnum) {
-               __u32 getsize = FATBUFSIZE / FS_BLOCK_SIZE;
+               __u32 getsize = FATBUFSIZE / mydata->sect_size;
                __u8 *bufptr = mydata->fatbuf;
                __u32 fatlength = mydata->fatlength;
                __u32 startblock = bufnum * FATBUFBLOCKS;
 
-               fatlength *= SECTOR_SIZE;       /* We want it in bytes now */
+               fatlength *= mydata->sect_size; /* We want it in bytes now */
                startblock += mydata->fat_sect; /* Offset from start of disk */
 
                if (getsize > fatlength)
@@ -291,21 +291,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 +322,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 +441,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 +473,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 +555,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 +702,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 +747,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))))
@@ -770,7 +785,7 @@ do_fat_read (const char *filename, void *buffer, unsigned long maxsize,
        __u32 cursect;
        int idx, isdir = 0;
        int files = 0, dirs = 0;
-       long ret = 0;
+       long ret = -1;
        int firsttime;
        __u32 root_cluster;
        int rootdir_size = 0;
@@ -793,6 +808,7 @@ do_fat_read (const char *filename, void *buffer, unsigned long maxsize,
        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) {
@@ -802,13 +818,18 @@ do_fat_read (const char *filename, void *buffer, unsigned long maxsize,
                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");
@@ -819,8 +840,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))
@@ -832,7 +854,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) {
@@ -857,10 +879,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;
@@ -933,9 +955,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 &&
@@ -987,7 +1009,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));
@@ -1031,10 +1053,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;
-                       } else {
-                               return -1;
+                               ret = 0;
                        }
+                       goto exit;
                }
        }
 rootdir_done:
@@ -1071,13 +1092,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;
                }
        }
@@ -1085,6 +1106,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;
 }
 
index afb2116..c2465d2 100644 (file)
 /* Maximum Long File Name length supported here is 128 UTF-16 code units */
 #define VFAT_MAXLEN_BYTES      256 /* Maximum LFN buffer in bytes */
 #define VFAT_MAXSEQ            9   /* Up to 9 of 13 2-byte UTF-16 entries */
-#define LINEAR_PREFETCH_SIZE   (SECTOR_SIZE*2) /* Prefetch buffer size */
-
-#define SECTOR_SIZE FS_BLOCK_SIZE
-
-#define FS_BLOCK_SIZE  512
-
-#if FS_BLOCK_SIZE != SECTOR_SIZE
-#error FS_BLOCK_SIZE != SECTOR_SIZE - This code needs to be fixed!
-#endif
+#define LINEAR_PREFETCH_SIZE   (mydata->sect_size*2) /* Prefetch buffer size */
 
 #define MAX_CLUSTSIZE  65536
-#define DIRENTSPERBLOCK        (FS_BLOCK_SIZE/sizeof(dir_entry))
-#define DIRENTSPERCLUST        ((mydata->clust_size*SECTOR_SIZE)/sizeof(dir_entry))
+#define DIRENTSPERBLOCK        (mydata->sect_size / sizeof(dir_entry))
+#define DIRENTSPERCLUST        ((mydata->clust_size * mydata->sect_size) / \
+                        sizeof(dir_entry))
 
 #define FATBUFBLOCKS   6
-#define FATBUFSIZE     (FS_BLOCK_SIZE*FATBUFBLOCKS)
+#define FATBUFSIZE     (mydata->sect_size * FATBUFBLOCKS)
 #define FAT12BUFSIZE   ((FATBUFSIZE*2)/3)
 #define FAT16BUFSIZE   (FATBUFSIZE/2)
 #define FAT32BUFSIZE   (FATBUFSIZE/4)
@@ -181,11 +174,12 @@ typedef struct dir_slot {
  * (see FAT32 accesses)
  */
 typedef struct {
-       __u8    fatbuf[FATBUFSIZE]; /* Current FAT buffer */
+       __u8    *fatbuf;        /* Current FAT buffer */
        int     fatsize;        /* Size of FAT in bits */
        __u16   fatlength;      /* Length of FAT in sectors */
        __u16   fat_sect;       /* Starting sector of the FAT */
        __u16   rootdir_sect;   /* Start sector of root directory */
+       __u16   sect_size;      /* Size of sectors in bytes */
        __u16   clust_size;     /* Size of clusters in sectors */
        short   data_begin;     /* The sector of the first cluster, can be negative */
        int     fatbufnum;      /* Used by get_fatent, init to -1 */