[U-Boot] [PATCH 1/2] fat: fix crash with big sector size

Sergei Shtylyov sshtylyov at ru.mvista.com
Mon Aug 8 21:38:33 CEST 2011


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 at mvista.com>

---
 fs/fat/fat.c  |   97 +++++++++++++++++++++++++++++++++++-----------------------
 include/fat.h |   20 ++++-------
 2 files changed, 67 insertions(+), 50 deletions(-)

Index: u-boot/fs/fat/fat.c
===================================================================
--- u-boot.orig/fs/fat/fat.c
+++ u-boot/fs/fat/fat.c
@@ -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, 
 
 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,
 
 	/* 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 clust
 
 	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 
 	      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 curclu
 	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 curclu
 		}
 
 		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 (fsdat
 		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 (fsdat
 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, vol
 
 	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 
 	__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 
 	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 
 		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 
 	       "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 
 
 	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 
 		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 
 				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 
 			}
 
 			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 
 			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: u-boot/include/fat.h
===================================================================
--- u-boot.orig/include/fat.h
+++ u-boot/include/fat.h
@@ -33,22 +33,15 @@
 /* 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 */


More information about the U-Boot mailing list