fat: fix crash with big sector size

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>
diff --git a/fs/fat/fat.c b/fs/fat/fat.c
index c450bf6..a344469 100644
--- a/fs/fat/fat.c
+++ b/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 @@
 
 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 @@
 
 	/* 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 @@
 
 	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 @@
 	      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 @@
 	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 @@
 		}
 
 		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 @@
 		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 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 @@
 
 	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 @@
 	__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 @@
 	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 @@
 		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 @@
 	       "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 @@
 
 	if (*fnamecopy == '\0') {
 		if (!dols)
-			return -1;
+			goto exit;
 
 		dols = LS_ROOT;
 	} else if ((idx = dirdelim(fnamecopy)) >= 0) {
@@ -857,10 +879,10 @@
 		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 @@
 				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 @@
 			}
 
 			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 @@
 			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 @@
 		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 @@
 	ret = get_contents(mydata, dentptr, buffer, maxsize);
 	debug("Size: %d, got: %ld\n", FAT2CPU32(dentptr->size), ret);
 
+exit:
+	free(mydata->fatbuf);
 	return ret;
 }