nand: Extend nand_(read|write)_skip_bad with *actual and limit parameters

We make these two functions take a size_t pointer to how much space
was used on NAND to read or write the buffer (when reads/writes happen)
so that bad blocks can be accounted for.  We also make them take an
loff_t limit on how much data can be read or written.  This means that
we can now catch the case of when writing to a partition would exceed
the partition size due to bad blocks.  To do this we also need to make
check_skip_len count not just complete blocks used but partial ones as
well.  All callers of nand_(read|write)_skip_bad are adjusted to call
these with the most sensible limits available.

The changes were started by Pantelis and finished by Tom.

Signed-off-by: Pantelis Antoniou <panto@antoniou-consulting.com>
Signed-off-by: Tom Rini <trini@ti.com>
diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c
index ff2d348..4727f9c 100644
--- a/drivers/mtd/nand/nand_util.c
+++ b/drivers/mtd/nand/nand_util.c
@@ -416,11 +416,13 @@
  * @param nand NAND device
  * @param offset offset in flash
  * @param length image length
+ * @param used length of flash needed for the requested length
  * @return 0 if the image fits and there are no bad blocks
  *         1 if the image fits, but there are bad blocks
  *        -1 if the image does not fit
  */
-static int check_skip_len(nand_info_t *nand, loff_t offset, size_t length)
+static int check_skip_len(nand_info_t *nand, loff_t offset, size_t length,
+		size_t *used)
 {
 	size_t len_excl_bad = 0;
 	int ret = 0;
@@ -442,8 +444,13 @@
 			ret = 1;
 
 		offset += block_len;
+		*used += block_len;
 	}
 
+	/* If the length is not a multiple of block_len, adjust. */
+	if (len_excl_bad > length)
+		*used -= (len_excl_bad - length);
+
 	return ret;
 }
 
@@ -476,23 +483,36 @@
  * Write image to NAND flash.
  * Blocks that are marked bad are skipped and the is written to the next
  * block instead as long as the image is short enough to fit even after
- * skipping the bad blocks.
+ * skipping the bad blocks.  Due to bad blocks we may not be able to
+ * perform the requested write.  In the case where the write would
+ * extend beyond the end of the NAND device, both length and actual (if
+ * not NULL) are set to 0.  In the case where the write would extend
+ * beyond the limit we are passed, length is set to 0 and actual is set
+ * to the required length.
  *
  * @param nand  	NAND device
  * @param offset	offset in flash
  * @param length	buffer length
+ * @param actual	set to size required to write length worth of
+ *			buffer or 0 on error, if not NULL
+ * @param lim		maximum size that actual may be in order to not
+ *			exceed the buffer
  * @param buffer        buffer to read from
  * @param flags		flags modifying the behaviour of the write to NAND
  * @return		0 in case of success
  */
 int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
-			u_char *buffer, int flags)
+		size_t *actual, loff_t lim, u_char *buffer, int flags)
 {
 	int rval = 0, blocksize;
 	size_t left_to_write = *length;
+	size_t used_for_write = 0;
 	u_char *p_buffer = buffer;
 	int need_skip;
 
+	if (actual)
+		*actual = 0;
+
 #ifdef CONFIG_CMD_NAND_YAFFS
 	if (flags & WITH_YAFFS_OOB) {
 		if (flags & ~WITH_YAFFS_OOB)
@@ -529,13 +549,23 @@
 		return -EINVAL;
 	}
 
+	need_skip = check_skip_len(nand, offset, *length, &used_for_write);
+
-	need_skip = check_skip_len(nand, offset, *length);
+	if (actual)
+		*actual = used_for_write;
+
 	if (need_skip < 0) {
 		printf("Attempt to write outside the flash area\n");
 		*length = 0;
 		return -EINVAL;
 	}
 
+	if (used_for_write > lim) {
+		puts("Size of write exceeds partition or device limit\n");
+		*length = 0;
+		return -EFBIG;
+	}
+
 	if (!need_skip && !(flags & WITH_DROP_FFS)) {
 		rval = nand_write(nand, offset, length, buffer);
 		if (rval == 0)
@@ -626,36 +656,58 @@
  *
  * Read image from NAND flash.
  * Blocks that are marked bad are skipped and the next block is read
- * instead as long as the image is short enough to fit even after skipping the
- * bad blocks.
+ * instead as long as the image is short enough to fit even after
+ * skipping the bad blocks.  Due to bad blocks we may not be able to
+ * perform the requested read.  In the case where the read would extend
+ * beyond the end of the NAND device, both length and actual (if not
+ * NULL) are set to 0.  In the case where the read would extend beyond
+ * the limit we are passed, length is set to 0 and actual is set to the
+ * required length.
  *
  * @param nand NAND device
  * @param offset offset in flash
  * @param length buffer length, on return holds number of read bytes
+ * @param actual set to size required to read length worth of buffer or 0
+ * on error, if not NULL
+ * @param lim maximum size that actual may be in order to not exceed the
+ * buffer
  * @param buffer buffer to write to
  * @return 0 in case of success
  */
 int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
-		       u_char *buffer)
+		size_t *actual, loff_t lim, u_char *buffer)
 {
 	int rval;
 	size_t left_to_read = *length;
+	size_t used_for_read = 0;
 	u_char *p_buffer = buffer;
 	int need_skip;
 
 	if ((offset & (nand->writesize - 1)) != 0) {
 		printf("Attempt to read non page-aligned data\n");
 		*length = 0;
+		if (actual)
+			*actual = 0;
 		return -EINVAL;
 	}
 
+	need_skip = check_skip_len(nand, offset, *length, &used_for_read);
+
+	if (actual)
+		*actual = used_for_read;
+
-	need_skip = check_skip_len(nand, offset, *length);
 	if (need_skip < 0) {
 		printf("Attempt to read outside the flash area\n");
 		*length = 0;
 		return -EINVAL;
 	}
 
+	if (used_for_read > lim) {
+		puts("Size of read exceeds partition or device limit\n");
+		*length = 0;
+		return -EFBIG;
+	}
+
 	if (!need_skip) {
 		rval = nand_read(nand, offset, length, buffer);
 		if (!rval || rval == -EUCLEAN)