am 22d79a5c: make offsets in firmware update header not point to bad blocks

Merge commit '22d79a5c5eab9c1e86ff2af210bb072689e2d630' into eclair-plus-aosp

* commit '22d79a5c5eab9c1e86ff2af210bb072689e2d630':
  make offsets in firmware update header not point to bad blocks
diff --git a/bootloader.c b/bootloader.c
index de441e1..bc79bee 100644
--- a/bootloader.c
+++ b/bootloader.c
@@ -198,7 +198,7 @@
     header.version = UPDATE_VERSION;
     header.size = header_size;
 
-    header.image_offset = mtd_erase_blocks(write, 0);
+    off_t image_start_pos = mtd_erase_blocks(write, 0);
     header.image_length = update_length;
     if ((int) header.image_offset == -1 ||
         mtd_write_data(write, update, update_length) != update_length) {
@@ -206,6 +206,8 @@
         mtd_write_close(write);
         return -1;
     }
+    off_t busy_start_pos = mtd_erase_blocks(write, 0);
+    header.image_offset = mtd_find_write_start(write, image_start_pos);
 
     header.bitmap_width = bitmap_width;
     header.bitmap_height = bitmap_height;
@@ -213,7 +215,6 @@
 
     int bitmap_length = (bitmap_bpp + 7) / 8 * bitmap_width * bitmap_height;
 
-    header.busy_bitmap_offset = mtd_erase_blocks(write, 0);
     header.busy_bitmap_length = busy_bitmap != NULL ? bitmap_length : 0;
     if ((int) header.busy_bitmap_offset == -1 ||
         mtd_write_data(write, busy_bitmap, bitmap_length) != bitmap_length) {
@@ -221,8 +222,9 @@
         mtd_write_close(write);
         return -1;
     }
+    off_t fail_start_pos = mtd_erase_blocks(write, 0);
+    header.busy_bitmap_offset = mtd_find_write_start(write, busy_start_pos);
 
-    header.fail_bitmap_offset = mtd_erase_blocks(write, 0);
     header.fail_bitmap_length = fail_bitmap != NULL ? bitmap_length : 0;
     if ((int) header.fail_bitmap_offset == -1 ||
         mtd_write_data(write, fail_bitmap, bitmap_length) != bitmap_length) {
@@ -230,6 +232,8 @@
         mtd_write_close(write);
         return -1;
     }
+    mtd_erase_blocks(write, 0);
+    header.fail_bitmap_offset = mtd_find_write_start(write, fail_start_pos);
 
     /* Write the header last, after all the blocks it refers to, so that
      * when the magic number is installed everything is valid.
@@ -252,7 +256,7 @@
         return -1;
     }
 
-    if (mtd_erase_blocks(write, 0) != (off_t) header.image_offset) {
+    if (mtd_erase_blocks(write, 0) != image_start_pos) {
         LOGE("Misalignment rewriting %s\n(%s)\n", CACHE_NAME, strerror(errno));
         mtd_write_close(write);
         return -1;
diff --git a/mtdutils/mtdutils.c b/mtdutils/mtdutils.c
index 8d32520..18e6a5d 100644
--- a/mtdutils/mtdutils.c
+++ b/mtdutils/mtdutils.c
@@ -47,6 +47,10 @@
     char *buffer;
     size_t stored;
     int fd;
+
+    off_t* bad_block_offsets;
+    int bad_block_alloc;
+    int bad_block_count;
 };
 
 typedef struct {
@@ -366,6 +370,10 @@
     MtdWriteContext *ctx = (MtdWriteContext*) malloc(sizeof(MtdWriteContext));
     if (ctx == NULL) return NULL;
 
+    ctx->bad_block_offsets = NULL;
+    ctx->bad_block_alloc = 0;
+    ctx->bad_block_count = 0;
+
     ctx->buffer = malloc(partition->erase_size);
     if (ctx->buffer == NULL) {
         free(ctx);
@@ -386,8 +394,20 @@
     return ctx;
 }
 
-static int write_block(const MtdPartition *partition, int fd, const char *data)
+static void add_bad_block_offset(MtdWriteContext *ctx, off_t pos) {
+    if (ctx->bad_block_count + 1 > ctx->bad_block_alloc) {
+        ctx->bad_block_alloc = (ctx->bad_block_alloc*2) + 1;
+        ctx->bad_block_offsets = realloc(ctx->bad_block_offsets,
+                                         ctx->bad_block_alloc * sizeof(off_t));
+    }
+    ctx->bad_block_offsets[ctx->bad_block_count++] = pos;
+}
+
+static int write_block(MtdWriteContext *ctx, const char *data)
 {
+    const MtdPartition *partition = ctx->partition;
+    int fd = ctx->fd;
+
     off_t pos = lseek(fd, 0, SEEK_CUR);
     if (pos == (off_t) -1) return 1;
 
@@ -395,6 +415,7 @@
     while (pos + size <= (int) partition->size) {
         loff_t bpos = pos;
         if (ioctl(fd, MEMGETBADBLOCK, &bpos) > 0) {
+            add_bad_block_offset(ctx, pos);
             fprintf(stderr, "mtd: not writing bad block at 0x%08lx\n", pos);
             pos += partition->erase_size;
             continue;  // Don't try to erase known factory-bad blocks.
@@ -436,6 +457,7 @@
         }
 
         // Try to erase it once more as we give up on this block
+        add_bad_block_offset(ctx, pos);
         fprintf(stderr, "mtd: skipping write block at 0x%08lx\n", pos);
         ioctl(fd, MEMERASE, &erase_info);
         pos += partition->erase_size;
@@ -461,13 +483,13 @@
 
         // If a complete block was accumulated, write it
         if (ctx->stored == ctx->partition->erase_size) {
-            if (write_block(ctx->partition, ctx->fd, ctx->buffer)) return -1;
+            if (write_block(ctx, ctx->buffer)) return -1;
             ctx->stored = 0;
         }
 
         // Write complete blocks directly from the user's buffer
         while (ctx->stored == 0 && len - wrote >= ctx->partition->erase_size) {
-            if (write_block(ctx->partition, ctx->fd, data + wrote)) return -1;
+            if (write_block(ctx, data + wrote)) return -1;
             wrote += ctx->partition->erase_size;
         }
     }
@@ -481,7 +503,7 @@
     if (ctx->stored > 0) {
         size_t zero = ctx->partition->erase_size - ctx->stored;
         memset(ctx->buffer + ctx->stored, 0, zero);
-        if (write_block(ctx->partition, ctx->fd, ctx->buffer)) return -1;
+        if (write_block(ctx, ctx->buffer)) return -1;
         ctx->stored = 0;
     }
 
@@ -522,7 +544,23 @@
     // Make sure any pending data gets written
     if (mtd_erase_blocks(ctx, 0) == (off_t) -1) r = -1;
     if (close(ctx->fd)) r = -1;
+    free(ctx->bad_block_offsets);
     free(ctx->buffer);
     free(ctx);
     return r;
 }
+
+/* Return the offset of the first good block at or after pos (which
+ * might be pos itself).
+ */
+off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos) {
+    int i;
+    for (i = 0; i < ctx->bad_block_count; ++i) {
+        if (ctx->bad_block_offsets[i] == pos) {
+            pos += ctx->partition->erase_size;
+        } else if (ctx->bad_block_offsets[i] > pos) {
+            return pos;
+        }
+    }
+    return pos;
+}
diff --git a/mtdutils/mtdutils.h b/mtdutils/mtdutils.h
index 8d2cb56..528a5bb 100644
--- a/mtdutils/mtdutils.h
+++ b/mtdutils/mtdutils.h
@@ -49,6 +49,7 @@
 MtdWriteContext *mtd_write_partition(const MtdPartition *);
 ssize_t mtd_write_data(MtdWriteContext *, const char *data, size_t data_len);
 off_t mtd_erase_blocks(MtdWriteContext *, int blocks);  /* 0 ok, -1 for all */
+off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos);
 int mtd_write_close(MtdWriteContext *);
 
 #endif  // MTDUTILS_H_